Skip to content

Commit bd7ec5d

Browse files
authored
Feature/sqlite ut (gogf#1882)
1 parent 9e6e800 commit bd7ec5d

File tree

6 files changed

+5877
-8
lines changed

6 files changed

+5877
-8
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ cbuild
1515
**/.DS_Store
1616
.test/
1717
cmd/gf/main
18+
go.work

contrib/drivers/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
4040
import _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
4141
```
4242
Note:
43-
- It does not support `Save/Replace` features.
43+
- It does not support `Save` features.
4444

4545
## PostgreSQL
4646
```

contrib/drivers/sqlite/sqlite.go

+79-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/gogf/gf/v2/errors/gerror"
2626
"github.com/gogf/gf/v2/os/gfile"
2727
"github.com/gogf/gf/v2/text/gstr"
28+
"github.com/gogf/gf/v2/util/gconv"
2829
)
2930

3031
// Driver is the driver for sqlite database.
@@ -35,6 +36,8 @@ type Driver struct {
3536
var (
3637
// tableFieldsMap caches the table information retrieved from database.
3738
tableFieldsMap = gmap.New(true)
39+
// Error
40+
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
3841
)
3942

4043
func init() {
@@ -147,10 +150,17 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
147150
}
148151
fields = make(map[string]*gdb.TableField)
149152
for i, m := range result {
153+
mKey := ""
154+
if m["pk"].Bool() {
155+
mKey = "pri"
156+
}
150157
fields[strings.ToLower(m["name"].String())] = &gdb.TableField{
151-
Index: i,
152-
Name: strings.ToLower(m["name"].String()),
153-
Type: strings.ToLower(m["type"].String()),
158+
Index: i,
159+
Name: strings.ToLower(m["name"].String()),
160+
Type: strings.ToLower(m["type"].String()),
161+
Key: mKey,
162+
Default: m["dflt_value"].Val(),
163+
Null: !m["notnull"].Bool(),
154164
}
155165
}
156166
return fields
@@ -166,11 +176,73 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
166176
func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) {
167177
switch option.InsertOption {
168178
case gdb.InsertOptionSave:
169-
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
170-
171-
case gdb.InsertOptionReplace:
172-
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by sqlite driver`)
179+
return nil, ErrorSave
180+
case gdb.InsertOptionIgnore, gdb.InsertOptionReplace:
181+
var (
182+
keys []string // Field names.
183+
values []string // Value holder string array, like: (?,?,?)
184+
params []interface{} // Values that will be committed to underlying database driver.
185+
onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement.
186+
)
187+
// Handle the field names and placeholders.
188+
for k := range list[0] {
189+
keys = append(keys, k)
190+
}
191+
// Prepare the batch result pointer.
192+
var (
193+
charL, charR = d.GetChars()
194+
batchResult = new(gdb.SqlResult)
195+
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
196+
operation = "INSERT OR IGNORE"
197+
)
173198

199+
if option.InsertOption == gdb.InsertOptionReplace {
200+
operation = "INSERT OR REPLACE"
201+
}
202+
var (
203+
listLength = len(list)
204+
valueHolder = make([]string, 0)
205+
)
206+
for i := 0; i < listLength; i++ {
207+
values = values[:0]
208+
// Note that the map type is unordered,
209+
// so it should use slice+key to retrieve the value.
210+
for _, k := range keys {
211+
if s, ok := list[i][k].(gdb.Raw); ok {
212+
values = append(values, gconv.String(s))
213+
} else {
214+
values = append(values, "?")
215+
params = append(params, list[i][k])
216+
}
217+
}
218+
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
219+
// Batch package checks: It meets the batch number, or it is the last element.
220+
if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
221+
var (
222+
stdSqlResult sql.Result
223+
affectedRows int64
224+
)
225+
stdSqlResult, err = d.DoExec(ctx, link, fmt.Sprintf(
226+
"%s INTO %s(%s) VALUES%s %s",
227+
operation, d.QuotePrefixTableName(table), keysStr,
228+
gstr.Join(valueHolder, ","),
229+
onDuplicateStr,
230+
), params...)
231+
if err != nil {
232+
return stdSqlResult, err
233+
}
234+
if affectedRows, err = stdSqlResult.RowsAffected(); err != nil {
235+
err = gerror.WrapCode(gcode.CodeDbOperationError, err, `sql.Result.RowsAffected failed`)
236+
return stdSqlResult, err
237+
} else {
238+
batchResult.Result = stdSqlResult
239+
batchResult.Affected += affectedRows
240+
}
241+
params = params[:0]
242+
valueHolder = valueHolder[:0]
243+
}
244+
}
245+
return batchResult, nil
174246
default:
175247
return d.Core.DoInsert(ctx, link, table, list, option)
176248
}
+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2+
//
3+
// This Source Code Form is subject to the terms of the MIT License.
4+
// If a copy of the MIT was not distributed with this file,
5+
// You can obtain one at https://github.com/gogf/gf.
6+
7+
package sqlite_test
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/gogf/gf/v2/container/garray"
13+
"github.com/gogf/gf/v2/database/gdb"
14+
"github.com/gogf/gf/v2/errors/gcode"
15+
"github.com/gogf/gf/v2/errors/gerror"
16+
"github.com/gogf/gf/v2/frame/g"
17+
"github.com/gogf/gf/v2/os/gctx"
18+
"github.com/gogf/gf/v2/os/gfile"
19+
"github.com/gogf/gf/v2/os/gtime"
20+
"github.com/gogf/gf/v2/test/gtest"
21+
)
22+
23+
var (
24+
db gdb.DB
25+
dbPrefix gdb.DB
26+
dbInvalid gdb.DB
27+
configNode gdb.ConfigNode
28+
dbDir = gfile.Temp("sqlite")
29+
ctx = gctx.New()
30+
31+
// Error
32+
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
33+
)
34+
35+
const (
36+
TableSize = 10
37+
TableName = "user"
38+
TestSchema1 = "test1"
39+
TestSchema2 = "test2"
40+
TableNamePrefix = "gf_"
41+
CreateTime = "2018-10-24 10:00:00"
42+
DBGroupTest = "test"
43+
DBGroupPrefix = "prefix"
44+
DBGroupInvalid = "invalid"
45+
)
46+
47+
func init() {
48+
fmt.Println("init sqlite db start")
49+
50+
if err := gfile.Mkdir(dbDir); err != nil {
51+
gtest.Error(err)
52+
}
53+
54+
fmt.Println("init sqlite db dir: ", dbDir)
55+
56+
configNode = gdb.ConfigNode{
57+
Type: "sqlite",
58+
Link: gfile.Join(dbDir, "test.db"),
59+
Charset: "utf8",
60+
}
61+
nodePrefix := configNode
62+
nodePrefix.Prefix = TableNamePrefix
63+
64+
nodeInvalid := configNode
65+
66+
gdb.AddConfigNode(DBGroupTest, configNode)
67+
gdb.AddConfigNode(DBGroupPrefix, nodePrefix)
68+
gdb.AddConfigNode(DBGroupInvalid, nodeInvalid)
69+
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
70+
71+
// Default db.
72+
if r, err := gdb.NewByGroup(); err != nil {
73+
gtest.Error(err)
74+
} else {
75+
db = r
76+
}
77+
78+
// Prefix db.
79+
if r, err := gdb.NewByGroup(DBGroupPrefix); err != nil {
80+
gtest.Error(err)
81+
} else {
82+
dbPrefix = r
83+
}
84+
85+
// Invalid db.
86+
if r, err := gdb.NewByGroup(DBGroupInvalid); err != nil {
87+
gtest.Error(err)
88+
} else {
89+
dbInvalid = r
90+
}
91+
92+
fmt.Println("init sqlite db finish")
93+
}
94+
95+
func createTable(table ...string) string {
96+
return createTableWithDb(db, table...)
97+
}
98+
99+
func createInitTable(table ...string) string {
100+
return createInitTableWithDb(db, table...)
101+
}
102+
103+
func dropTable(table string) {
104+
dropTableWithDb(db, table)
105+
}
106+
107+
func createTableWithDb(db gdb.DB, table ...string) (name string) {
108+
if len(table) > 0 {
109+
name = table[0]
110+
} else {
111+
name = fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano())
112+
}
113+
dropTableWithDb(db, name)
114+
115+
if _, err := db.Exec(ctx, fmt.Sprintf(`
116+
CREATE TABLE %s (
117+
id INTEGER PRIMARY KEY AUTOINCREMENT
118+
UNIQUE
119+
NOT NULL,
120+
passport VARCHAR(45) NOT NULL
121+
DEFAULT passport,
122+
password VARCHAR(128) NOT NULL
123+
DEFAULT password,
124+
nickname VARCHAR(45),
125+
create_time DATETIME
126+
);
127+
`, name,
128+
)); err != nil {
129+
gtest.Fatal(err)
130+
}
131+
132+
return
133+
}
134+
135+
func createInitTableWithDb(db gdb.DB, table ...string) (name string) {
136+
name = createTableWithDb(db, table...)
137+
array := garray.New(true)
138+
for i := 1; i <= TableSize; i++ {
139+
array.Append(g.Map{
140+
"id": i,
141+
"passport": fmt.Sprintf(`user_%d`, i),
142+
"password": fmt.Sprintf(`pass_%d`, i),
143+
"nickname": fmt.Sprintf(`name_%d`, i),
144+
"create_time": gtime.NewFromStr(CreateTime).String(),
145+
})
146+
}
147+
148+
result, err := db.Insert(ctx, name, array.Slice())
149+
gtest.AssertNil(err)
150+
151+
n, e := result.RowsAffected()
152+
gtest.Assert(e, nil)
153+
gtest.Assert(n, TableSize)
154+
return
155+
}
156+
157+
func dropTableWithDb(db gdb.DB, table string) {
158+
if _, err := db.Exec(ctx, fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
159+
gtest.Error(err)
160+
}
161+
}

0 commit comments

Comments
 (0)