Skip to content

Commit

Permalink
[WIP] go-orm proof of concept example
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Stewart <[email protected]>
  • Loading branch information
paralin committed Apr 18, 2021
1 parent 984676c commit 07eff69
Show file tree
Hide file tree
Showing 6 changed files with 595 additions and 0 deletions.
176 changes: 176 additions & 0 deletions example/gorm/dialector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package main

import (
"database/sql"
"strconv"
"strings"

"gorm.io/gorm"
"gorm.io/gorm/callbacks"
"gorm.io/gorm/clause"
"gorm.io/gorm/logger"
"gorm.io/gorm/migrator"
"gorm.io/gorm/schema"
)

// Note: code adapted from gorm source: go-gorm/sqlite.

// Dialector implements the Dialector interface from gorm.
type Dialector struct {
db *sql.DB
}

// NewDialector constructs a new dialector from a sql store.
func NewDialector(db *sql.DB) gorm.Dialector {
return &Dialector{db: db}
}

// Name returns the name of the dialector.
func (d *Dialector) Name() string {
return "genjidb"
}

// Initialize initializes the dialector with a db.
func (d *Dialector) Initialize(db *gorm.DB) (err error) {
// register callbacks
callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{
LastInsertIDReversed: true,
})
for k, v := range d.ClauseBuilders() {
db.ClauseBuilders[k] = v
}
return nil
}

// ClauseBuilders returns the set of clause builders.
func (d *Dialector) ClauseBuilders() map[string]clause.ClauseBuilder {
return map[string]clause.ClauseBuilder{
"INSERT": func(c clause.Clause, builder clause.Builder) {
if insert, ok := c.Expression.(clause.Insert); ok {
if stmt, ok := builder.(*gorm.Statement); ok {
stmt.WriteString("INSERT ")
if insert.Modifier != "" {
stmt.WriteString(insert.Modifier)
stmt.WriteByte(' ')
}

stmt.WriteString("INTO ")
if insert.Table.Name == "" {
stmt.WriteQuoted(stmt.Table)
} else {
stmt.WriteQuoted(insert.Table)
}
return
}
}

c.Build(builder)
},
"LIMIT": func(c clause.Clause, builder clause.Builder) {
if limit, ok := c.Expression.(clause.Limit); ok {
if limit.Limit > 0 {
builder.WriteString("LIMIT ")
builder.WriteString(strconv.Itoa(limit.Limit))
}
if limit.Offset > 0 {
if limit.Limit > 0 {
builder.WriteString(" ")
}
builder.WriteString("OFFSET ")
builder.WriteString(strconv.Itoa(limit.Offset))
}
}
},
"FOR": func(c clause.Clause, builder clause.Builder) {
if _, ok := c.Expression.(clause.Locking); ok {
// SQLite3 does not support row-level locking.
return
}
c.Build(builder)
},
}
}

func (d *Dialector) DefaultValueOf(field *schema.Field) clause.Expression {
if field.AutoIncrement {
return clause.Expr{SQL: "NULL"}
}

// doesn't work, will raise error
return clause.Expr{SQL: "DEFAULT"}
}

func (d *Dialector) Migrator(db *gorm.DB) gorm.Migrator {
return Migrator{migrator.Migrator{Config: migrator.Config{
DB: db,
Dialector: d,
CreateIndexAfterCreateTable: true,
}}}
}

func (d *Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {
writer.WriteByte('?')
}

func (d *Dialector) QuoteTo(writer clause.Writer, str string) {
writer.WriteByte('`')
if strings.Contains(str, ".") {
for idx, str := range strings.Split(str, ".") {
if idx > 0 {
writer.WriteString(".`")
}
writer.WriteString(str)
writer.WriteByte('`')
}
} else {
writer.WriteString(str)
writer.WriteByte('`')
}
}

func (d *Dialector) Explain(sql string, vars ...interface{}) string {
return logger.ExplainSQL(sql, nil, `"`, vars...)
}

func (d *Dialector) DataTypeOf(field *schema.Field) string {
switch field.DataType {
case schema.Bool:
return "numeric"
case schema.Int, schema.Uint:
/*
if field.AutoIncrement && !field.PrimaryKey {
// https://www.sqlite.org/autoinc.html
return "integer PRIMARY KEY AUTOINCREMENT"
} else {
}
*/
return "integer"
case schema.Float:
return "real"
case schema.String:
return "text"
case schema.Time:
// GenjiDB does not (yet) support datetime
// return "datetime"
return "text"
case schema.Bytes:
return "blob"
}

return string(field.DataType)
}

func (d *Dialector) SavePoint(tx *gorm.DB, name string) error {
// tx.Exec("SAVEPOINT " + name)
// return nil
return gorm.ErrNotImplemented
}

func (d *Dialector) RollbackTo(tx *gorm.DB, name string) error {
// tx.Exec("ROLLBACK TO SAVEPOINT " + name)
// return nil
return gorm.ErrNotImplemented
}

// _ is a type assertion
var _ gorm.Dialector = ((*Dialector)(nil))
13 changes: 13 additions & 0 deletions example/gorm/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/genjidb/genji/example/gorm

go 1.15

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/genjidb/genji v0.12.0
github.com/kr/pretty v0.1.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gorm.io/gorm v1.21.7
)

replace github.com/genjidb/genji v0.12.0 => ../../
37 changes: 37 additions & 0 deletions example/gorm/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vmihailenco/msgpack/v5 v5.1.4 h1:6K44/cU6dMNGkVTGGuu7ef2NdSRFMhAFGGLfE3cqtHM=
github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI=
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.21.7 h1:MuY8oejVL5l3iT7PfE3z5I4J+KW/Nu2w/uTpLe3vV1Q=
gorm.io/gorm v1.21.7/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
68 changes: 68 additions & 0 deletions example/gorm/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"context"
"database/sql"
"errors"

"github.com/genjidb/genji"
"github.com/genjidb/genji/engine/memoryengine"
gdriver "github.com/genjidb/genji/sql/driver"
"gorm.io/gorm"
)

func run() error {
ctx := context.Background()

store := memoryengine.NewEngine()
gjdb, err := genji.New(ctx, store)
if err != nil {
return err
}
/*
driver, ok := gdriver.NewDriver(gjdb).(driver.DriverContext)
if !ok {
return gorm.ErrNotImplemented
}
conn, err := driver.OpenConnector("")
if err != nil {
return err
}
*/
conn := gdriver.NewConnector(gjdb)
sqlDB := sql.OpenDB(conn)
dialector := NewDialector(sqlDB)
conf := &gorm.Config{Dialector: dialector, ConnPool: sqlDB}
db, err := gorm.Open(dialector, conf)
if err != nil {
return err
}
if err := db.AutoMigrate(&Entry{}); err != nil {
return err
}
db.Create(&Entry{Value: 4, ID: 1})
db.Create(&Entry{Value: 10, ID: 2})
db.Create(&Entry{Value: 30, ID: 3})

var e Entry
out := db.Where("value = ?", 30).Find(&e)
if out.Error != nil {
return out.Error
}
if e.Value != 30 {
return errors.New("value was incorrect")
}
return nil
}

// Entry is an entry in the database.
type Entry struct {
ID int `gorm:"primaryKey"`
Value int `json:"value"`
}

func main() {
if err := run(); err != nil {
panic(err)
}
}
Loading

0 comments on commit 07eff69

Please sign in to comment.