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  }