gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/utils.go (about) 1 package rethinkdb 2 3 import ( 4 "reflect" 5 "strconv" 6 "strings" 7 "sync/atomic" 8 9 "gopkg.in/rethinkdb/rethinkdb-go.v6/encoding" 10 p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2" 11 ) 12 13 // Helper functions for constructing terms 14 15 // constructRootTerm is an alias for creating a new term. 16 func constructRootTerm(name string, termType p.Term_TermType, args []interface{}, optArgs map[string]interface{}) Term { 17 return Term{ 18 name: name, 19 rootTerm: true, 20 termType: termType, 21 args: convertTermList(args), 22 optArgs: convertTermObj(optArgs), 23 } 24 } 25 26 // constructMethodTerm is an alias for creating a new term. Unlike constructRootTerm 27 // this function adds the previous expression in the tree to the argument list to 28 // create a method term. 29 func constructMethodTerm(prevVal Term, name string, termType p.Term_TermType, args []interface{}, optArgs map[string]interface{}) Term { 30 args = append([]interface{}{prevVal}, args...) 31 32 return Term{ 33 name: name, 34 rootTerm: false, 35 termType: termType, 36 args: convertTermList(args), 37 optArgs: convertTermObj(optArgs), 38 } 39 } 40 41 // Helper functions for creating internal RQL types 42 43 func newQuery(t Term, qopts map[string]interface{}, copts *ConnectOpts) (q Query, err error) { 44 queryOpts := map[string]interface{}{} 45 for k, v := range qopts { 46 queryOpts[k], err = Expr(v).Build() 47 if err != nil { 48 return 49 } 50 } 51 if copts.Database != "" { 52 queryOpts["db"], err = DB(copts.Database).Build() 53 if err != nil { 54 return 55 } 56 } 57 58 builtTerm, err := t.Build() 59 if err != nil { 60 return q, err 61 } 62 63 // Construct query 64 return Query{ 65 Type: p.Query_START, 66 Term: &t, 67 Opts: queryOpts, 68 builtTerm: builtTerm, 69 }, nil 70 } 71 72 // makeArray takes a slice of terms and produces a single MAKE_ARRAY term 73 func makeArray(args termsList) Term { 74 return Term{ 75 name: "[...]", 76 termType: p.Term_MAKE_ARRAY, 77 args: args, 78 } 79 } 80 81 // makeObject takes a map of terms and produces a single MAKE_OBJECT term 82 func makeObject(args termsObj) Term { 83 return Term{ 84 name: "{...}", 85 termType: p.Term_MAKE_OBJ, 86 optArgs: args, 87 } 88 } 89 90 var nextVarID int64 91 92 func makeFunc(f interface{}) Term { 93 value := reflect.ValueOf(f) 94 valueType := value.Type() 95 96 var argNums = make([]interface{}, valueType.NumIn()) 97 var args = make([]reflect.Value, valueType.NumIn()) 98 for i := 0; i < valueType.NumIn(); i++ { 99 // Get a slice of the VARs to use as the function arguments 100 varID := atomic.AddInt64(&nextVarID, 1) 101 args[i] = reflect.ValueOf(constructRootTerm("var", p.Term_VAR, []interface{}{varID}, map[string]interface{}{})) 102 argNums[i] = varID 103 104 // make sure all input arguments are of type Term 105 argValueTypeName := valueType.In(i).String() 106 if argValueTypeName != "rethinkdb.Term" && argValueTypeName != "interface {}" { 107 panic("Function argument is not of type Term or interface {}") 108 } 109 } 110 111 if valueType.NumOut() != 1 { 112 panic("Function does not have a single return value") 113 } 114 115 body := value.Call(args)[0].Interface() 116 argsArr := makeArray(convertTermList(argNums)) 117 118 return constructRootTerm("func", p.Term_FUNC, []interface{}{argsArr, body}, map[string]interface{}{}) 119 } 120 121 func funcWrap(value interface{}) Term { 122 val := Expr(value) 123 124 if implVarScan(val) && val.termType != p.Term_ARGS { 125 return makeFunc(func(x Term) Term { 126 return val 127 }) 128 } 129 return val 130 } 131 132 func funcWrapArgs(args []interface{}) []interface{} { 133 for i, arg := range args { 134 args[i] = funcWrap(arg) 135 } 136 137 return args 138 } 139 140 // implVarScan recursivly checks a value to see if it contains an 141 // IMPLICIT_VAR term. If it does it returns true 142 func implVarScan(value Term) bool { 143 if value.termType == p.Term_IMPLICIT_VAR { 144 return true 145 } 146 for _, v := range value.args { 147 if implVarScan(v) { 148 return true 149 } 150 } 151 152 for _, v := range value.optArgs { 153 if implVarScan(v) { 154 return true 155 } 156 } 157 158 return false 159 } 160 161 // Convert an opt args struct to a map. 162 func optArgsToMap(optArgs OptArgs) map[string]interface{} { 163 data, err := encode(optArgs) 164 165 if err == nil && data != nil { 166 if m, ok := data.(map[string]interface{}); ok { 167 return m 168 } 169 } 170 171 return map[string]interface{}{} 172 } 173 174 // Convert a list into a slice of terms 175 func convertTermList(l []interface{}) termsList { 176 if len(l) == 0 { 177 return nil 178 } 179 180 terms := make(termsList, len(l)) 181 for i, v := range l { 182 terms[i] = Expr(v) 183 } 184 185 return terms 186 } 187 188 // Convert a map into a map of terms 189 func convertTermObj(o map[string]interface{}) termsObj { 190 if len(o) == 0 { 191 return nil 192 } 193 194 terms := make(termsObj, len(o)) 195 for k, v := range o { 196 terms[k] = Expr(v) 197 } 198 199 return terms 200 } 201 202 // Helper functions for debugging 203 204 func allArgsToStringSlice(args termsList, optArgs termsObj) []string { 205 allArgs := make([]string, len(args)+len(optArgs)) 206 i := 0 207 208 for _, v := range args { 209 allArgs[i] = v.String() 210 i++ 211 } 212 for k, v := range optArgs { 213 allArgs[i] = k + "=" + v.String() 214 i++ 215 } 216 217 return allArgs 218 } 219 220 func argsToStringSlice(args termsList) []string { 221 allArgs := make([]string, len(args)) 222 223 for i, v := range args { 224 allArgs[i] = v.String() 225 } 226 227 return allArgs 228 } 229 230 func optArgsToStringSlice(optArgs termsObj) []string { 231 allArgs := make([]string, len(optArgs)) 232 i := 0 233 234 for k, v := range optArgs { 235 allArgs[i] = k + "=" + v.String() 236 i++ 237 } 238 239 return allArgs 240 } 241 242 func splitAddress(address string) (hostname string, port int) { 243 hostname = "localhost" 244 port = 28015 245 246 addrParts := strings.Split(address, ":") 247 248 if len(addrParts) >= 1 { 249 hostname = addrParts[0] 250 } 251 if len(addrParts) >= 2 { 252 port, _ = strconv.Atoi(addrParts[1]) 253 } 254 255 return 256 } 257 258 func encode(data interface{}) (interface{}, error) { 259 if _, ok := data.(Term); ok { 260 return data, nil 261 } 262 263 v, err := encoding.Encode(data) 264 if err != nil { 265 return nil, err 266 } 267 268 return v, nil 269 } 270 271 // shouldRetryQuery checks the result of a query and returns true if the query 272 // should be retried 273 func shouldRetryQuery(q Query, err error) bool { 274 if err == nil { 275 return false 276 } 277 278 if _, ok := err.(RQLConnectionError); ok { 279 return true 280 } 281 282 return err == ErrConnectionClosed 283 }