Skip to content

Commit f789138

Browse files
Merge pull request #19 from dipdup-io/feature/new-orm
Feature: add bun as ORM and test
2 parents e9794d1 + 29ec6a2 commit f789138

File tree

17 files changed

+1342
-364
lines changed

17 files changed

+1342
-364
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
steps:
99
- uses: actions/setup-go@v3
1010
with:
11-
go-version: '1.19'
11+
go-version: '1.20'
1212
- uses: actions/checkout@v3
1313
- name: golangci-lint
1414
uses: golangci/golangci-lint-action@v3
@@ -21,7 +21,7 @@ jobs:
2121
- name: install Go
2222
uses: actions/setup-go@v2
2323
with:
24-
go-version: 1.18.x
24+
go-version: 1.20.x
2525
- name: checkout code
2626
uses: actions/checkout@v2
2727
- uses: actions/cache@v2

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
lint:
2-
golangci-lint run --go=1.18
2+
golangci-lint run
33

44
test:
55
go test ./...

database/bun.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"fmt"
7+
"runtime"
8+
9+
"github.com/dipdup-net/go-lib/config"
10+
"github.com/pkg/errors"
11+
"github.com/uptrace/bun"
12+
"github.com/uptrace/bun/dialect/pgdialect"
13+
)
14+
15+
// Bun -
16+
type Bun struct {
17+
sqldb *sql.DB
18+
conn *bun.DB
19+
}
20+
21+
// NewBun -
22+
func NewBun() *Bun {
23+
return new(Bun)
24+
}
25+
26+
// DB -
27+
func (db *Bun) DB() *bun.DB {
28+
return db.conn
29+
}
30+
31+
// Connect -
32+
func (db *Bun) Connect(ctx context.Context, cfg config.Database) error {
33+
if cfg.Kind != config.DBKindPostgres {
34+
return errors.Wrap(ErrUnsupportedDatabaseType, cfg.Kind)
35+
}
36+
if cfg.Path != "" {
37+
conn, err := sql.Open("postgres", cfg.Path)
38+
if err != nil {
39+
return err
40+
}
41+
db.sqldb = conn
42+
db.conn = bun.NewDB(db.sqldb, pgdialect.New())
43+
} else {
44+
connStr := fmt.Sprintf(
45+
"postgres://%s:%s@%s:%d/%s?sslmode=disable",
46+
cfg.User,
47+
cfg.Password,
48+
cfg.Host,
49+
cfg.Port,
50+
cfg.Database,
51+
)
52+
conn, err := sql.Open("postgres", connStr)
53+
if err != nil {
54+
return err
55+
}
56+
db.sqldb = conn
57+
db.conn = bun.NewDB(db.sqldb, pgdialect.New())
58+
}
59+
maxOpenConns := 4 * runtime.GOMAXPROCS(0)
60+
db.sqldb.SetMaxOpenConns(maxOpenConns)
61+
db.sqldb.SetMaxIdleConns(maxOpenConns)
62+
return nil
63+
}
64+
65+
// Close -
66+
func (db *Bun) Close() error {
67+
if err := db.conn.Close(); err != nil {
68+
return err
69+
}
70+
return db.sqldb.Close()
71+
}
72+
73+
// Exec -
74+
func (db *Bun) Exec(ctx context.Context, query string, args ...any) (int64, error) {
75+
result, err := db.conn.ExecContext(ctx, query, args...)
76+
if err != nil {
77+
return 0, err
78+
}
79+
return result.RowsAffected()
80+
}
81+
82+
// Ping -
83+
func (db *Bun) Ping(ctx context.Context) error {
84+
if db.conn == nil {
85+
return ErrConnectionIsNotInitialized
86+
}
87+
return db.conn.PingContext(ctx)
88+
}
89+
90+
// State -
91+
func (db *Bun) State(ctx context.Context, indexName string) (*State, error) {
92+
var s State
93+
err := db.conn.NewSelect().Model(&s).Where("index_name = ?", indexName).Limit(1).Scan(ctx)
94+
return &s, err
95+
}
96+
97+
// CreateState -
98+
func (db *Bun) CreateState(ctx context.Context, s *State) error {
99+
_, err := db.conn.NewInsert().Model(s).Exec(ctx)
100+
return err
101+
}
102+
103+
// UpdateState -
104+
func (db *Bun) UpdateState(ctx context.Context, s *State) error {
105+
_, err := db.conn.NewUpdate().Model(s).Where("index_name = ?", s.IndexName).Exec(ctx)
106+
return err
107+
}
108+
109+
// DeleteState -
110+
func (db *Bun) DeleteState(ctx context.Context, s *State) error {
111+
_, err := db.conn.NewDelete().Model(s).Where("index_name = ?", s.IndexName).Exec(ctx)
112+
return err
113+
}
114+
115+
// MakeTableComment -
116+
func (db *Bun) MakeTableComment(ctx context.Context, name string, comment string) error {
117+
_, err := db.conn.ExecContext(ctx,
118+
`COMMENT ON TABLE ? IS ?`,
119+
bun.Ident(name),
120+
comment)
121+
122+
return err
123+
}
124+
125+
// MakeColumnComment -
126+
func (db *Bun) MakeColumnComment(ctx context.Context, tableName string, columnName string, comment string) error {
127+
_, err := db.conn.ExecContext(ctx,
128+
`COMMENT ON COLUMN ?.? IS ?`,
129+
bun.Ident(tableName),
130+
bun.Ident(columnName),
131+
comment)
132+
133+
return err
134+
}
135+
136+
// CreateTable -
137+
func (db *Bun) CreateTable(ctx context.Context, model any, opts ...CreateTableOption) error {
138+
if model == nil {
139+
return nil
140+
}
141+
var options CreateTableOptions
142+
for i := range opts {
143+
opts[i](&options)
144+
}
145+
146+
query := db.DB().
147+
NewCreateTable().
148+
Model(model)
149+
150+
if options.ifNotExists {
151+
query = query.IfNotExists()
152+
}
153+
154+
if options.partitionBy != "" {
155+
query = query.PartitionBy(options.partitionBy)
156+
}
157+
158+
if options.temporary {
159+
query = query.Temp()
160+
}
161+
162+
_, err := query.Exec(ctx)
163+
return err
164+
}

