github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/expr/parser_test.go (about) 1 // Modifications Copyright (c) 2017-2018 Uber Technologies, Inc. 2 // Copyright (c) 2013-2016 Errplane Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy of 5 // this software and associated documentation files (the "Software"), to deal in 6 // the Software without restriction, including without limitation the rights to 7 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 // the Software, and to permit persons to whom the Software is furnished to do so, 9 // subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in all 12 // copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21 package expr_test 22 23 import ( 24 "reflect" 25 "strings" 26 "testing" 27 28 "github.com/uber/aresdb/query/expr" 29 ) 30 31 // Ensure the parser can parse expressions into an AST. 32 func TestParser_ParseExpr(t *testing.T) { 33 var tests = []struct { 34 s string 35 expr expr.Expr 36 err string 37 }{ 38 // Primitives 39 {s: `100`, expr: &expr.NumberLiteral{Val: 100, Int: 100, Expr: "100", ExprType: expr.Unsigned}}, 40 {s: `'foo bar'`, expr: &expr.StringLiteral{Val: "foo bar"}}, 41 {s: `true`, expr: &expr.BooleanLiteral{Val: true}}, 42 {s: `false`, expr: &expr.BooleanLiteral{Val: false}}, 43 {s: `my_ident`, expr: &expr.VarRef{Val: "my_ident"}}, 44 {s: `*`, expr: &expr.Wildcard{}}, 45 46 // Simple binary expression 47 { 48 s: `1 + 2`, 49 expr: &expr.BinaryExpr{ 50 Op: expr.ADD, 51 LHS: &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 52 RHS: &expr.NumberLiteral{Val: 2, Int: 2, Expr: "2", ExprType: expr.Unsigned}, 53 }, 54 }, 55 56 // Binary expression with LHS precedence 57 { 58 s: `1 * 2 + 3`, 59 expr: &expr.BinaryExpr{ 60 Op: expr.ADD, 61 LHS: &expr.BinaryExpr{ 62 Op: expr.MUL, 63 LHS: &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 64 RHS: &expr.NumberLiteral{Val: 2, Int: 2, Expr: "2", ExprType: expr.Unsigned}, 65 }, 66 RHS: &expr.NumberLiteral{Val: 3, Int: 3, Expr: "3", ExprType: expr.Unsigned}, 67 }, 68 }, 69 70 // Binary expression with RHS precedence 71 { 72 s: `1 + 2 * 3`, 73 expr: &expr.BinaryExpr{ 74 Op: expr.ADD, 75 LHS: &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 76 RHS: &expr.BinaryExpr{ 77 Op: expr.MUL, 78 LHS: &expr.NumberLiteral{Val: 2, Int: 2, Expr: "2", ExprType: expr.Unsigned}, 79 RHS: &expr.NumberLiteral{Val: 3, Int: 3, Expr: "3", ExprType: expr.Unsigned}, 80 }, 81 }, 82 }, 83 84 // Binary expression with LHS paren group. 85 { 86 s: `(1 + 2) * 3`, 87 expr: &expr.BinaryExpr{ 88 Op: expr.MUL, 89 LHS: &expr.ParenExpr{ 90 Expr: &expr.BinaryExpr{ 91 Op: expr.ADD, 92 LHS: &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 93 RHS: &expr.NumberLiteral{Val: 2, Int: 2, Expr: "2", ExprType: expr.Unsigned}, 94 }, 95 }, 96 RHS: &expr.NumberLiteral{Val: 3, Int: 3, Expr: "3", ExprType: expr.Unsigned}, 97 }, 98 }, 99 100 // Binary expression with no precedence, tests left associativity. 101 { 102 s: `1 * 2 * 3`, 103 expr: &expr.BinaryExpr{ 104 Op: expr.MUL, 105 LHS: &expr.BinaryExpr{ 106 Op: expr.MUL, 107 LHS: &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 108 RHS: &expr.NumberLiteral{Val: 2, Int: 2, Expr: "2", ExprType: expr.Unsigned}, 109 }, 110 RHS: &expr.NumberLiteral{Val: 3, Int: 3, Expr: "3", ExprType: expr.Unsigned}, 111 }, 112 }, 113 114 // Binary expression with IN. 115 { 116 s: "id IN (12, 18)", 117 expr: &expr.BinaryExpr{ 118 Op: expr.IN, 119 LHS: &expr.VarRef{Val: "id"}, 120 RHS: &expr.Call{Name: "", Args: []expr.Expr{ 121 &expr.NumberLiteral{Val: 12, Int: 12, Expr: "12", ExprType: expr.Unsigned}, 122 &expr.NumberLiteral{Val: 18, Int: 18, Expr: "18", ExprType: expr.Unsigned}, 123 }}, 124 }, 125 }, 126 { 127 s: "id IN (12)", 128 expr: &expr.BinaryExpr{ 129 Op: expr.IN, 130 LHS: &expr.VarRef{Val: "id"}, 131 RHS: &expr.Call{Name: "", Args: []expr.Expr{ 132 &expr.NumberLiteral{Val: 12, Int: 12, Expr: "12", ExprType: expr.Unsigned}, 133 }}, 134 }, 135 }, 136 { 137 s: "id IN (12, 15)", 138 expr: &expr.BinaryExpr{ 139 Op: expr.IN, 140 LHS: &expr.VarRef{Val: "id"}, 141 RHS: &expr.Call{Name: "", Args: []expr.Expr{ 142 &expr.NumberLiteral{Val: 12, Int: 12, Expr: "12", ExprType: expr.Unsigned}, 143 &expr.NumberLiteral{Val: 15, Int: 15, Expr: "15", ExprType: expr.Unsigned}, 144 }}, 145 }, 146 }, 147 // Binary expression with NOT IN. 148 { 149 s: "id NOT IN (12, 18)", 150 expr: &expr.BinaryExpr{ 151 Op: expr.NOT_IN, 152 LHS: &expr.VarRef{Val: "id"}, 153 RHS: &expr.Call{Name: "", Args: []expr.Expr{ 154 &expr.NumberLiteral{Val: 12, Int: 12, Expr: "12", ExprType: expr.Unsigned}, 155 &expr.NumberLiteral{Val: 18, Int: 18, Expr: "18", ExprType: expr.Unsigned}, 156 }}, 157 }, 158 }, 159 { 160 s: "id NOT IN (12, 15)", 161 expr: &expr.BinaryExpr{ 162 Op: expr.NOT_IN, 163 LHS: &expr.VarRef{Val: "id"}, 164 RHS: &expr.Call{Name: "", Args: []expr.Expr{ 165 &expr.NumberLiteral{Val: 12, Int: 12, Expr: "12", ExprType: expr.Unsigned}, 166 &expr.NumberLiteral{Val: 15, Int: 15, Expr: "15", ExprType: expr.Unsigned}, 167 }}, 168 }, 169 }, 170 // Unary expression. 171 { 172 s: "not now", 173 expr: &expr.UnaryExpr{ 174 Op: expr.NOT, 175 Expr: &expr.VarRef{Val: "now"}, 176 }, 177 }, 178 { 179 s: "!today", 180 expr: &expr.UnaryExpr{ 181 Op: expr.EXCLAMATION, 182 Expr: &expr.VarRef{Val: "today"}, 183 }, 184 }, 185 { 186 s: "-c", 187 expr: &expr.UnaryExpr{ 188 Op: expr.UNARY_MINUS, 189 Expr: &expr.VarRef{Val: "c"}, 190 }, 191 }, 192 { 193 s: "not ! a + b and c", 194 expr: &expr.BinaryExpr{ 195 Op: expr.AND, 196 LHS: &expr.UnaryExpr{ 197 Op: expr.NOT, 198 Expr: &expr.BinaryExpr{ 199 Op: expr.ADD, 200 LHS: &expr.UnaryExpr{ 201 Op: expr.EXCLAMATION, 202 Expr: &expr.VarRef{Val: "a"}, 203 }, 204 RHS: &expr.VarRef{Val: "b"}, 205 }, 206 }, 207 RHS: &expr.VarRef{Val: "c"}, 208 }, 209 }, 210 // Derived unary expression. 211 { 212 s: "a is null", 213 expr: &expr.UnaryExpr{ 214 Op: expr.IS_NULL, 215 Expr: &expr.VarRef{Val: "a"}, 216 }, 217 }, 218 { 219 s: "a is not null", 220 expr: &expr.UnaryExpr{ 221 Op: expr.IS_NOT_NULL, 222 Expr: &expr.VarRef{Val: "a"}, 223 }, 224 }, 225 { 226 s: "a is unknown", 227 expr: &expr.UnaryExpr{ 228 Op: expr.IS_NULL, 229 Expr: &expr.VarRef{Val: "a"}, 230 }, 231 }, 232 { 233 s: "a is true", 234 expr: &expr.UnaryExpr{ 235 Op: expr.IS_TRUE, 236 Expr: &expr.VarRef{Val: "a"}, 237 }, 238 }, 239 { 240 s: "a is not true", 241 expr: &expr.UnaryExpr{ 242 Op: expr.IS_FALSE, 243 Expr: &expr.VarRef{Val: "a"}, 244 }, 245 }, 246 { 247 s: "not ! a is not true and c", 248 expr: &expr.BinaryExpr{ 249 Op: expr.AND, 250 LHS: &expr.UnaryExpr{ 251 Op: expr.NOT, 252 Expr: &expr.UnaryExpr{ 253 Op: expr.IS_FALSE, 254 Expr: &expr.UnaryExpr{ 255 Op: expr.EXCLAMATION, 256 Expr: &expr.VarRef{Val: "a"}, 257 }, 258 }, 259 }, 260 RHS: &expr.VarRef{Val: "c"}, 261 }, 262 }, 263 264 // Complex binary expression. 265 { 266 s: `value + 3 < 30 AND 1 + 2 OR true`, 267 expr: &expr.BinaryExpr{ 268 Op: expr.OR, 269 LHS: &expr.BinaryExpr{ 270 Op: expr.AND, 271 LHS: &expr.BinaryExpr{ 272 Op: expr.LT, 273 LHS: &expr.BinaryExpr{ 274 Op: expr.ADD, 275 LHS: &expr.VarRef{Val: "value"}, 276 RHS: &expr.NumberLiteral{Val: 3, Int: 3, Expr: "3", ExprType: expr.Unsigned}, 277 }, 278 RHS: &expr.NumberLiteral{Val: 30, Int: 30, Expr: "30", ExprType: expr.Unsigned}, 279 }, 280 RHS: &expr.BinaryExpr{ 281 Op: expr.ADD, 282 LHS: &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 283 RHS: &expr.NumberLiteral{Val: 2, Int: 2, Expr: "2", ExprType: expr.Unsigned}, 284 }, 285 }, 286 RHS: &expr.BooleanLiteral{Val: true}, 287 }, 288 }, 289 290 // Case 291 { 292 s: "case when a then b end", 293 expr: &expr.Case{ 294 WhenThens: []expr.WhenThen{ 295 { 296 When: &expr.VarRef{Val: "a"}, 297 Then: &expr.VarRef{Val: "b"}, 298 }, 299 }, 300 }, 301 }, 302 { 303 s: "case when a then b else c end", 304 expr: &expr.Case{ 305 WhenThens: []expr.WhenThen{ 306 { 307 When: &expr.VarRef{Val: "a"}, 308 Then: &expr.VarRef{Val: "b"}, 309 }, 310 }, 311 Else: &expr.VarRef{Val: "c"}, 312 }, 313 }, 314 { 315 s: "case when a then b when a2 then b2 else c end", 316 expr: &expr.Case{ 317 WhenThens: []expr.WhenThen{ 318 { 319 When: &expr.VarRef{Val: "a"}, 320 Then: &expr.VarRef{Val: "b"}, 321 }, 322 { 323 When: &expr.VarRef{Val: "a2"}, 324 Then: &expr.VarRef{Val: "b2"}, 325 }, 326 }, 327 Else: &expr.VarRef{Val: "c"}, 328 }, 329 }, 330 { 331 s: "case end", 332 err: "found END, expected WHEN at line 1, char 6", 333 }, 334 { 335 s: "case else b end", 336 err: "found ELSE, expected WHEN at line 1, char 6", 337 }, 338 { 339 s: "case when a then b", 340 err: "found EOF, expected END at line 1, char 20", 341 }, 342 343 // Function call (empty) 344 { 345 s: `my_func()`, 346 expr: &expr.Call{ 347 Name: "my_func", 348 }, 349 }, 350 351 // Function call (multi-arg) 352 { 353 s: `my_func(1, -2 + 3)`, 354 expr: &expr.Call{ 355 Name: "my_func", 356 Args: []expr.Expr{ 357 &expr.NumberLiteral{Val: 1, Int: 1, Expr: "1", ExprType: expr.Unsigned}, 358 &expr.BinaryExpr{ 359 Op: expr.ADD, 360 LHS: &expr.NumberLiteral{Val: -2, Int: -2, Expr: "-2", ExprType: expr.Signed}, 361 RHS: &expr.NumberLiteral{Val: 3, Int: 3, Expr: "3", ExprType: expr.Unsigned}, 362 }, 363 }, 364 }, 365 }, 366 } 367 368 for i, tt := range tests { 369 expr, err := expr.NewParser(strings.NewReader(tt.s)).ParseExpr(0) 370 if !reflect.DeepEqual(tt.err, errstring(err)) { 371 t.Errorf("%d. %q: error mismatch:\n exp=%s\n got=%s\n\n", i, tt.s, tt.err, err) 372 } else if tt.err == "" && !reflect.DeepEqual(tt.expr, expr) { 373 t.Errorf("%d. %q\n\nexpr mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.s, tt.expr, expr) 374 } 375 } 376 } 377 378 // Ensure a string can be quoted. 379 func TestQuote(t *testing.T) { 380 for i, tt := range []struct { 381 in string 382 out string 383 }{ 384 {``, `''`}, 385 {`foo`, `'foo'`}, 386 {"foo\nbar", `'foo\nbar'`}, 387 {`foo bar\\`, `'foo bar\\\\'`}, 388 {`'foo'`, `'\'foo\''`}, 389 } { 390 if out := expr.QuoteString(tt.in); tt.out != out { 391 t.Errorf("%d. %s: mismatch: %s != %s", i, tt.in, tt.out, out) 392 } 393 } 394 } 395 396 // Ensure an identifier's segments can be quoted. 397 func TestQuoteIdent(t *testing.T) { 398 for i, tt := range []struct { 399 ident []string 400 s string 401 }{ 402 {[]string{``}, ``}, 403 {[]string{`select`}, `"select"`}, 404 {[]string{`in-bytes`}, `"in-bytes"`}, 405 {[]string{`foo`, `bar`}, `"foo".bar`}, 406 {[]string{`foo`, ``, `bar`}, `"foo"..bar`}, 407 {[]string{`foo bar`, `baz`}, `"foo bar".baz`}, 408 {[]string{`foo.bar`, `baz`}, `"foo.bar".baz`}, 409 {[]string{`foo.bar`, `rp`, `baz`}, `"foo.bar"."rp".baz`}, 410 {[]string{`foo.bar`, `rp`, `1baz`}, `"foo.bar"."rp"."1baz"`}, 411 } { 412 if s := expr.QuoteIdent(tt.ident...); tt.s != s { 413 t.Errorf("%d. %s: mismatch: %s != %s", i, tt.ident, tt.s, s) 414 } 415 } 416 } 417 418 // errstring converts an error to its string representation. 419 func errstring(err error) string { 420 if err != nil { 421 return err.Error() 422 } 423 return "" 424 }