Skip to content

Commit 1aa82bd

Browse files
committed
add day4 chain operation
1 parent 37f1c2e commit 1aa82bd

24 files changed

+870
-46
lines changed

gee-orm/day3-save-query/geeorm/clause/clause.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ type Type int
1515

1616
// Support types for Clause
1717
const (
18-
INSERT Type = 0
19-
VALUES Type = 1
20-
SELECT Type = 2
21-
LIMIT Type = 3
18+
INSERT Type = 0
19+
VALUES Type = 1
20+
SELECT Type = 2
21+
LIMIT Type = 3
22+
WHERE Type = 4
23+
ORDERBY Type = 5
2224
)
2325

2426
// Set adds a sub clause of specific type
@@ -33,7 +35,7 @@ func (c *Clause) Set(name Type, vars ...interface{}) {
3335
}
3436

3537
// Build generate the final SQL and SQLVars
36-
func (c *Clause) Build(orders []Type) (string, []interface{}) {
38+
func (c *Clause) Build(orders ...Type) (string, []interface{}) {
3739
var sqls []string
3840
var vars []interface{}
3941
for _, order := range orders {

gee-orm/day3-save-query/geeorm/clause/clause_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ func TestClause_Build(t *testing.T) {
2020
var clause Clause
2121
clause.Set(LIMIT, 3)
2222
clause.Set(SELECT, "User", []string{"*"})
23-
orders := []Type{SELECT, LIMIT}
24-
sql, vars := clause.Build(orders)
23+
clause.Set(WHERE, "Name = ?", 18)
24+
clause.Set(ORDERBY, "Age ASC")
25+
sql, vars := clause.Build(SELECT, WHERE, ORDERBY, LIMIT)
2526
t.Log(sql, vars)
26-
if sql != "SELECT * FROM User LIMIT ?" {
27+
if sql != "SELECT * FROM User WHERE Name = ? ORDER BY Age ASC LIMIT ?" {
2728
t.Fatal("failed to build SQL")
2829
}
29-
if !reflect.DeepEqual(vars, []interface{}{3}) {
30+
if !reflect.DeepEqual(vars, []interface{}{18, 3}) {
3031
t.Fatal("failed to build SQLVars")
3132
}
3233
}

gee-orm/day3-save-query/geeorm/clause/generator.go

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ func init() {
1515
generators[VALUES] = _values
1616
generators[SELECT] = _select
1717
generators[LIMIT] = _limit
18+
generators[WHERE] = _where
19+
generators[ORDERBY] = _orderby
1820
}
1921

2022
func genBindVars(num int) string {
@@ -64,3 +66,13 @@ func _limit(values ...interface{}) (string, []interface{}) {
6466
// LIMIT $num
6567
return "LIMIT ?", values
6668
}
69+
70+
func _where(values ...interface{}) (string, []interface{}) {
71+
// WHERE $desc
72+
desc, vars := values[0], values[1:]
73+
return fmt.Sprintf("WHERE %s", desc), vars
74+
}
75+
76+
func _orderby(values ...interface{}) (string, []interface{}) {
77+
return fmt.Sprintf("ORDER BY %s", values[0]), []interface{}{}
78+
}

gee-orm/day3-save-query/geeorm/session/record.go

+2-21
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func (s *Session) Create(values ...interface{}) (int64, error) {
1515
}
1616

1717
s.clause.Set(clause.VALUES, recordValues...)
18-
sql, vars := s.clause.Build([]clause.Type{clause.INSERT, clause.VALUES})
18+
sql, vars := s.clause.Build(clause.INSERT, clause.VALUES)
1919
result, err := s.Raw(sql, vars...).Exec()
2020
if err != nil {
2121
return 0, err
@@ -24,33 +24,14 @@ func (s *Session) Create(values ...interface{}) (int64, error) {
2424
return result.RowsAffected()
2525
}
2626

27-
// First gets the 1st row
28-
func (s *Session) First(value interface{}) error {
29-
table := s.RefTable(value)
30-
31-
s.clause.Set(clause.SELECT, table.TableName, table.FieldNames)
32-
s.clause.Set(clause.LIMIT, 1)
33-
34-
sql, vars := s.clause.Build([]clause.Type{clause.SELECT, clause.LIMIT})
35-
row := s.Raw(sql, vars...).QueryRow()
36-
37-
dest := reflect.ValueOf(value).Elem()
38-
var values []interface{}
39-
for _, name := range table.FieldNames {
40-
values = append(values, dest.FieldByName(name).Addr().Interface())
41-
}
42-
43-
return row.Scan(values...)
44-
}
45-
4627
// Find gets all eligible records
4728
func (s *Session) Find(values interface{}) error {
4829
destSlice := reflect.Indirect(reflect.ValueOf(values))
4930
destType := destSlice.Type().Elem()
5031
table := s.RefTable(reflect.New(destType).Elem().Interface())
5132

5233
s.clause.Set(clause.SELECT, table.TableName, table.FieldNames)
53-
sql, vars := s.clause.Build([]clause.Type{clause.SELECT})
34+
sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT)
5435
rows, err := s.Raw(sql, vars...).QueryRows()
5536
if err != nil {
5637
return err

gee-orm/day3-save-query/geeorm/session/record_test.go

+15-16
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,30 @@ import "testing"
55
var (
66
user1 = &User{"Tom", 18}
77
user2 = &User{"Sam", 25}
8+
user3 = &User{"Jack", 25}
89
)
910

10-
func TestSession_Create(t *testing.T) {
11-
_ = NewSession().DropTable(&User{})
12-
_ = NewSession().CreateTable(&User{})
13-
if affected, err := NewSession().Create(user1, user2); err != nil || affected != 2 {
14-
t.Fatal("failed to create record")
11+
func testRecordInit(t *testing.T) {
12+
t.Helper()
13+
err1 := NewSession().DropTable(&User{})
14+
err2 := NewSession().CreateTable(&User{})
15+
_, err3 := NewSession().Create(user1, user2)
16+
if err1 != nil || err2 != nil || err3 != nil {
17+
t.Fatal("failed init test records")
1518
}
19+
1620
}
1721

18-
func TestSession_First(t *testing.T) {
19-
_ = NewSession().DropTable(&User{})
20-
_ = NewSession().CreateTable(&User{})
21-
_, _ = NewSession().Create(user1)
22-
u := &User{}
23-
err := NewSession().First(u)
24-
if err != nil || u.Age != user1.Age || u.Name != user1.Name {
25-
t.Fatal("failed to query first")
22+
func TestSession_Create(t *testing.T) {
23+
testRecordInit(t)
24+
affected, err := NewSession().Create(user3)
25+
if err != nil || affected != 1 {
26+
t.Fatal("failed to create record")
2627
}
2728
}
2829

2930
func TestSession_Find(t *testing.T) {
30-
_ = NewSession().DropTable(&User{})
31-
_ = NewSession().CreateTable(&User{})
32-
_, _ = NewSession().Create(user1, user2)
31+
testRecordInit(t)
3332
users := []User{}
3433
if err := NewSession().Find(&users); err != nil || len(users) != 2 {
3534
t.Fatal("failed to query all")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package clause
2+
3+
import (
4+
"strings"
5+
)
6+
7+
// Clause contains SQL conditions
8+
type Clause struct {
9+
sql map[Type]string
10+
sqlVars map[Type][]interface{}
11+
}
12+
13+
// Type is the type of Clause
14+
type Type int
15+
16+
// Support types for Clause
17+
const (
18+
INSERT Type = 0
19+
VALUES Type = 1
20+
SELECT Type = 2
21+
LIMIT Type = 3
22+
WHERE Type = 4
23+
ORDERBY Type = 5
24+
)
25+
26+
// Set adds a sub clause of specific type
27+
func (c *Clause) Set(name Type, vars ...interface{}) {
28+
if c.sql == nil {
29+
c.sql = make(map[Type]string)
30+
c.sqlVars = make(map[Type][]interface{})
31+
}
32+
sql, vars := generators[name](vars...)
33+
c.sql[name] = sql
34+
c.sqlVars[name] = vars
35+
}
36+
37+
// Build generate the final SQL and SQLVars
38+
func (c *Clause) Build(orders ...Type) (string, []interface{}) {
39+
var sqls []string
40+
var vars []interface{}
41+
for _, order := range orders {
42+
if sql, ok := c.sql[order]; ok {
43+
sqls = append(sqls, sql)
44+
vars = append(vars, c.sqlVars[order]...)
45+
}
46+
}
47+
return strings.Join(sqls, " "), vars
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package clause
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestClause_Set(t *testing.T) {
9+
var clause Clause
10+
clause.Set(INSERT, "User", []string{"Name", "Age"})
11+
sql := clause.sql[INSERT]
12+
vars := clause.sqlVars[INSERT]
13+
t.Log(sql, vars)
14+
if sql != "INSERT INTO User (Name,Age)" || len(vars) != 0 {
15+
t.Fatal("failed to get clause")
16+
}
17+
}
18+
19+
func TestClause_Build(t *testing.T) {
20+
var clause Clause
21+
clause.Set(LIMIT, 3)
22+
clause.Set(SELECT, "User", []string{"*"})
23+
clause.Set(WHERE, "Name = ?", 18)
24+
clause.Set(ORDERBY, "Age ASC")
25+
sql, vars := clause.Build(SELECT, WHERE, ORDERBY, LIMIT)
26+
t.Log(sql, vars)
27+
if sql != "SELECT * FROM User WHERE Name = ? ORDER BY Age ASC LIMIT ?" {
28+
t.Fatal("failed to build SQL")
29+
}
30+
if !reflect.DeepEqual(vars, []interface{}{18, 3}) {
31+
t.Fatal("failed to build SQLVars")
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package clause
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type generator func(values ...interface{}) (string, []interface{})
9+
10+
var generators map[Type]generator
11+
12+
func init() {
13+
generators = make(map[Type]generator)
14+
generators[INSERT] = _insert
15+
generators[VALUES] = _values
16+
generators[SELECT] = _select
17+
generators[LIMIT] = _limit
18+
generators[WHERE] = _where
19+
generators[ORDERBY] = _orderby
20+
}
21+
22+
func genBindVars(num int) string {
23+
var vars []string
24+
for i := 0; i < num; i++ {
25+
vars = append(vars, "?")
26+
}
27+
return strings.Join(vars, ", ")
28+
}
29+
30+
func _insert(values ...interface{}) (string, []interface{}) {
31+
// INSERT INTO $tableName ($fields)
32+
tableName := values[0]
33+
fields := strings.Join(values[1].([]string), ",")
34+
return fmt.Sprintf("INSERT INTO %s (%v)", tableName, fields), []interface{}{}
35+
}
36+
37+
func _values(values ...interface{}) (string, []interface{}) {
38+
// VALUES ($v1), (&v2), ...
39+
var bindStr string
40+
var sql strings.Builder
41+
var vars []interface{}
42+
sql.WriteString("VALUES ")
43+
for i, value := range values {
44+
v := value.([]interface{})
45+
if bindStr == "" {
46+
bindStr = genBindVars(len(v))
47+
}
48+
sql.WriteString(fmt.Sprintf("(%v)", bindStr))
49+
if i+1 != len(values) {
50+
sql.WriteString(", ")
51+
}
52+
vars = append(vars, v...)
53+
}
54+
return sql.String(), vars
55+
56+
}
57+
58+
func _select(values ...interface{}) (string, []interface{}) {
59+
// SELECT $fields FROM $tableName
60+
tableName := values[0]
61+
fields := strings.Join(values[1].([]string), ",")
62+
return fmt.Sprintf("SELECT %v FROM %s", fields, tableName), []interface{}{}
63+
}
64+
65+
func _limit(values ...interface{}) (string, []interface{}) {
66+
// LIMIT $num
67+
return "LIMIT ?", values
68+
}
69+
70+
func _where(values ...interface{}) (string, []interface{}) {
71+
// WHERE $desc
72+
desc, vars := values[0], values[1:]
73+
return fmt.Sprintf("WHERE %s", desc), vars
74+
}
75+
76+
func _orderby(values ...interface{}) (string, []interface{}) {
77+
return fmt.Sprintf("ORDER BY %s", values[0]), []interface{}{}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package dialect
2+
3+
import "reflect"
4+
5+
var dialectsMap = map[string]Dialect{}
6+
7+
// Dialect is an interface contains methods that a dialect has to implement
8+
type Dialect interface {
9+
DataTypeOf(typ reflect.Value) string
10+
TableExistSQL(tableName string) (string, []interface{})
11+
}
12+
13+
// RegisterDialect register a dialect to the global variable
14+
func RegisterDialect(name string, dialect Dialect) {
15+
dialectsMap[name] = dialect
16+
}
17+
18+
// Get the dialect from global variable if it exists
19+
func GetDialect(name string) (dialect Dialect, ok bool) {
20+
dialect, ok = dialectsMap[name]
21+
return
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package dialect
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"time"
7+
)
8+
9+
type sqlite3 struct{}
10+
11+
var _ Dialect = (*sqlite3)(nil)
12+
13+
func init() {
14+
RegisterDialect("sqlite3", &sqlite3{})
15+
}
16+
17+
// Get Data Type for sqlite3 Dialect
18+
func (s *sqlite3) DataTypeOf(typ reflect.Value) string {
19+
switch typ.Kind() {
20+
case reflect.Bool:
21+
return "bool"
22+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
23+
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
24+
return "integer"
25+
case reflect.Int64, reflect.Uint64:
26+
return "bigint"
27+
case reflect.Float32, reflect.Float64:
28+
return "real"
29+
case reflect.String:
30+
return "text"
31+
case reflect.Array, reflect.Slice:
32+
return "blob"
33+
case reflect.Struct:
34+
if _, ok := typ.Interface().(time.Time); ok {
35+
return "datetime"
36+
}
37+
}
38+
panic(fmt.Sprintf("invalid sql type %s (%s)", typ.Type().Name(), typ.Kind()))
39+
}
40+
41+
// TableExistSQL returns SQL that judge whether the table exists in database
42+
func (s *sqlite3) TableExistSQL(tableName string) (string, []interface{}) {
43+
args := []interface{}{tableName}
44+
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
45+
}

0 commit comments

Comments
 (0)