Skip to content
This repository was archived by the owner on Jun 14, 2019. It is now read-only.

Commit 7bcac66

Browse files
BetaCat0lunny
authored andcommitted
Rewrite From (#30)
* add more test cases & replace some err in errors.New(...) style & add some built-in err types * update From & related test cases * add test cases & fix bug * fix issues
1 parent c53507e commit 7bcac66

File tree

9 files changed

+81
-43
lines changed

9 files changed

+81
-43
lines changed

builder.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type limit struct {
4343
type Builder struct {
4444
optype
4545
dialect string
46+
isNested bool
4647
tableName string
4748
subQuery *Builder
4849
cond Cond
@@ -69,12 +70,23 @@ func (b *Builder) Where(cond Cond) *Builder {
6970
return b
7071
}
7172

72-
// From sets the name of table or the sub query's alias and itself
73-
func (b *Builder) From(tableName string, subQuery ...*Builder) *Builder {
74-
b.tableName = tableName
73+
// From sets from subject(can be a table name in string or a builder pointer) and its alias
74+
func (b *Builder) From(subject interface{}, alias ...string) *Builder {
75+
switch subject.(type) {
76+
case *Builder:
77+
b.subQuery = subject.(*Builder)
7578

76-
if len(subQuery) > 0 {
77-
b.subQuery = subQuery[0]
79+
if len(alias) > 0 {
80+
b.tableName = alias[0]
81+
} else {
82+
b.isNested = true
83+
}
84+
case string:
85+
b.tableName = subject.(string)
86+
87+
if len(alias) > 0 {
88+
b.tableName = b.tableName + " " + alias[0]
89+
}
7890
}
7991

8092
return b

builder_b_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func randQuery(dialect string, rgc *randGenConf) *Builder {
149149
}
150150

151151
if isUnionized && rgc.allowLimit && rand.Intn(1000) >= 500 {
152-
b = randLimit(Dialect(dialect).Select().From("t", b))
152+
b = randLimit(Dialect(dialect).Select().From(b, "t"))
153153
}
154154

155155
return b
@@ -198,7 +198,7 @@ func randSelectByCondition(dialect string, rgc *randGenConf) *Builder {
198198
if rgc.allowSubQuery {
199199
cpRgc := *rgc
200200
cpRgc.allowSubQuery = false
201-
b = Dialect(dialect).Select(randSelects()...).From(randTableName(0), randQuery(dialect, &cpRgc))
201+
b = Dialect(dialect).Select(randSelects()...).From(randQuery(dialect, &cpRgc), randTableName(0))
202202
} else {
203203
b = Dialect(dialect).Select(randSelects()...).From(randTableName(0))
204204
}

builder_limit.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,19 @@
55
package builder
66

77
import (
8-
"errors"
98
"fmt"
109
"strings"
1110
)
1211

1312
func (b *Builder) limitWriteTo(w Writer) error {
1413
if strings.TrimSpace(b.dialect) == "" {
15-
return errors.New("field `dialect` must be set up when performing LIMIT, try use `Dialect(dbType)` at first")
14+
return ErrDialectNotSetUp
1615
}
1716

1817
if b.limitation != nil {
1918
limit := b.limitation
2019
if limit.offset < 0 || limit.limitN <= 0 {
21-
return errors.New("unexpected offset/limitN")
20+
return ErrInvalidLimitation
2221
}
2322
// erase limit condition
2423
b.limitation = nil
@@ -37,19 +36,19 @@ func (b *Builder) limitWriteTo(w Writer) error {
3736
var wb *Builder
3837
if b.optype == unionType {
3938
wb = Dialect(b.dialect).Select("at.*", "ROWNUM RN").
40-
From("at", b)
39+
From(b, "at")
4140
} else {
4241
wb = b
4342
}
4443

4544
if limit.offset == 0 {
46-
final = Dialect(b.dialect).Select(selects...).From("at", wb).
45+
final = Dialect(b.dialect).Select(selects...).From(wb, "at").
4746
Where(Lte{"at.RN": limit.limitN})
4847
} else {
4948
sub := Dialect(b.dialect).Select("*").
50-
From("at", b).Where(Lte{"at.RN": limit.offset + limit.limitN})
49+
From(b, "at").Where(Lte{"at.RN": limit.offset + limit.limitN})
5150

52-
final = Dialect(b.dialect).Select(selects...).From("att", sub).
51+
final = Dialect(b.dialect).Select(selects...).From(sub, "att").
5352
Where(Gt{"att.RN": limit.offset})
5453
}
5554

@@ -78,15 +77,15 @@ func (b *Builder) limitWriteTo(w Writer) error {
7877
var wb *Builder
7978
if b.optype == unionType {
8079
wb = Dialect(b.dialect).Select("*", "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN").
81-
From("at", b)
80+
From(b, "at")
8281
} else {
8382
wb = b
8483
}
8584

8685
if limit.offset == 0 {
87-
final = Dialect(b.dialect).Select(selects...).From("at", wb)
86+
final = Dialect(b.dialect).Select(selects...).From(wb, "at")
8887
} else {
89-
final = Dialect(b.dialect).Select(selects...).From("at", wb).Where(Gt{"at.RN": limit.offset})
88+
final = Dialect(b.dialect).Select(selects...).From(wb, "at").Where(Gt{"at.RN": limit.offset})
9089
}
9190

9291
return final.WriteTo(ow)

builder_limit_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ func TestBuilder_Limit4Mssql(t *testing.T) {
3232
assert.NoError(t, f.executableCheck(sql))
3333

3434
// union with limit -- MsSQL style
35-
sql, err = Dialect(MSSQL).Select("a", "b", "c").From("at",
35+
sql, err = Dialect(MSSQL).Select("a", "b", "c").From(
3636
Dialect(MSSQL).Select("a", "b", "c").From("table1").Where(Neq{"a": "1"}).
3737
OrderBy("a ASC").Limit(5, 6).Union("ALL",
38-
Select("a", "b", "c").From("table1").Where(Neq{"b": "2"}).OrderBy("a DESC").Limit(10))).
38+
Select("a", "b", "c").From("table1").Where(Neq{"b": "2"}).OrderBy("a DESC").Limit(10)), "at").
3939
OrderBy("b DESC").Limit(7, 9).ToBoundSQL()
4040
assert.NoError(t, err)
4141
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT TOP 16 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM ((SELECT a,b,c FROM (SELECT TOP 11 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM table1 WHERE a<>'1' ORDER BY a ASC) at WHERE at.RN>6) UNION ALL (SELECT a,b,c FROM (SELECT TOP 10 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM table1 WHERE b<>'2' ORDER BY a DESC) at)) at ORDER BY b DESC) at WHERE at.RN>9", sql)
@@ -71,10 +71,10 @@ func TestBuilder_Limit4MysqlLike(t *testing.T) {
7171
assert.NoError(t, f.executableCheck(sql))
7272

7373
// union -- MySQL/SQLite/PostgreSQL style
74-
sql, err = Dialect(MYSQL).Select("a", "b", "c").From("at",
74+
sql, err = Dialect(MYSQL).Select("a", "b", "c").From(
7575
Dialect(MYSQL).Select("a", "b", "c").From("table1").Where(Eq{"a": "1"}).OrderBy("a ASC").
7676
Limit(5, 9).Union("ALL",
77-
Select("a", "b", "c").From("table1").Where(Eq{"a": "2"}).OrderBy("a DESC").Limit(10))).
77+
Select("a", "b", "c").From("table1").Where(Eq{"a": "2"}).OrderBy("a DESC").Limit(10)), "at").
7878
Limit(5, 10).ToBoundSQL()
7979
assert.NoError(t, err)
8080
assert.EqualValues(t, "SELECT a,b,c FROM ((SELECT a,b,c FROM table1 WHERE a='1' ORDER BY a ASC LIMIT 5 OFFSET 9) UNION ALL (SELECT a,b,c FROM table1 WHERE a='2' ORDER BY a DESC LIMIT 10)) at LIMIT 5 OFFSET 10", sql)
@@ -117,11 +117,11 @@ func TestBuilder_Limit4Oracle(t *testing.T) {
117117
assert.NoError(t, f.executableCheck(sql))
118118

119119
// union with limit -- OracleSQL style
120-
sql, err = Dialect(ORACLE).Select("a", "b", "c").From("at",
120+
sql, err = Dialect(ORACLE).Select("a", "b", "c").From(
121121
Dialect(ORACLE).Select("a", "b", "c").From("table1").
122122
Where(Neq{"a": "0"}).OrderBy("a ASC").Limit(5, 10).Union("ALL",
123123
Select("a", "b", "c").From("table1").Where(Neq{"b": "48"}).
124-
OrderBy("a DESC").Limit(10))).
124+
OrderBy("a DESC").Limit(10)), "at").
125125
Limit(3).ToBoundSQL()
126126
assert.NoError(t, err)
127127
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT a,b,c,ROWNUM RN FROM ((SELECT a,b,c FROM (SELECT * FROM (SELECT a,b,c,ROWNUM RN FROM table1 WHERE a<>'0' ORDER BY a ASC) at WHERE at.RN<=15) att WHERE att.RN>10) UNION ALL (SELECT a,b,c FROM (SELECT a,b,c,ROWNUM RN FROM table1 WHERE b<>'48' ORDER BY a DESC) at WHERE at.RN<=10)) at) at WHERE at.RN<=3", sql)

builder_select.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package builder
66

77
import (
8-
"errors"
98
"fmt"
109
)
1110

@@ -16,7 +15,7 @@ func Select(cols ...string) *Builder {
1615
}
1716

1817
func (b *Builder) selectWriteTo(w Writer) error {
19-
if len(b.tableName) <= 0 {
18+
if len(b.tableName) <= 0 && !b.isNested {
2019
return ErrNoTableName
2120
}
2221

@@ -26,12 +25,6 @@ func (b *Builder) selectWriteTo(w Writer) error {
2625
return b.limitWriteTo(w)
2726
}
2827

29-
// perform limit before writing to writer when b.dialect between ORACLE and MSSQL
30-
// this avoid a duplicate writing problem in simple limit query
31-
if b.limitation != nil && (b.dialect == ORACLE || b.dialect == MSSQL) {
32-
return b.limitWriteTo(w)
33-
}
34-
3528
if _, err := fmt.Fprint(w, "SELECT "); err != nil {
3629
return err
3730
}
@@ -57,6 +50,10 @@ func (b *Builder) selectWriteTo(w Writer) error {
5750
return err
5851
}
5952
} else {
53+
if b.cond.IsValid() && len(b.tableName) <= 0 {
54+
return ErrUnnamedDerivedTable
55+
}
56+
6057
switch b.subQuery.optype {
6158
case selectType, unionType:
6259
fmt.Fprint(w, " FROM (")
@@ -70,7 +67,7 @@ func (b *Builder) selectWriteTo(w Writer) error {
7067
fmt.Fprintf(w, ") %v", b.tableName)
7168
}
7269
default:
73-
return errors.New("SubQuery is limited in SELECT and UNION")
70+
return ErrUnexpectedSubQuery
7471
}
7572
}
7673

builder_select_test.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,46 @@ func TestBuilder_From(t *testing.T) {
7777
assert.EqualValues(t, "SELECT c FROM table1", sql)
7878
assert.EqualValues(t, 0, len(args))
7979

80-
// from sub
81-
sql, args, err = Select("sub.id").From("sub",
82-
Select("id").From("table1").Where(Eq{"a": 1})).Where(Eq{"b": 1}).ToSQL()
80+
// from sub with alias
81+
sql, args, err = Select("sub.id").From(Select("id").From("table1").Where(Eq{"a": 1}),
82+
"sub").Where(Eq{"b": 1}).ToSQL()
8383
assert.NoError(t, err)
8484
assert.EqualValues(t, "SELECT sub.id FROM (SELECT id FROM table1 WHERE a=?) sub WHERE b=?", sql)
8585
assert.EqualValues(t, []interface{}{1, 1}, args)
8686

87-
// from union
88-
sql, args, err = Select("sub.id").From("sub",
89-
Select("id").From("table1").Where(Eq{"a": 1}).
90-
Union("all", Select("id").From("table1").Where(Eq{"a": 2}))).Where(Eq{"b": 1}).ToSQL()
87+
// from sub without alias and with conditions
88+
sql, args, err = Select("sub.id").From(Select("id").From("table1").Where(Eq{"a": 1})).Where(Eq{"b": 1}).ToSQL()
89+
assert.Error(t, err)
90+
assert.EqualValues(t, ErrUnnamedDerivedTable, err)
91+
92+
// from sub without alias and conditions
93+
sql, args, err = Select("sub.id").From(Select("id").From("table1").Where(Eq{"a": 1})).ToSQL()
94+
assert.NoError(t, err)
95+
assert.EqualValues(t, "SELECT sub.id FROM (SELECT id FROM table1 WHERE a=?)", sql)
96+
assert.EqualValues(t, []interface{}{1}, args)
97+
98+
// from union with alias
99+
sql, args, err = Select("sub.id").From(
100+
Select("id").From("table1").Where(Eq{"a": 1}).Union(
101+
"all", Select("id").From("table1").Where(Eq{"a": 2})), "sub").Where(Eq{"b": 1}).ToSQL()
91102
assert.NoError(t, err)
92103
assert.EqualValues(t, "SELECT sub.id FROM ((SELECT id FROM table1 WHERE a=?) UNION ALL (SELECT id FROM table1 WHERE a=?)) sub WHERE b=?", sql)
93104
assert.EqualValues(t, []interface{}{1, 2, 1}, args)
94105

106+
// from union without alias
107+
sql, args, err = Select("sub.id").From(
108+
Select("id").From("table1").Where(Eq{"a": 1}).Union(
109+
"all", Select("id").From("table1").Where(Eq{"a": 2}))).Where(Eq{"b": 1}).ToSQL()
110+
assert.Error(t, err)
111+
assert.EqualValues(t, ErrUnnamedDerivedTable, err)
112+
113+
// will raise error
114+
sql, args, err = Select("c").From(Insert(Eq{"a": 1}).From("table1"), "table1").ToSQL()
115+
assert.Error(t, err)
116+
assert.EqualValues(t, ErrUnexpectedSubQuery, err)
117+
95118
// will raise error
96-
sql, args, err = Select("c").From("table1", Insert(Eq{"a": 1}).From("table1")).ToSQL()
119+
sql, args, err = Select("c").From(Delete(Eq{"a": 1}).From("table1"), "table1").ToSQL()
97120
assert.Error(t, err)
98-
fmt.Println(err)
121+
assert.EqualValues(t, ErrUnexpectedSubQuery, err)
99122
}

builder_union.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
func (b *Builder) unionWriteTo(w Writer) error {
13-
if b.limitation != nil || b.cond != NewCond() ||
13+
if b.limitation != nil || b.cond.IsValid() ||
1414
b.orderBy != "" || b.having != "" || b.groupBy != "" {
1515
return ErrNotUnexpectedUnionConditions
1616
}

error.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,12 @@ var (
2727
ErrNotUnexpectedUnionConditions = errors.New("Unexpected conditional fields in UNION query")
2828
// ErrUnsupportedUnionMembers unexpected members in UNION query
2929
ErrUnsupportedUnionMembers = errors.New("Unexpected members in UNION query")
30+
// ErrUnexpectedSubQuery Unexpected sub-query in SELECT query
31+
ErrUnexpectedSubQuery = errors.New("Unexpected sub-query in SELECT query")
32+
// ErrDialectNotSetUp dialect is not setup yet
33+
ErrDialectNotSetUp = errors.New("Dialect is not setup yet, try to use `Dialect(dbType)` at first")
34+
// ErrInvalidLimitation offset or limit is not correct
35+
ErrInvalidLimitation = errors.New("Offset or limit is not correct")
36+
// ErrUnnamedDerivedTable Every derived table must have its own alias
37+
ErrUnnamedDerivedTable = errors.New("Every derived table must have its own alias")
3038
)

sql_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,4 @@ func TestExecutableCheck(t *testing.T) {
157157

158158
err = f.executableCheck("SELECT * FROM table3")
159159
assert.Error(t, err)
160-
fmt.Println(err)
161160
}

0 commit comments

Comments
 (0)