Skip to content

Commit 6279156

Browse files
authored
Merge pull request #622 from manhrev/feat/sub-query-expression
Add EXISTS, ALL, SOME, ANY expressions
2 parents a2ce165 + 793b804 commit 6279156

File tree

6 files changed

+111
-2
lines changed

6 files changed

+111
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Added `bob.Each` function to iterate over query results (range-over-func). (thanks @toqueteos)
1616
- Added support for the `VALUES` statement in MySQL, PostgreSQL, and SQLite. (thanks @manhrev)
1717
- Added MariaDB compatibility check in gen/bobgen-mysql (thanks @dumdev25)
18+
- Added `ALL`, `SOME`, `ANY` expressions for MySQL and PostgreSQL dialects. Added `EXISTS` expression for all dialects. (thanks @manhrev)
1819

1920
### Fixed
2021

dialect/mysql/starters.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func Raw(query string, args ...any) Expression {
9696
}
9797

9898
// SQL: CAST(a AS int)
99-
// Go: psql.Cast("a", "int")
99+
// Go: mysql.Cast("a", "int")
100100
func Cast(exp bob.Expression, typname string) Expression {
101101
return bmod.Cast(exp, typname)
102102
}
@@ -106,3 +106,27 @@ func Cast(exp bob.Expression, typname string) Expression {
106106
func Case() expr.CaseChain[Expression, Expression] {
107107
return expr.NewCase[Expression, Expression]()
108108
}
109+
110+
// SQL: EXISTS ((SELECT 1))
111+
// Go: mysql.Exists(mysql.Select(sm.Columns("1")))
112+
func Exists(exp bob.Expression) Expression {
113+
return bmod.Exists(exp)
114+
}
115+
116+
// SQL: - 1 - 2
117+
// Go: mysql.Minus(mysql.Arg(1)).Minus(mysql.Arg(2))
118+
func Minus(exp bob.Expression) Expression {
119+
return bmod.Minus(exp)
120+
}
121+
122+
// SQL: a = ANY((SELECT name FROM users))
123+
// Go: mysql.Quote("a").EQ(mysql.Any(mysql.Select(sm.Columns("name"), sm.From("users"))))
124+
func Any(exp bob.Expression) bob.Expression {
125+
return bmod.Any(exp)
126+
}
127+
128+
// SQL: a = ALL((SELECT name FROM users))
129+
// Go: mysql.Quote("a").EQ(mysql.All(mysql.Select(sm.Columns("name"), sm.From("users"))))
130+
func All(exp bob.Expression) bob.Expression {
131+
return bmod.All(exp)
132+
}

dialect/psql/starters.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,27 @@ func Cast(exp bob.Expression, typname string) Expression {
106106
func Case() expr.CaseChain[Expression, Expression] {
107107
return expr.NewCase[Expression, Expression]()
108108
}
109+
110+
// SQL: EXISTS ((SELECT 1))
111+
// Go: psql.Exists(psql.Select(sm.Columns("1")))
112+
func Exists(exp bob.Expression) Expression {
113+
return bmod.Exists(exp)
114+
}
115+
116+
// SQL: - 1 - 2
117+
// Go: psql.Minus(psql.Arg(1)).Minus(psql.Arg(2))
118+
func Minus(exp bob.Expression) Expression {
119+
return bmod.Minus(exp)
120+
}
121+
122+
// SQL: a = ANY((SELECT name FROM users))
123+
// Go: psql.Quote("a").EQ(psql.Any(psql.Select(sm.Columns("name"), sm.From("users"))))
124+
func Any(exp bob.Expression) bob.Expression {
125+
return bmod.Any(exp)
126+
}
127+
128+
// SQL: a = ALL((SELECT name FROM users))
129+
// Go: psql.Quote("a").EQ(psql.All(psql.Select(sm.Columns("name"), sm.From("users"))))
130+
func All(exp bob.Expression) bob.Expression {
131+
return bmod.All(exp)
132+
}

dialect/sqlite/starters.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func Raw(query string, args ...any) Expression {
9696
}
9797

9898
// SQL: CAST(a AS int)
99-
// Go: psql.Cast("a", "int")
99+
// Go: sqlite.Cast("a", "int")
100100
func Cast(exp bob.Expression, typname string) Expression {
101101
return bmod.Cast(exp, typname)
102102
}
@@ -106,3 +106,15 @@ func Cast(exp bob.Expression, typname string) Expression {
106106
func Case() expr.CaseChain[Expression, Expression] {
107107
return expr.NewCase[Expression, Expression]()
108108
}
109+
110+
// SQL: EXISTS ((SELECT 1))
111+
// Go: sqlite.Exists(sqlite.Select(sm.Columns("1")))
112+
func Exists(exp bob.Expression) Expression {
113+
return bmod.Exists(exp)
114+
}
115+
116+
// SQL: - 1 - 2
117+
// Go: sqlite.Minus(sqlite.Arg(1)).Minus(sqlite.Arg(2))
118+
func Minus(exp bob.Expression) Expression {
119+
return bmod.Minus(exp)
120+
}

expr/builder.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,30 @@ func Not[T bob.Expression, B builder[T]](exp bob.Expression) T {
4545
return b.New(Join{Exprs: []bob.Expression{not, X[T, B](exp)}})
4646
}
4747

48+
// Exists expression
49+
func Exists[T bob.Expression, B builder[T]](exp bob.Expression) T {
50+
var b B
51+
return b.New(Join{Exprs: []bob.Expression{exists, group{exp}}})
52+
}
53+
54+
// prefix the expression with a - (minus)
55+
func Minus[T bob.Expression, B builder[T]](exp bob.Expression) T {
56+
var b B
57+
return b.New(Join{Exprs: []bob.Expression{minus, X[T, B](exp)}})
58+
}
59+
60+
// ANY expression
61+
func Any[T bob.Expression, B builder[T]](exp bob.Expression) T {
62+
var b B
63+
return b.New(Join{Exprs: []bob.Expression{anyOp, group{exp}}})
64+
}
65+
66+
// ALL expression
67+
func All[T bob.Expression, B builder[T]](exp bob.Expression) T {
68+
var b B
69+
return b.New(Join{Exprs: []bob.Expression{all, group{exp}}})
70+
}
71+
4872
// To be embedded in query mods
4973
// T is the chain type, this allows dialects to have custom chain methods
5074
// F is function type, so that the dialect can change where it
@@ -107,3 +131,23 @@ func (e Builder[T, B]) Quote(aa ...string) T {
107131
func (e Builder[T, B]) Cast(exp bob.Expression, typname string) T {
108132
return X[T, B](Cast(exp, typname))
109133
}
134+
135+
// EXISTS expression
136+
func (e Builder[T, B]) Exists(exp bob.Expression) T {
137+
return Exists[T, B](exp)
138+
}
139+
140+
// prefix the expression with a - (minus)
141+
func (e Builder[T, B]) Minus(exp bob.Expression) T {
142+
return Minus[T, B](exp)
143+
}
144+
145+
// ANY expression
146+
func (e Builder[T, B]) Any(exp bob.Expression) T {
147+
return Any[T, B](exp)
148+
}
149+
150+
// ALL expression
151+
func (e Builder[T, B]) All(exp bob.Expression) T {
152+
return All[T, B](exp)
153+
}

expr/constants.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ var (
1818
notBetween = Raw("NOT BETWEEN")
1919
isDistinctFrom = Raw("IS DISTINCT FROM")
2020
isNotDistinctFrom = Raw("IS NOT DISTINCT FROM")
21+
exists = Raw("EXISTS")
22+
minus = Raw("-")
23+
anyOp = Raw("ANY")
24+
all = Raw("ALL")
2125
)

0 commit comments

Comments
 (0)