Skip to content

Commit

Permalink
feat: Add NOT EXISTS subquery
Browse files Browse the repository at this point in the history
  • Loading branch information
novln committed Oct 5, 2018
1 parent 9c70773 commit c6f2d75
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 1 deletion.
28 changes: 28 additions & 0 deletions builder/select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,34 @@ func TestSelect_Exists(t *testing.T) {
})
}

func TestSelect_NotExists(t *testing.T) {
RunBuilderTests(t, []BuilderTest{
{
Name: "Where clause",
Builder: loukoum.
Select("id").
From("users").
Where(loukoum.Condition("deleted_at").IsNull(true)).
And(loukoum.NotExists(loukoum.Select("1").From("news").Where(
loukoum.Condition("news.created_at").GreaterThan(2)),
)),
String: fmt.Sprint(
"SELECT id FROM users WHERE ((deleted_at IS NULL) AND ",
"(NOT EXISTS (SELECT 1 FROM news WHERE (news.created_at > 2))))",
),
Query: fmt.Sprint(
"SELECT id FROM users WHERE ((deleted_at IS NULL) AND ",
"(NOT EXISTS (SELECT 1 FROM news WHERE (news.created_at > $1))))",
),
NamedQuery: fmt.Sprint(
"SELECT id FROM users WHERE ((deleted_at IS NULL) AND ",
"(NOT EXISTS (SELECT 1 FROM news WHERE (news.created_at > :arg_1))))",
),
Args: []interface{}{2},
},
})
}

func TestSelect_GroupBy(t *testing.T) {
RunBuilderTests(t, []BuilderTest{
{
Expand Down
5 changes: 5 additions & 0 deletions loukoum.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ func Exists(value interface{}) stmt.Exists {
return stmt.NewExists(value)
}

// NotExists is a wrapper to create a new NotExists expression.
func NotExists(value interface{}) stmt.NotExists {
return stmt.NewNotExists(value)
}

// Count is a wrapper to create a new Count expression.
func Count(value string) stmt.Count {
return stmt.NewCount(value)
Expand Down
4 changes: 4 additions & 0 deletions stmt/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ func NewWrapper(arg Expression) Expression {
return &Wrapper{
Value: value,
}
case NotExists:
return &Wrapper{
Value: value,
}
default:
return arg
}
Expand Down
36 changes: 35 additions & 1 deletion stmt/subquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,45 @@ func (exists Exists) Write(ctx types.Context) {
}

// IsEmpty returns true if statement is undefined.
func (exists Exists) IsEmpty() bool {
func (Exists) IsEmpty() bool {
return false
}

func (Exists) selectExpression() {}

// Ensure that Exists is an Expression
var _ Expression = Exists{}

// NotExists is a subquery expression.
type NotExists struct {
Subquery Expression
}

// NewNotExists returns a new NotExists instance.
func NewNotExists(value interface{}) NotExists {
return NotExists{
Subquery: NewExpression(value),
}
}

func (NotExists) expression() {}

// Write exposes statement as a SQL query.
func (nexists NotExists) Write(ctx types.Context) {
ctx.Write(token.Not.String())
ctx.Write(" ")
ctx.Write(token.Exists.String())
ctx.Write(" (")
nexists.Subquery.Write(ctx)
ctx.Write(")")
}

// IsEmpty returns true if statement is undefined.
func (NotExists) IsEmpty() bool {
return false
}

func (NotExists) selectExpression() {}

// Ensure that NotExists is an Expression
var _ Expression = NotExists{}
2 changes: 2 additions & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const (
Do = Type("DO")
Nothing = Type("NOTHING")
With = Type("WITH")
Not = Type("NOT")
Exists = Type("EXISTS")
Count = Type("COUNT")
Max = Type("MAX")
Expand Down Expand Up @@ -118,6 +119,7 @@ var keywords = map[string]Type{
"DO": Do,
"NOTHING": Nothing,
"WITH": With,
"NOT": Not,
"EXISTS": Exists,
"COUNT": Count,
"MAX": Max,
Expand Down

0 comments on commit c6f2d75

Please sign in to comment.