database/comment.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ package database
22

33
import (
44
"context"
5-
"github.com/dipdup-net/go-lib/hasura"
6-
"github.com/pkg/errors"
75
"reflect"
86
"strings"
7+
8+
"github.com/dipdup-net/go-lib/hasura"
9+
"github.com/pkg/errors"
910
)
1011

12+
const (
13+
fieldTableName = "tableName"
14+
fieldBaseModel = "BaseModel"
15+
)
16+
17+
// MakeComments -
1118
func MakeComments(ctx context.Context, sc SchemeCommenter, models ...interface{}) error {
12-
if models == nil {
19+
if len(models) == 0 {
1320
return nil
1421
}
1522

@@ -29,9 +36,9 @@ func MakeComments(ctx context.Context, sc SchemeCommenter, models ...interface{}
2936
for i := 0; i < modelType.NumField(); i++ {
3037
fieldType := modelType.Field(i)
3138

32-
if fieldType.Name == "tableName" {
39+
if fieldType.Name == fieldTableName || fieldType.Name == fieldBaseModel {
3340
var ok bool
34-
tableName, ok = getPgName(fieldType)
41+
tableName, ok = getDatabaseTagName(fieldType)
3542
if !ok {
3643
tableName = hasura.ToSnakeCase(modelType.Name())
3744
}
@@ -75,7 +82,7 @@ func makeEmbeddedComments(ctx context.Context, sc SchemeCommenter, tableName str
7582
continue
7683
}
7784

78-
if fieldType.Name == "tableName" {
85+
if fieldType.Name == fieldTableName {
7986
return errors.New("Embedded type must not have tableName field.")
8087
}
8188

@@ -93,7 +100,7 @@ func makeFieldComment(ctx context.Context, sc SchemeCommenter, tableName string,
93100
return nil
94101
}
95102

96-
columnName, ok := getPgName(fieldType)
103+
columnName, ok := getDatabaseTagName(fieldType)
97104

98105
if columnName == "-" {
99106
return nil
@@ -110,13 +117,26 @@ func makeFieldComment(ctx context.Context, sc SchemeCommenter, tableName string,
110117
return nil
111118
}
112119

113-
func getPgName(fieldType reflect.StructField) (name string, ok bool) {
114-
pgTag, ok := fieldType.Tag.Lookup("pg")
115-
if !ok {
120+
func getDatabaseTagName(fieldType reflect.StructField) (name string, ok bool) {
121+
pgTag, pgOk := fieldType.Tag.Lookup("pg")
122+
bunTag, bunOk := fieldType.Tag.Lookup("bun")
123+
ok = pgOk || bunOk
124+
125+
var tag string
126+
switch {
127+
case !pgOk && !bunOk:
116128
return "", false
129+
case pgOk && pgTag != "-":
130+
tag = pgTag
131+
case bunOk && bunTag != "-":
132+
tag = strings.TrimPrefix(bunTag, "table:")
133+
case pgOk:
134+
tag = pgTag
135+
case bunOk:
136+
tag = bunTag
117137
}
118138

119-
tags := strings.Split(pgTag, ",")
139+
tags := strings.Split(tag, ",")
120140

121141
if tags[0] != "" {
122142
name = tags[0]
@@ -128,7 +148,6 @@ func getPgName(fieldType reflect.StructField) (name string, ok bool) {
128148

129149
func getComment(fieldType reflect.StructField) (string, bool) {
130150
commentTag, ok := fieldType.Tag.Lookup("comment")
131-
132151
if ok {
133152
return commentTag, ok
134153
}

0 commit comments

Comments
 (0)