github.com/acoshift/pgsql@v0.15.3/pgstmt/select_test.go (about) 1 package pgstmt_test 2 3 import ( 4 "testing" 5 6 "github.com/lib/pq" 7 "github.com/stretchr/testify/assert" 8 9 "github.com/acoshift/pgsql/pgstmt" 10 ) 11 12 func TestSelect(t *testing.T) { 13 t.Parallel() 14 15 cases := []struct { 16 name string 17 result *pgstmt.Result 18 query string 19 args []any 20 }{ 21 { 22 "only select", 23 pgstmt.Select(func(b pgstmt.SelectStatement) { 24 b.Columns("1") 25 }), 26 "select 1", 27 nil, 28 }, 29 { 30 "select arg", 31 pgstmt.Select(func(b pgstmt.SelectStatement) { 32 b.Columns(pgstmt.Arg("x")) 33 }), 34 "select $1", 35 []any{ 36 "x", 37 }, 38 }, 39 { 40 "select without arg", 41 pgstmt.Select(func(b pgstmt.SelectStatement) { 42 b.Columns(1, "x", 1.2) 43 }), 44 "select 1, x, 1.2", 45 nil, 46 }, 47 { 48 "select from", 49 pgstmt.Select(func(b pgstmt.SelectStatement) { 50 b.Columns("id", "name") 51 b.From("users") 52 }), 53 "select id, name from users", 54 nil, 55 }, 56 { 57 "select from select", 58 pgstmt.Select(func(b pgstmt.SelectStatement) { 59 b.Columns("*") 60 b.FromSelect(func(b pgstmt.SelectStatement) { 61 b.Columns("p.id", "p.name") 62 b.ColumnSelect(func(b pgstmt.SelectStatement) { 63 b.Columns(stripSpace(` 64 json_build_object('content', coalesce(m.content, ''), 65 'type', coalesce(m.type, 0), 66 'timestamp', m.created_at) 67 `)) 68 b.From("messages m") 69 b.Where(func(b pgstmt.Cond) { 70 b.EqRaw("m.id", "p.id") 71 }) 72 b.OrderBy("created_at").Desc().NullsFirst() 73 b.Limit(1) 74 b.Offset(2) 75 }, "msg") 76 b.From("profile p") 77 b.LeftJoin("noti n").On(func(b pgstmt.Cond) { 78 b.EqRaw("n.id", "p.id") 79 b.Eq("n.user_id", 1) 80 }) 81 }, "t") 82 }), 83 ` 84 select * 85 from (select p.id, p.name, (select json_build_object('content', coalesce(m.content, ''), 86 'type', coalesce(m.type, 0), 87 'timestamp', m.created_at) 88 from messages m 89 where (m.id = p.id) 90 order by created_at desc nulls first 91 limit 1 92 offset 2) msg 93 from profile p 94 left join noti n on (n.id = p.id and n.user_id = $1)) t 95 `, 96 []any{ 97 1, 98 }, 99 }, 100 { 101 "select from where", 102 pgstmt.Select(func(b pgstmt.SelectStatement) { 103 b.Columns("id", "name") 104 b.From("users") 105 b.Where(func(b pgstmt.Cond) { 106 b.Eq("id", 3) 107 b.Eq("name", "test") 108 b.And(func(b pgstmt.Cond) { 109 b.Eq("age", 15) 110 b.Or(func(b pgstmt.Cond) { 111 b.Eq("age", 18) 112 }) 113 }) 114 b.Eq("is_active", true) 115 }) 116 }), 117 "select id, name from users where (id = $1 and name = $2 and is_active = $3) and ((age = $4) or (age = $5))", 118 []any{ 119 3, 120 "test", 121 true, 122 15, 123 18, 124 }, 125 }, 126 { 127 "select from where order", 128 pgstmt.Select(func(b pgstmt.SelectStatement) { 129 b.Columns("id", "name") 130 b.From("users") 131 b.Where(func(b pgstmt.Cond) { 132 b.Eq("id", 1) 133 }) 134 b.OrderBy("created_at").Asc().NullsLast() 135 b.OrderBy("id").Desc() 136 }), 137 "select id, name from users where (id = $1) order by created_at asc nulls last, id desc", 138 []any{ 139 1, 140 }, 141 }, 142 { 143 "select limit offset", 144 pgstmt.Select(func(b pgstmt.SelectStatement) { 145 b.Columns("id", "name") 146 b.From("users") 147 b.Where(func(b pgstmt.Cond) { 148 b.Eq("id", 1) 149 }) 150 b.OrderBy("id") 151 b.Limit(5) 152 b.Offset(10) 153 }), 154 "select id, name from users where (id = $1) order by id limit 5 offset 10", 155 []any{ 156 1, 157 }, 158 }, 159 { 160 "join", 161 pgstmt.Select(func(b pgstmt.SelectStatement) { 162 b.Columns("id", "name") 163 b.From("users") 164 b.LeftJoin("roles using id") 165 }), 166 "select id, name from users left join roles using id", 167 nil, 168 }, 169 { 170 "join on", 171 pgstmt.Select(func(b pgstmt.SelectStatement) { 172 b.Columns("id", "name") 173 b.From("users") 174 b.LeftJoin("roles").On(func(b pgstmt.Cond) { 175 b.EqRaw("users.id", "roles.id") 176 }) 177 }), 178 "select id, name from users left join roles on (users.id = roles.id)", 179 nil, 180 }, 181 { 182 "join using", 183 pgstmt.Select(func(b pgstmt.SelectStatement) { 184 b.Columns("id", "name") 185 b.From("users") 186 b.InnerJoin("roles").Using("id", "name") 187 }), 188 "select id, name from users inner join roles using (id, name)", 189 nil, 190 }, 191 { 192 "join select", 193 pgstmt.Select(func(b pgstmt.SelectStatement) { 194 b.Columns("id", "name", "count(*)") 195 b.From("users") 196 b.LeftJoinSelect(func(b pgstmt.SelectStatement) { 197 b.Columns("user_id", "data") 198 b.From("event") 199 }, "t").On(func(b pgstmt.Cond) { 200 b.EqRaw("t.user_id", "users.id") 201 }) 202 b.GroupBy("id", "name") 203 }), 204 "select id, name, count(*) from users left join (select user_id, data from event) t on (t.user_id = users.id) group by (id, name)", 205 nil, 206 }, 207 { 208 "group by having", 209 pgstmt.Select(func(b pgstmt.SelectStatement) { 210 b.Columns("city", "max(temp_lo)") 211 b.From("weather") 212 b.GroupBy("city") 213 b.Having(func(b pgstmt.Cond) { 214 b.LtRaw("max(temp_lo)", 40) 215 }) 216 }), 217 "select city, max(temp_lo) from weather group by (city) having (max(temp_lo) < 40)", 218 nil, 219 }, 220 { 221 "select any", 222 pgstmt.Select(func(b pgstmt.SelectStatement) { 223 b.Columns("*") 224 b.From("table") 225 b.Where(func(b pgstmt.Cond) { 226 b.Eq("x", pgstmt.Any(pq.Array([]int64{1, 2}))) 227 }) 228 }), 229 "select * from table where (x = any($1))", 230 []any{ 231 pq.Array([]int64{1, 2}), 232 }, 233 }, 234 { 235 "select all", 236 pgstmt.Select(func(b pgstmt.SelectStatement) { 237 b.Columns("*") 238 b.From("table") 239 b.Where(func(b pgstmt.Cond) { 240 b.Ne("x", pgstmt.All(pq.Array([]int64{1, 2}))) 241 }) 242 }), 243 "select * from table where (x != all($1))", 244 []any{ 245 pq.Array([]int64{1, 2}), 246 }, 247 }, 248 { 249 "select in", 250 pgstmt.Select(func(b pgstmt.SelectStatement) { 251 b.Columns("*") 252 b.From("table") 253 b.Where(func(b pgstmt.Cond) { 254 b.In("x", 1, 2) 255 }) 256 }), 257 "select * from table where (x in ($1, $2))", 258 []any{ 259 1, 260 2, 261 }, 262 }, 263 { 264 "select in select", 265 pgstmt.Select(func(b pgstmt.SelectStatement) { 266 b.Columns("*") 267 b.From("table") 268 b.Where(func(b pgstmt.Cond) { 269 b.InSelect("id", func(b pgstmt.SelectStatement) { 270 b.Columns("id") 271 b.From("table2") 272 }) 273 }) 274 }), 275 "select * from table where (id in (select id from table2))", 276 nil, 277 }, 278 { 279 "select not in", 280 pgstmt.Select(func(b pgstmt.SelectStatement) { 281 b.Columns("*") 282 b.From("table") 283 b.Where(func(b pgstmt.Cond) { 284 b.NotIn("x", 1, 2) 285 }) 286 }), 287 "select * from table where (x not in ($1, $2))", 288 []any{ 289 1, 290 2, 291 }, 292 }, 293 { 294 "select and mode", 295 pgstmt.Select(func(b pgstmt.SelectStatement) { 296 b.Columns("*") 297 b.From("table") 298 b.Where(func(b pgstmt.Cond) { 299 b.Mode().And() 300 b.EqRaw("a", 1) 301 b.EqRaw("a", 2) 302 }) 303 }), 304 "select * from table where (a = 1 and a = 2)", 305 nil, 306 }, 307 { 308 "select or mode", 309 pgstmt.Select(func(b pgstmt.SelectStatement) { 310 b.Columns("*") 311 b.From("table") 312 b.Where(func(b pgstmt.Cond) { 313 b.Mode().Or() 314 b.EqRaw("a", 1) 315 b.EqRaw("a", 2) 316 }) 317 }), 318 "select * from table where (a = 1 or a = 2)", 319 nil, 320 }, 321 { 322 "select nested or mode", 323 pgstmt.Select(func(b pgstmt.SelectStatement) { 324 b.Columns("*") 325 b.From("table") 326 b.Where(func(b pgstmt.Cond) { 327 b.EqRaw("a", 1) 328 b.And(func(b pgstmt.Cond) { 329 b.Mode().Or() 330 b.EqRaw("a", 2) 331 b.EqRaw("a", 3) 332 }) 333 }) 334 }), 335 "select * from table where (a = 1) and (a = 2 or a = 3)", 336 nil, 337 }, 338 { 339 "select nested and", 340 pgstmt.Select(func(b pgstmt.SelectStatement) { 341 b.Columns("*") 342 b.From("table") 343 b.Where(func(b pgstmt.Cond) { 344 b.EqRaw("a", 1) 345 b.EqRaw("b", 1) 346 b.And(func(b pgstmt.Cond) { 347 b.And(func(b pgstmt.Cond) { 348 b.EqRaw("c", 1) 349 b.EqRaw("d", 1) 350 }) 351 b.Or(func(b pgstmt.Cond) { 352 b.EqRaw("e", 1) 353 b.EqRaw("f", 1) 354 }) 355 }) 356 }) 357 }), 358 "select * from table where (a = 1 and b = 1) and ((c = 1 and d = 1) or (e = 1 and f = 1))", 359 nil, 360 }, 361 { 362 "select nested and single or without ops", 363 pgstmt.Select(func(b pgstmt.SelectStatement) { 364 b.Columns("*") 365 b.From("table") 366 b.Where(func(b pgstmt.Cond) { 367 b.EqRaw("a", 1) 368 b.EqRaw("b", 1) 369 b.And(func(b pgstmt.Cond) { 370 // nothing to `or` with 371 b.Or(func(b pgstmt.Cond) { 372 b.EqRaw("c", 1) 373 b.EqRaw("d", 1) 374 }) 375 }) 376 }) 377 }), 378 "select * from table where (a = 1 and b = 1) and (c = 1 and d = 1)", 379 nil, 380 }, 381 { 382 "select without op but nested", 383 pgstmt.Select(func(b pgstmt.SelectStatement) { 384 b.Columns("*") 385 b.From("table") 386 b.Where(func(b pgstmt.Cond) { 387 b.And(func(b pgstmt.Cond) { 388 b.Mode().Or() 389 b.EqRaw("a", 2) 390 b.EqRaw("a", 3) 391 }) 392 }) 393 }), 394 "select * from table where (a = 2 or a = 3)", 395 nil, 396 }, 397 { 398 "select distinct", 399 pgstmt.Select(func(b pgstmt.SelectStatement) { 400 b.Distinct() 401 b.Columns("col_1") 402 }), 403 "select distinct col_1", 404 nil, 405 }, 406 { 407 "select distinct on", 408 pgstmt.Select(func(b pgstmt.SelectStatement) { 409 b.Distinct().On("col_1", "col_2") 410 b.Columns("col_1", "col_3") 411 }), 412 "select distinct on (col_1, col_2) col_1, col_3", 413 nil, 414 }, 415 { 416 "left join lateral", 417 pgstmt.Select(func(b pgstmt.SelectStatement) { 418 b.Columns("m.name") 419 b.From("manufacturers m") 420 b.LeftJoin("lateral get_product_names(m.id) pname").On(func(b pgstmt.Cond) { 421 b.Raw("true") 422 }) 423 b.Where(func(b pgstmt.Cond) { 424 b.IsNull("pname") 425 }) 426 }), 427 ` 428 select m.name 429 from manufacturers m left join lateral get_product_names(m.id) pname on (true) 430 where (pname is null) 431 `, 432 nil, 433 }, 434 { 435 "left join lateral select", 436 pgstmt.Select(func(b pgstmt.SelectStatement) { 437 b.Columns("m.name") 438 b.From("manufacturers m") 439 b.LeftJoinLateralSelect(func(b pgstmt.SelectStatement) { 440 b.Columns("get_product_names(m.id) pname") 441 }, "t").On(func(b pgstmt.Cond) { 442 b.Raw("true") 443 }) 444 b.Where(func(b pgstmt.Cond) { 445 b.IsNull("pname") 446 }) 447 }), 448 ` 449 select m.name 450 from manufacturers m left join lateral (select get_product_names(m.id) pname) t on (true) 451 where (pname is null) 452 `, 453 nil, 454 }, 455 { 456 "inner join union", 457 pgstmt.Select(func(b pgstmt.SelectStatement) { 458 b.Columns("id") 459 b.From("table1") 460 b.InnerJoinUnion(func(b pgstmt.UnionStatement) { 461 b.Select(func(b pgstmt.SelectStatement) { 462 b.Columns("id") 463 b.From("table2") 464 }) 465 b.AllSelect(func(b pgstmt.SelectStatement) { 466 b.Columns("id") 467 b.From("table3") 468 }) 469 b.OrderBy("id").Desc() 470 b.Limit(100) 471 }, "t").Using("id") 472 }), 473 ` 474 select id 475 from table1 476 inner join ( 477 (select id from table2) 478 union all 479 (select id from table3) 480 order by id desc 481 limit 100 482 ) t using (id) 483 `, 484 nil, 485 }, 486 { 487 "select where not", 488 pgstmt.Select(func(b pgstmt.SelectStatement) { 489 b.Columns("*") 490 b.From("table1") 491 b.Where(func(b pgstmt.Cond) { 492 b.Eq("id", 1) 493 b.Not(func(b pgstmt.Cond) { 494 b.Op("tags", "@>", pq.Array([]string{"a", "b"})) 495 }) 496 }) 497 }), 498 ` 499 select * 500 from table1 501 where (id = $1 and (not (tags @> $2))) 502 `, 503 []any{1, pq.Array([]string{"a", "b"})}, 504 }, 505 { 506 "select cond eq", 507 pgstmt.Select(func(b pgstmt.SelectStatement) { 508 b.Columns("*") 509 b.From("table1") 510 b.Where(func(b pgstmt.Cond) { 511 b.Field("id").Eq().Value(1) 512 b.Field("name").Eq().Field("old_name") 513 b.Value(2).Eq().Field(pgstmt.Any("path")) 514 b.Field("t1").In().Value(3, 4) 515 b.Field("t2").In().Select(func(b pgstmt.SelectStatement) { 516 b.Columns(1) 517 }) 518 b.Field("deleted_at").IsNull() 519 }) 520 }), 521 ` 522 select * 523 from table1 524 where (id = $1 525 and name = old_name 526 and $2 = any(path) 527 and t1 in ($3, $4) 528 and t2 in (select 1) 529 and deleted_at is null) 530 `, 531 []any{1, 2, 3, 4}, 532 }, 533 { 534 "select where any", 535 pgstmt.Select(func(b pgstmt.SelectStatement) { 536 b.Columns("*") 537 b.From("table1") 538 b.Where(func(b pgstmt.Cond) { 539 b.Eq(pgstmt.Arg(1), pgstmt.Any(pgstmt.Raw("path"))) 540 }) 541 }), 542 ` 543 select * 544 from table1 545 where ($1 = any(path)) 546 `, 547 []any{1}, 548 }, 549 { 550 "select exists", 551 pgstmt.Select(func(b pgstmt.SelectStatement) { 552 b.ColumnExists(func(b pgstmt.SelectStatement) { 553 b.Columns("1") 554 b.From("table1") 555 b.Where(func(b pgstmt.Cond) { 556 b.Eq("t1", 1) 557 }) 558 }) 559 }), 560 ` 561 select exists ( 562 select 1 563 from table1 564 where (t1 = $1) 565 ) 566 `, 567 []any{1}, 568 }, 569 { 570 "array overlap", 571 pgstmt.Select(func(b pgstmt.SelectStatement) { 572 b.Columns("*") 573 b.From("table1") 574 b.Where(func(b pgstmt.Cond) { 575 b.Op("tags", "&&", pq.Array([]string{"a", "b"})) 576 }) 577 }), 578 ` 579 select * 580 from table1 581 where (tags && $1) 582 `, 583 []any{pq.Array([]string{"a", "b"})}, 584 }, 585 } 586 587 for _, tC := range cases { 588 t.Run(tC.name, func(t *testing.T) { 589 q, args := tC.result.SQL() 590 assert.Equal(t, stripSpace(tC.query), q) 591 assert.EqualValues(t, tC.args, args) 592 }) 593 } 594 }