Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type Config struct {
insecureSkipVerify=true
```


## 5.2 优雅的Debug
通过开启``debug``配置和命令行的``export EGO_DEBUG=true``,我们就可以在测试环境里看到请求里的配置名、地址、耗时、请求数据、响应数据
![image](./docs/images/ego_debug.png)
Expand Down Expand Up @@ -125,6 +126,7 @@ func testDB() error {
return err
}
```

## 6 GORM的日志
任何gorm的请求都会记录gorm的错误access日志,如果需要对gorm的日志做定制化处理,可参考以下使用方式。

Expand Down
55 changes: 55 additions & 0 deletions examples/sqlite/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"context"

"github.com/ego-component/egorm"
"github.com/gotomicro/ego"
"github.com/gotomicro/ego/core/elog"
)

// 运行方式:
// export EGO_DEBUG=true && go run main.go --config=config.toml
type KV struct {
ID int `gorm:"primaryKey" json:"id"`
K string `gorm:"uniqueIndex;not null" json:"k"`
V string `gorm:"not null" json:"v"`
}

func (KV) TableName() string { return "kv" }

var dbs []*egorm.Component

func main() {
if err := ego.New().Invoker(
openDB,
run,
).Run(); err != nil {
elog.Error("startup", elog.Any("err", err))
}
}

func openDB() error {
dbs = []*egorm.Component{
egorm.Load("sqlite.test").Build(),
egorm.Load("sqlite.share.memory").Build(),
egorm.Load("sqlite.nonshared.memory").Build(),
}
for _, db := range dbs {
if err := db.AutoMigrate(&KV{}); err != nil {
return err
}
}
return nil
}

func run() error {
ctx := context.Background()
for _, db := range dbs {
_ = db.WithContext(ctx).Create(&KV{K: "hello", V: "world"}).Error
var out KV
err := db.WithContext(ctx).Where("k = ?", "hello").First(&out).Error
elog.Info("kv", elog.String("k", out.K), elog.String("v", out.V), elog.FieldErr(err))
}
return nil
}
91 changes: 91 additions & 0 deletions internal/dsn/sqlite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package dsn

import (
"net/url"
"path/filepath"
"strings"

"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"

"github.com/ego-component/egorm/manager"
)

var (
_ manager.DSNParser = (*SqliteDSNParser)(nil)
)

type SqliteDSNParser struct {
}

func init() {
manager.Register(&SqliteDSNParser{})
}

func (p *SqliteDSNParser) Scheme() string {
return "sqlite"
}

func (p *SqliteDSNParser) NamingStrategy() schema.Namer {
return nil
}

func (p *SqliteDSNParser) GetDialector(dsn string) gorm.Dialector {
return sqlite.Open(dsn)
}

// ParseDSN supports typical gorm sqlite DSN strings:
// - file based: "test.db", "/abs/path/to/test.db", "file:test.db?cache=shared&_fk=1"
// - memory: ":memory:", "file::memory:?cache=shared"
func (p *SqliteDSNParser) ParseDSN(dsn string) (cfg *manager.DSN, err error) {
cfg = new(manager.DSN)
cfg.Params = map[string]string{}

// Normalize for parsing query parameters
raw := dsn
if strings.HasPrefix(raw, "file:") {
if idx := strings.IndexByte(raw, '?'); idx >= 0 {
query := raw[idx+1:]
for _, kv := range strings.Split(query, "&") {
parts := strings.SplitN(kv, "=", 2)
if len(parts) != 2 {
continue
}
val, decodeErr := url.QueryUnescape(parts[1])
if decodeErr != nil {
continue
}
cfg.Params[parts[0]] = val
}
}
}

// Determine DBName for logging/metrics only
switch {
// memory mode
case dsn == ":memory:":
case strings.Contains(dsn, "::memory:"):
cfg.DBName = "memory"
default:
trimmed := dsn
// strip "file:" prefix for name extraction
if strings.HasPrefix(trimmed, "file:") {
if idx := strings.IndexByte(trimmed, '?'); idx >= 0 {
trimmed = trimmed[:idx]
}
trimmed = strings.TrimPrefix(trimmed, "file:")
} else {
// if path like "dir/db.sqlite" keep base
if idx := strings.IndexByte(trimmed, '?'); idx >= 0 {
trimmed = trimmed[:idx]
}
}
base := filepath.Base(trimmed)
if base == "" || base == "." || base == "/" {
base = "sqlite"
}
cfg.DBName = base
}
return
}
90 changes: 90 additions & 0 deletions internal/dsn/sqlite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package dsn

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestSqliteDSNParser_ParseDSN(t *testing.T) {
tests := []struct {
name string
dsn string
expected *struct {
DBName string
Addr string
Params map[string]string
}
}{
{
name: "memory database",
dsn: ":memory:",
expected: &struct {
DBName string
Addr string
Params map[string]string
}{
DBName: "memory",
Addr: "",
Params: map[string]string{},
},
},
{
name: "file memory with cache",
dsn: "file::memory:?cache=shared",
expected: &struct {
DBName string
Addr string
Params map[string]string
}{
DBName: "memory",
Addr: "",
Params: map[string]string{
"cache": "shared",
},
},
},
{
name: "file based database",
dsn: "test.db",
expected: &struct {
DBName string
Addr string
Params map[string]string
}{
DBName: "test.db",
Addr: "",
Params: map[string]string{},
},
},
{
name: "file URI with parameters",
dsn: "file:./data/app.db?cache=shared&_journal_mode=WAL&_busy_timeout=5000&_fk=1",
expected: &struct {
DBName string
Addr string
Params map[string]string
}{
DBName: "app.db",
Addr: "",
Params: map[string]string{
"cache": "shared",
"_journal_mode": "WAL",
"_busy_timeout": "5000",
"_fk": "1",
},
},
},
}

dsnParser := SqliteDSNParser{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg, err := dsnParser.ParseDSN(tt.dsn)
assert.NoError(t, err)
assert.Equal(t, tt.expected.DBName, cfg.DBName)
assert.Equal(t, tt.expected.Addr, cfg.Addr)
assert.Equal(t, tt.expected.Params, cfg.Params)
})
}
}