Skip to content

Commit

Permalink
Implement SQL UNION.
Browse files Browse the repository at this point in the history
Does not yet work with ORDER BY or LIMIT on the UNION.
  • Loading branch information
danhhz committed Mar 3, 2016
1 parent 972d25a commit 3594cbc
Show file tree
Hide file tree
Showing 6 changed files with 480 additions and 10 deletions.
6 changes: 3 additions & 3 deletions sql/parser/sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions sql/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -1936,7 +1936,7 @@ simple_select:
| select_clause UNION all_or_distinct select_clause
{
$$.val = &Union{
Type: astUnion,
Type: UnionOp,
Left: $1.selectStmt(),
Right: $4.selectStmt(),
All: $3.bool(),
Expand All @@ -1945,7 +1945,7 @@ simple_select:
| select_clause INTERSECT all_or_distinct select_clause
{
$$.val = &Union{
Type: astIntersect,
Type: IntersectOp,
Left: $1.selectStmt(),
Right: $4.selectStmt(),
All: $3.bool(),
Expand All @@ -1954,7 +1954,7 @@ simple_select:
| select_clause EXCEPT all_or_distinct select_clause
{
$$.val = &Union{
Type: astExcept,
Type: ExceptOp,
Left: $1.selectStmt(),
Right: $4.selectStmt(),
All: $3.bool(),
Expand Down
24 changes: 20 additions & 4 deletions sql/parser/union.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,34 @@ import "fmt"

// Union represents a UNION statement.
type Union struct {
Type string
Type UnionType
Left, Right SelectStatement
All bool
}

// UnionType represents one of the three set operations in sql.
type UnionType int

// Union.Type
const (
astUnion = "UNION"
astExcept = "EXCEPT"
astIntersect = "INTERSECT"
UnionOp UnionType = iota
IntersectOp
ExceptOp
)

var unionTypeName = [...]string{
UnionOp: "UNION",
IntersectOp: "INTERSECT",
ExceptOp: "EXCEPT",
}

func (i UnionType) String() string {
if i < 0 || i > UnionType(len(unionTypeName)-1) {
return fmt.Sprintf("UnionType(%d)", i)
}
return unionTypeName[i]
}

func (node *Union) String() string {
all := ""
if node.All {
Expand Down
3 changes: 3 additions & 0 deletions sql/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ func (p *planner) makePlan(stmt parser.Statement, autoCommit bool) (planNode, *r
return p.ShowTables(n)
case *parser.Truncate:
return p.Truncate(n)
case *parser.Union:
return p.Union(n, autoCommit)
case *parser.Update:
return p.Update(n, autoCommit)
case *parser.Values:
Expand Down Expand Up @@ -321,6 +323,7 @@ var _ planNode = &scanNode{}
var _ planNode = &sortNode{}
var _ planNode = &valuesNode{}
var _ planNode = &selectNode{}
var _ planNode = &unionNode{}
var _ planNode = &emptyNode{}
var _ planNode = &explainDebugNode{}
var _ planNode = &explainTraceNode{}
Expand Down
134 changes: 134 additions & 0 deletions sql/testdata/union
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
query I rowsort
VALUES (1), (1), (1), (2), (2) UNION VALUES (1), (3), (1)
----
1
2
3

query I rowsort
VALUES (1), (1), (1), (2), (2) UNION ALL VALUES (1), (3), (1)
----
1
1
1
1
1
2
2
3

query I rowsort
VALUES (1), (1), (1), (2), (2) INTERSECT VALUES (1), (3), (1)
----
1

query I rowsort
VALUES (1), (1), (1), (2), (2) INTERSECT ALL VALUES (1), (3), (1)
----
1
1

query I rowsort
VALUES (1), (1), (1), (2), (2) EXCEPT VALUES (1), (3), (1)
----
2

query I rowsort
VALUES (1), (1), (1), (2), (2) EXCEPT ALL VALUES (1), (3), (1)
----
1
2
2

query II rowsort
VALUES (1, 2), (1, 1), (1, 2), (2, 1), (2, 1) UNION VALUES (1, 3), (3, 4), (1, 1)
----
1 1
1 2
1 3
2 1
3 4

statement ok
CREATE TABLE uniontest (
k INT,
v INT
)

statement OK
INSERT INTO uniontest VALUES
(1, 1),
(1, 1),
(1, 1),
(1, 2),
(1, 2),
(2, 1),
(2, 3),
(2, 1)

query I rowsort
SELECT v FROM uniontest WHERE k = 1 UNION SELECT v FROM uniontest WHERE k = 2
----
1
2
3

query I rowsort
SELECT v FROM uniontest WHERE k = 1 UNION ALL SELECT v FROM uniontest WHERE k = 2
----
1
1
1
1
1
2
2
3

query I rowsort
SELECT v FROM uniontest WHERE k = 1 INTERSECT SELECT v FROM uniontest WHERE k = 2
----
1

query I rowsort
SELECT v FROM uniontest WHERE k = 1 INTERSECT ALL SELECT v FROM uniontest WHERE k = 2
----
1
1

query I rowsort
SELECT v FROM uniontest WHERE k = 1 EXCEPT SELECT v FROM uniontest WHERE k = 2
----
2

query I rowsort
SELECT v FROM uniontest WHERE k = 1 EXCEPT ALL SELECT v FROM uniontest WHERE k = 2
----
1
2
2

query ITT rowsort
EXPLAIN SELECT v FROM uniontest UNION SELECT k FROM uniontest
----
0 union -
1 scan uniontest@primary
1 scan uniontest@primary

query error each UNION query must have the same number of columns: 2 vs 1
SELECT 1, 2 UNION SELECT 3

query error each INTERSECT query must have the same number of columns: 2 vs 1
SELECT 1, 2 INTERSECT SELECT 3

query error each EXCEPT query must have the same number of columns: 2 vs 1
SELECT 1, 2 EXCEPT SELECT 3

query error UNION types int and string cannot be matched
SELECT 1 UNION SELECT '3'

query error INTERSECT types int and string cannot be matched
SELECT 1 INTERSECT SELECT '3'

query error EXCEPT types int and string cannot be matched
SELECT 1 EXCEPT SELECT '3'
Loading

0 comments on commit 3594cbc

Please sign in to comment.