github.com/Ali-iotechsys/sqlboiler/v4@v4.0.0-20221208124957-6aec9a5f1f71/queries/query_builders_test.go (about) 1 package queries 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "os" 8 "path/filepath" 9 "reflect" 10 "strings" 11 "testing" 12 13 "github.com/davecgh/go-spew/spew" 14 "github.com/volatiletech/sqlboiler/v4/drivers" 15 ) 16 17 var writeGoldenFiles = flag.Bool( 18 "test.golden", 19 false, 20 "Write golden files.", 21 ) 22 23 func newIntPtr(a int) *int { 24 return &a 25 } 26 27 func TestBuildQuery(t *testing.T) { 28 t.Parallel() 29 30 tests := []struct { 31 q *Query 32 args []interface{} 33 }{ 34 {&Query{from: []string{"t"}}, nil}, 35 {&Query{from: []string{"q"}, limit: newIntPtr(5), offset: 6}, nil}, 36 {&Query{ 37 from: []string{"q"}, 38 orderBy: []argClause{ 39 {"a ASC", []interface{}{}}, 40 {"b like ? DESC", []interface{}{"stuff"}}, 41 }, 42 }, []interface{}{"stuff"}}, 43 {&Query{from: []string{"t"}, selectCols: []string{"count(*) as ab, thing as bd", `"stuff"`}}, nil}, 44 {&Query{from: []string{"a", "b"}, selectCols: []string{"count(*) as ab, thing as bd", `"stuff"`}}, nil}, 45 {&Query{ 46 selectCols: []string{"a.happy", "r.fun", "q"}, 47 from: []string{"happiness as a"}, 48 joins: []join{{clause: "rainbows r on a.id = r.happy_id"}}, 49 }, nil}, 50 {&Query{ 51 from: []string{"happiness as a"}, 52 joins: []join{{clause: "rainbows r on a.id = r.happy_id"}}, 53 }, nil}, 54 {&Query{ 55 from: []string{"videos"}, 56 joins: []join{{ 57 clause: "(select id from users where deleted = ?) u on u.id = videos.user_id", 58 args: []interface{}{true}, 59 }}, 60 where: []where{{clause: "videos.deleted = ?", args: []interface{}{false}}}, 61 }, []interface{}{true, false}}, 62 {&Query{ 63 from: []string{"a"}, 64 groupBy: []string{"id", "name"}, 65 where: []where{ 66 {clause: "a=? or b=?", args: []interface{}{1, 2}}, 67 {clause: "c=?", args: []interface{}{3}}, 68 }, 69 having: []argClause{ 70 {clause: "id <> ?", args: []interface{}{1}}, 71 {clause: "length(name, ?) > ?", args: []interface{}{"utf8", 5}}, 72 }, 73 }, []interface{}{1, 2, 3, 1, "utf8", 5}}, 74 {&Query{ 75 delete: true, 76 from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`}, 77 where: []where{ 78 {clause: "a=?", args: []interface{}{1}}, 79 {clause: "b=?", args: []interface{}{2}}, 80 {clause: "c=?", args: []interface{}{3}}, 81 }, 82 }, []interface{}{1, 2, 3}}, 83 {&Query{ 84 delete: true, 85 from: []string{"thing happy", `upset as "sad"`, "fun", "thing as stuff", `"angry" as mad`}, 86 where: []where{ 87 {clause: "(id=? and thing=?) or stuff=?", args: []interface{}{1, 2, 3}}, 88 }, 89 limit: newIntPtr(5), 90 }, []interface{}{1, 2, 3}}, 91 {&Query{ 92 from: []string{"thing happy", `"fun"`, `stuff`}, 93 update: map[string]interface{}{ 94 "col1": 1, 95 `"col2"`: 2, 96 `"fun".col3`: 3, 97 }, 98 where: []where{ 99 {clause: "aa=? or bb=? or cc=?", orSeparator: true, args: []interface{}{4, 5, 6}}, 100 {clause: "dd=? or ee=? or ff=? and gg=?", args: []interface{}{7, 8, 9, 10}}, 101 }, 102 limit: newIntPtr(5), 103 }, []interface{}{2, 3, 1, 4, 5, 6, 7, 8, 9, 10}}, 104 {&Query{from: []string{"cats"}, joins: []join{{JoinInner, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 105 {&Query{from: []string{"cats c"}, joins: []join{{JoinInner, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 106 {&Query{from: []string{"cats as c"}, joins: []join{{JoinInner, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 107 {&Query{from: []string{"cats as c", "dogs as d"}, joins: []join{{JoinInner, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 108 {&Query{from: []string{"cats"}, joins: []join{{JoinOuterLeft, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 109 {&Query{from: []string{"cats c"}, joins: []join{{JoinOuterLeft, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 110 {&Query{from: []string{"cats as c"}, joins: []join{{JoinOuterLeft, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 111 {&Query{from: []string{"cats as c", "dogs as d"}, joins: []join{{JoinOuterLeft, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 112 {&Query{from: []string{"cats"}, joins: []join{{JoinOuterRight, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 113 {&Query{from: []string{"cats c"}, joins: []join{{JoinOuterRight, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 114 {&Query{from: []string{"cats as c"}, joins: []join{{JoinOuterRight, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 115 {&Query{from: []string{"cats as c", "dogs as d"}, joins: []join{{JoinOuterRight, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 116 {&Query{from: []string{"cats"}, joins: []join{{JoinOuterFull, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 117 {&Query{from: []string{"cats c"}, joins: []join{{JoinOuterFull, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 118 {&Query{from: []string{"cats as c"}, joins: []join{{JoinOuterFull, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 119 {&Query{from: []string{"cats as c", "dogs as d"}, joins: []join{{JoinOuterFull, "dogs d on d.cat_id = cats.id", nil}}}, nil}, 120 {&Query{ 121 from: []string{"t"}, 122 withs: []argClause{ 123 {"cte_0 AS (SELECT * FROM other_t0)", nil}, 124 {"cte_1 AS (SELECT * FROM other_t1 WHERE thing=? AND stuff=?)", []interface{}{3, 7}}, 125 }, 126 }, []interface{}{3, 7}, 127 }, 128 {&Query{from: []string{"t"}, distinct: "id"}, nil}, 129 {&Query{from: []string{"t"}, distinct: "id", count: true}, nil}, 130 {&Query{from: []string{"t"}, distinct: "id, t.*", joins: []join{{JoinInner, "dogs d on d.cat_id = t.id", nil}}}, nil}, 131 {&Query{from: []string{"t"}, distinct: "id, t.*", count: true, joins: []join{{JoinInner, "dogs d on d.cat_id = t.id", nil}}}, nil}, 132 {&Query{from: []string{"t"}, where: []where{{clause: "deleted_at is null"}, {clause: "deleted_at = survives"}}, removeSoftDelete: true}, nil}, 133 } 134 135 for i, test := range tests { 136 filename := filepath.Join("_fixtures", fmt.Sprintf("%02d.sql", i)) 137 test.q.dialect = &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true} 138 out, args := BuildQuery(test.q) 139 140 if *writeGoldenFiles { 141 err := os.WriteFile(filename, []byte(out), 0664) 142 if err != nil { 143 t.Fatalf("Failed to write golden file %s: %s\n", filename, err) 144 } 145 t.Logf("wrote golden file: %s\n", filename) 146 continue 147 } 148 149 byt, err := os.ReadFile(filename) 150 if err != nil { 151 t.Fatalf("Failed to read golden file %q: %v", filename, err) 152 } 153 154 if string(bytes.TrimSpace(byt)) != out { 155 t.Errorf("[%02d] Test failed:\nWant:\n%s\nGot:\n%s", i, byt, out) 156 } 157 158 if !reflect.DeepEqual(args, test.args) { 159 t.Errorf("[%02d] Test failed:\nWant:\n%s\nGot:\n%s", i, spew.Sdump(test.args), spew.Sdump(args)) 160 } 161 } 162 } 163 164 func TestWriteStars(t *testing.T) { 165 t.Parallel() 166 167 tests := []struct { 168 In Query 169 Out []string 170 }{ 171 { 172 In: Query{from: []string{`a`}}, 173 Out: []string{`"a".*`}, 174 }, 175 { 176 In: Query{from: []string{`a as b`}}, 177 Out: []string{`"b".*`}, 178 }, 179 { 180 In: Query{from: []string{`a as b`, `c`}}, 181 Out: []string{`"b".*`, `"c".*`}, 182 }, 183 { 184 In: Query{from: []string{`a as b`, `c as d`}}, 185 Out: []string{`"b".*`, `"d".*`}, 186 }, 187 } 188 189 for i, test := range tests { 190 test.In.dialect = &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true} 191 selects := writeStars(&test.In) 192 if !reflect.DeepEqual(selects, test.Out) { 193 t.Errorf("writeStar test fail %d\nwant: %v\ngot: %v", i, test.Out, selects) 194 } 195 } 196 } 197 198 func TestWhereClause(t *testing.T) { 199 t.Parallel() 200 201 tests := []struct { 202 q Query 203 expect string 204 }{ 205 // Or("a=?") 206 { 207 q: Query{ 208 where: []where{{clause: "a=?", orSeparator: true}}, 209 }, 210 expect: " WHERE (a=$1)", 211 }, 212 // Where("a=?") 213 { 214 q: Query{ 215 where: []where{{clause: "a=?"}}, 216 }, 217 expect: " WHERE (a=$1)", 218 }, 219 // Where("(a=?)") 220 { 221 q: Query{ 222 where: []where{{clause: "(a=?)"}}, 223 }, 224 expect: " WHERE ((a=$1))", 225 }, 226 // Where("((a=? OR b=?))") 227 { 228 q: Query{ 229 where: []where{{clause: "((a=? OR b=?))"}}, 230 }, 231 expect: " WHERE (((a=$1 OR b=$2)))", 232 }, 233 // Where("(a=?)", Or("(b=?)") 234 { 235 q: Query{ 236 where: []where{ 237 {clause: "(a=?)"}, 238 {clause: "(b=?)", orSeparator: true}, 239 }, 240 }, 241 expect: " WHERE ((a=$1)) OR ((b=$2))", 242 }, 243 // Where("a=? OR b=?") 244 { 245 q: Query{ 246 where: []where{{clause: "a=? OR b=?"}}, 247 }, 248 expect: " WHERE (a=$1 OR b=$2)", 249 }, 250 // Where("a=?"), Where("b=?") 251 { 252 q: Query{ 253 where: []where{{clause: "a=?"}, {clause: "b=?"}}, 254 }, 255 expect: " WHERE (a=$1) AND (b=$2)", 256 }, 257 // Where("(a=? AND b=?) OR c=?") 258 { 259 q: Query{ 260 where: []where{{clause: "(a=? AND b=?) OR c=?"}}, 261 }, 262 expect: " WHERE ((a=$1 AND b=$2) OR c=$3)", 263 }, 264 // Where("a=? OR b=?"), Where("c=? OR d=? OR e=?") 265 { 266 q: Query{ 267 where: []where{ 268 {clause: "(a=? OR b=?)"}, 269 {clause: "(c=? OR d=? OR e=?)"}, 270 }, 271 }, 272 expect: " WHERE ((a=$1 OR b=$2)) AND ((c=$3 OR d=$4 OR e=$5))", 273 }, 274 // Where("(a=? AND b=?) OR (c=? AND d=? AND e=?) OR f=? OR f=?") 275 { 276 q: Query{ 277 where: []where{ 278 {clause: "(a=? AND b=?) OR (c=? AND d=? AND e=?) OR f=? OR g=?"}, 279 }, 280 }, 281 expect: " WHERE ((a=$1 AND b=$2) OR (c=$3 AND d=$4 AND e=$5) OR f=$6 OR g=$7)", 282 }, 283 // Where("(a=? AND b=?) OR (c=? AND d=? OR e=?) OR f=? OR g=?") 284 { 285 q: Query{ 286 where: []where{ 287 {clause: "(a=? AND b=?) OR (c=? AND d=? OR e=?) OR f=? OR g=?"}, 288 }, 289 }, 290 expect: " WHERE ((a=$1 AND b=$2) OR (c=$3 AND d=$4 OR e=$5) OR f=$6 OR g=$7)", 291 }, 292 // Where("a=? or b=?"), Or("c=? and d=?"), Or("e=? or f=?") 293 { 294 q: Query{ 295 where: []where{ 296 {clause: "a=? or b=?", orSeparator: true}, 297 {clause: "c=? and d=?", orSeparator: true}, 298 {clause: "e=? or f=?", orSeparator: true}, 299 }, 300 }, 301 expect: " WHERE (a=$1 or b=$2) OR (c=$3 and d=$4) OR (e=$5 or f=$6)", 302 }, 303 // Where("a=? or b=?"), Or("c=? and d=?"), Or("e=? or f=?") 304 { 305 q: Query{ 306 where: []where{ 307 {clause: "a=? or b=?"}, 308 {clause: "c=? and d=?", orSeparator: true}, 309 {clause: "e=? or f=?"}, 310 }, 311 }, 312 expect: " WHERE (a=$1 or b=$2) OR (c=$3 and d=$4) AND (e=$5 or f=$6)", 313 }, 314 // Where("a=?", 1), Or2(Expr(Where("b=? and c=?", 2, 3))) 315 { 316 q: Query{ 317 where: []where{ 318 {clause: "a=?"}, 319 {kind: whereKindLeftParen, orSeparator: true}, 320 {clause: "b=? and c=?", orSeparator: true}, 321 {kind: whereKindRightParen}, 322 }, 323 }, 324 expect: " WHERE a=$1 OR (b=$2 and c=$3)", 325 }, 326 } 327 328 for i, test := range tests { 329 test.q.dialect = &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true} 330 result, _ := whereClause(&test.q, 1) 331 if result != test.expect { 332 t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, result) 333 } 334 } 335 } 336 337 func TestNotInClause(t *testing.T) { 338 t.Parallel() 339 tests := []struct { 340 q Query 341 expect string 342 args []interface{} 343 }{ 344 { 345 q: Query{ 346 where: []where{{kind: whereKindNotIn, clause: "a not in ?", args: []interface{}{}, orSeparator: true}}, 347 }, 348 expect: ` WHERE (1=1)`, 349 }, 350 { 351 q: Query{ 352 where: []where{{kind: whereKindNotIn, clause: "a not in ?", args: []interface{}{1}, orSeparator: true}}, 353 }, 354 expect: ` WHERE ("a" NOT IN ($1))`, 355 args: []interface{}{1}, 356 }, 357 } 358 for i, test := range tests { 359 test.q.dialect = &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true} 360 result, args := whereClause(&test.q, 1) 361 if result != test.expect { 362 t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, result) 363 } 364 if !reflect.DeepEqual(args, test.args) { 365 t.Errorf("%d) Mismatch between expected args:\n%#v\n%#v\n", i, test.args, args) 366 } 367 } 368 } 369 370 func TestInClause(t *testing.T) { 371 t.Parallel() 372 373 tests := []struct { 374 q Query 375 expect string 376 args []interface{} 377 }{ 378 { 379 q: Query{ 380 where: []where{{kind: whereKindIn, clause: "a in ?", args: []interface{}{}, orSeparator: true}}, 381 }, 382 expect: ` WHERE (1=0)`, 383 }, 384 { 385 q: Query{ 386 where: []where{ 387 where{kind: whereKindIn, clause: "a in ?", args: []interface{}{}, orSeparator: true}, 388 where{kind: whereKindIn, clause: "a in ?", args: []interface{}{1}, orSeparator: true}, 389 }, 390 }, 391 expect: ` WHERE (1=0) OR ("a" IN ($1))`, 392 args: []interface{}{1}, 393 }, 394 395 { 396 q: Query{ 397 where: []where{{kind: whereKindIn, clause: "a in ?", args: []interface{}{1}, orSeparator: true}}, 398 }, 399 expect: ` WHERE ("a" IN ($1))`, 400 args: []interface{}{1}, 401 }, 402 { 403 q: Query{ 404 where: []where{{kind: whereKindIn, clause: "a in ?", args: []interface{}{1, 2, 3}}}, 405 }, 406 expect: ` WHERE ("a" IN ($1,$2,$3))`, 407 args: []interface{}{1, 2, 3}, 408 }, 409 { 410 q: Query{ 411 where: []where{{kind: whereKindIn, clause: "? in ?", args: []interface{}{1, 2, 3}}}, 412 }, 413 expect: " WHERE ($1 IN ($2,$3))", 414 args: []interface{}{1, 2, 3}, 415 }, 416 { 417 q: Query{ 418 where: []where{{kind: whereKindIn, clause: "( ? , ? ) in ( ? )", orSeparator: true, args: []interface{}{"a", "b", 1, 2, 3, 4}}}, 419 }, 420 expect: " WHERE (( $1 , $2 ) IN ( (($3,$4),($5,$6)) ))", 421 args: []interface{}{"a", "b", 1, 2, 3, 4}, 422 }, 423 { 424 q: Query{ 425 where: []where{{kind: whereKindIn, clause: `("a")in(?)`, orSeparator: true, args: []interface{}{1, 2, 3}}}, 426 }, 427 expect: ` WHERE (("a") IN (($1,$2,$3)))`, 428 args: []interface{}{1, 2, 3}, 429 }, 430 { 431 q: Query{ 432 where: []where{{kind: whereKindIn, clause: `("a")in?`, args: []interface{}{1}}}, 433 }, 434 expect: ` WHERE (("a") IN ($1))`, 435 args: []interface{}{1}, 436 }, 437 { 438 q: Query{ 439 where: []where{ 440 {clause: "a=?", args: []interface{}{1}}, 441 {kind: whereKindIn, clause: `?,?,"name" in ?`, orSeparator: true, args: []interface{}{"c", "d", 3, 4, 5, 6, 7, 8}}, 442 {kind: whereKindIn, clause: `?,?,"name" in ?`, orSeparator: true, args: []interface{}{"e", "f", 9, 10, 11, 12, 13, 14}}, 443 }, 444 }, 445 expect: ` WHERE (a=$1) OR ($2,$3,"name" IN (($4,$5,$6),($7,$8,$9))) OR ($10,$11,"name" IN (($12,$13,$14),($15,$16,$17)))`, 446 args: []interface{}{1, "c", "d", 3, 4, 5, 6, 7, 8, "e", "f", 9, 10, 11, 12, 13, 14}, 447 }, 448 { 449 q: Query{ 450 where: []where{ 451 {kind: whereKindIn, clause: `("a")in`, args: []interface{}{1}}, 452 {kind: whereKindIn, clause: `("a")in?`, orSeparator: true, args: []interface{}{1}}, 453 }, 454 }, 455 expect: ` WHERE (("a")in) OR (("a") IN ($1))`, 456 args: []interface{}{1, 1}, 457 }, 458 { 459 q: Query{ 460 where: []where{ 461 {kind: whereKindIn, clause: `\?,\? in \?`, args: []interface{}{1}}, 462 {kind: whereKindIn, clause: `\?,\?in \?`, orSeparator: true, args: []interface{}{1}}, 463 }, 464 }, 465 expect: ` WHERE (?,? IN ?) OR (?,? IN ?)`, 466 args: []interface{}{1, 1}, 467 }, 468 { 469 q: Query{ 470 where: []where{ 471 {kind: whereKindIn, clause: `("a")in`, args: []interface{}{1}}, 472 {kind: whereKindIn, clause: `("a") in thing`, args: []interface{}{1, 2, 3}}, 473 {kind: whereKindIn, clause: `("a")in?`, orSeparator: true, args: []interface{}{4, 5, 6}}, 474 }, 475 }, 476 expect: ` WHERE (("a")in) AND (("a") IN thing) OR (("a") IN ($1,$2,$3))`, 477 args: []interface{}{1, 1, 2, 3, 4, 5, 6}, 478 }, 479 { 480 q: Query{ 481 where: []where{ 482 {kind: whereKindIn, clause: `("a")in?`, orSeparator: true, args: []interface{}{4, 5, 6}}, 483 {kind: whereKindIn, clause: `("a") in thing`, args: []interface{}{1, 2, 3}}, 484 {kind: whereKindIn, clause: `("a")in`, args: []interface{}{1}}, 485 }, 486 }, 487 expect: ` WHERE (("a") IN ($1,$2,$3)) AND (("a") IN thing) AND (("a")in)`, 488 args: []interface{}{4, 5, 6, 1, 2, 3, 1}, 489 }, 490 { 491 q: Query{ 492 where: []where{ 493 {kind: whereKindIn, clause: `("a")in?`, orSeparator: true, args: []interface{}{4, 5, 6}}, 494 {kind: whereKindIn, clause: `("a")in`, args: []interface{}{1}}, 495 {kind: whereKindIn, clause: `("a") in thing`, args: []interface{}{1, 2, 3}}, 496 }, 497 }, 498 expect: ` WHERE (("a") IN ($1,$2,$3)) AND (("a")in) AND (("a") IN thing)`, 499 args: []interface{}{4, 5, 6, 1, 1, 2, 3}, 500 }, 501 } 502 503 for i, test := range tests { 504 test.q.dialect = &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true} 505 result, args := whereClause(&test.q, 1) 506 if result != test.expect { 507 t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, result) 508 } 509 if !reflect.DeepEqual(args, test.args) { 510 t.Errorf("%d) Mismatch between expected args:\n%#v\n%#v\n", i, test.args, args) 511 } 512 } 513 } 514 515 func TestLimitClause(t *testing.T) { 516 t.Parallel() 517 518 tests := []struct { 519 limit *int 520 expectPredicate func(sql string) bool 521 }{ 522 {nil, func(sql string) bool { 523 return !strings.Contains(sql, "LIMIT") 524 }}, 525 {newIntPtr(0), func(sql string) bool { 526 return strings.Contains(sql, "LIMIT 0") 527 }}, 528 {newIntPtr(5), func(sql string) bool { 529 return strings.Contains(sql, "LIMIT 5") 530 }}, 531 } 532 533 for i, test := range tests { 534 q := &Query{ 535 limit: test.limit, 536 dialect: &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true, UseTopClause: false}, 537 } 538 sql, _ := BuildQuery(q) 539 if !test.expectPredicate(sql) { 540 t.Errorf("%d) Unexpected built SQL query: %s", i, sql) 541 } 542 } 543 } 544 545 func TestConvertQuestionMarks(t *testing.T) { 546 t.Parallel() 547 548 tests := []struct { 549 clause string 550 start int 551 expect string 552 count int 553 }{ 554 {clause: "hello friend", start: 1, expect: "hello friend", count: 0}, 555 {clause: "thing=?", start: 2, expect: "thing=$2", count: 1}, 556 {clause: "thing=? and stuff=? and happy=?", start: 2, expect: "thing=$2 and stuff=$3 and happy=$4", count: 3}, 557 {clause: `thing \? stuff`, start: 2, expect: `thing ? stuff`, count: 0}, 558 {clause: `thing \? stuff and happy \? fun`, start: 2, expect: `thing ? stuff and happy ? fun`, count: 0}, 559 { 560 clause: `thing \? stuff ? happy \? and mad ? fun \? \? \?`, 561 start: 2, 562 expect: `thing ? stuff $2 happy ? and mad $3 fun ? ? ?`, 563 count: 2, 564 }, 565 { 566 clause: `thing ? stuff ? happy \? fun \? ? ?`, 567 start: 1, 568 expect: `thing $1 stuff $2 happy ? fun ? $3 $4`, 569 count: 4, 570 }, 571 {clause: `?`, start: 1, expect: `$1`, count: 1}, 572 {clause: `???`, start: 1, expect: `$1$2$3`, count: 3}, 573 {clause: `\?`, start: 1, expect: `?`}, 574 {clause: `\?\?\?`, start: 1, expect: `???`}, 575 {clause: `\??\??\??`, start: 1, expect: `?$1?$2?$3`, count: 3}, 576 {clause: `?\??\??\?`, start: 1, expect: `$1?$2?$3?`, count: 3}, 577 } 578 579 for i, test := range tests { 580 res, count := convertQuestionMarks(test.clause, test.start) 581 if res != test.expect { 582 t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, res) 583 } 584 if count != test.count { 585 t.Errorf("%d) Expected count %d, got %d", i, test.count, count) 586 } 587 } 588 } 589 590 func TestConvertInQuestionMarks(t *testing.T) { 591 t.Parallel() 592 593 tests := []struct { 594 clause string 595 start int 596 group int 597 total int 598 expect string 599 }{ 600 {clause: "?", expect: "(($1,$2,$3),($4,$5,$6),($7,$8,$9))", start: 1, total: 9, group: 3}, 601 {clause: "?", expect: "(($2,$3),($4))", start: 2, total: 3, group: 2}, 602 {clause: "hello friend", start: 1, expect: "hello friend", total: 0, group: 1}, 603 {clause: "thing ? thing", start: 2, expect: "thing ($2,$3) thing", total: 2, group: 1}, 604 {clause: "thing?thing", start: 2, expect: "thing($2)thing", total: 1, group: 1}, 605 {clause: `thing \? stuff`, start: 2, expect: `thing ? stuff`, total: 0, group: 1}, 606 {clause: `thing \? stuff and happy \? fun`, start: 2, expect: `thing ? stuff and happy ? fun`, total: 0, group: 1}, 607 {clause: "thing ? thing ? thing", start: 1, expect: "thing ($1,$2,$3) thing ? thing", total: 3, group: 1}, 608 {clause: `?`, start: 1, expect: `($1)`, total: 1, group: 1}, 609 {clause: `???`, start: 1, expect: `($1,$2,$3)??`, total: 3, group: 1}, 610 {clause: `\?`, start: 1, expect: `?`, total: 0, group: 1}, 611 {clause: `\?\?\?`, start: 1, expect: `???`, total: 0, group: 1}, 612 {clause: `\??\??\??`, start: 1, expect: `?($1,$2,$3)????`, total: 3, group: 1}, 613 {clause: `?\??\??\?`, start: 1, expect: `($1,$2,$3)?????`, total: 3, group: 1}, 614 } 615 616 for i, test := range tests { 617 res, count := convertInQuestionMarks(true, test.clause, test.start, test.group, test.total) 618 if res != test.expect { 619 t.Errorf("%d) Mismatch between expect and result:\n%s\n%s\n", i, test.expect, res) 620 } 621 if count != test.total { 622 t.Errorf("%d) Expected %d, got %d", i, test.total, count) 623 } 624 } 625 626 res, count := convertInQuestionMarks(false, "?", 1, 3, 9) 627 if res != "((?,?,?),(?,?,?),(?,?,?))" { 628 t.Errorf("Mismatch between expected and result: %s", res) 629 } 630 if count != 9 { 631 t.Errorf("Expected 9 results, got %d", count) 632 } 633 } 634 635 func TestWriteAsStatements(t *testing.T) { 636 t.Parallel() 637 638 query := Query{ 639 selectCols: []string{ 640 `a`, 641 `a.fun`, 642 `"b"."fun"`, 643 `"b".fun`, 644 `b."fun"`, 645 `a.clown.run`, 646 `COUNT(a)`, 647 }, 648 dialect: &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true}, 649 } 650 651 expect := []string{ 652 `"a"`, 653 `"a"."fun" as "a.fun"`, 654 `"b"."fun" as "b.fun"`, 655 `"b"."fun" as "b.fun"`, 656 `"b"."fun" as "b.fun"`, 657 `"a"."clown"."run" as "a.clown.run"`, 658 `COUNT(a)`, 659 } 660 661 gots := writeAsStatements(&query) 662 663 for i, got := range gots { 664 if expect[i] != got { 665 t.Errorf(`%d) want: %s, got: %s`, i, expect[i], got) 666 } 667 } 668 } 669 670 func TestWriteComment(t *testing.T) { 671 t.Parallel() 672 673 var buf bytes.Buffer 674 query := Query{ 675 dialect: &drivers.Dialect{LQ: '"', RQ: '"', UseIndexPlaceholders: true}, 676 } 677 678 // empty comment 679 buf.Reset() 680 query.comment = "" 681 writeComment(&query, &buf) 682 if got := buf.String(); got != "" { 683 t.Errorf(`bad empty comment, got: %s`, got) 684 } 685 686 // one line comment 687 buf.Reset() 688 query.comment = "comment" 689 writeComment(&query, &buf) 690 if got := buf.String(); got != "-- comment\n" { 691 t.Errorf(`bad one line comment, got: %s`, got) 692 } 693 694 // two lines comment 695 buf.Reset() 696 query.comment = "first\nsecond" 697 writeComment(&query, &buf) 698 if got := buf.String(); got != "-- first\n-- second\n" { 699 t.Errorf(`bad two lines comment, got: %s`, got) 700 } 701 }