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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ profile
example/example
cmd/templates.go
db/
install.sh
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## FYI
This is just a MySQL Patch for [eaigner/hood](https://github.com/eaigner/hood).Let us known if you have any questions.

- - -


If you are looking for something more lightweight and flexible, have a look at [jet](http://github.com/eaigner/jet)

For questions, suggestions and general topics visit the [group](https://groups.google.com/forum/#!forum/golang-hood).
Expand Down Expand Up @@ -44,11 +50,11 @@ You can find the documentation over at [GoDoc](http://godoc.org/github.com/eaign
If the dialect is registered, you can open the database directly using

hd, err := hood.Open("postgres", "user=<username> dbname=<database>")

or you can pass an existing database and dialect to `hood.New(*sql.DB, hood.Dialect)`

hd := hood.New(db, NewPostgres())

## Schemas

Schemas can be declared using the following syntax (only for demonstration purposes, would not produce valid SQL since it has 2 primary keys)
Expand Down Expand Up @@ -112,6 +118,7 @@ To use migrations, you first have to install the `hood` tool. To do that run the

go get github.com/eaigner/hood
cd $GOPATH/src/github.com/eaigner/hood
cp install.sample.sh install.sh
./install.sh

Assuming you have your `$GOPATH/bin` directory in your `PATH`, you can now invoke the hood tool with `hood`.
Expand Down Expand Up @@ -206,7 +213,7 @@ Besides the `sql:` struct tag, you can specify a `validate:` tag for model valid

- `presence` validates that a field is set
- `len(min:max)` validates that a `string` field’s length lies within the specified range
- `len(min:)` validates that it has the specified min length,
- `len(min:)` validates that it has the specified min length,
- `len(:max)` or max length
- `range(min:max)` validates that an `int` value lies in the specific range
- `range(min:)` validates that it has the specified min value,
Expand Down
18 changes: 9 additions & 9 deletions base.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (d *base) QuerySql(hood *Hood) (string, []interface{}) {
return hood.substituteMarkers(strings.Join(query, " ")), args
}

func (d *base) Insert(hood *Hood, model *Model) (Id, error) {
func (d *base) Insert(hood *Hood, model *Model) (interface{}, error) {
sql, args := d.Dialect.InsertSql(model)
result, err := hood.Exec(sql, args...)
if err != nil {
Expand All @@ -181,7 +181,7 @@ func (d *base) Insert(hood *Hood, model *Model) (Id, error) {
if err != nil {
return -1, err
}
return Id(id), nil
return id, nil
}

func (d *base) InsertSql(model *Model) (string, []interface{}) {
Expand All @@ -200,13 +200,13 @@ func (d *base) InsertSql(model *Model) (string, []interface{}) {
return sql, values
}

func (d *base) Update(hood *Hood, model *Model) (Id, error) {
func (d *base) Update(hood *Hood, model *Model) (interface{}, error) {
sql, args := d.Dialect.UpdateSql(model)
_, err := hood.Exec(sql, args...)
if err != nil {
return -1, err
}
return model.Pk.Value.(Id), nil
return model.Pk.Value, nil
}

func (d *base) UpdateSql(model *Model) (string, []interface{}) {
Expand All @@ -227,10 +227,10 @@ func (d *base) UpdateSql(model *Model) (string, []interface{}) {
return sql, values
}

func (d *base) Delete(hood *Hood, model *Model) (Id, error) {
func (d *base) Delete(hood *Hood, model *Model) (interface{}, error) {
sql, args := d.Dialect.DeleteSql(model)
_, err := hood.Exec(sql, args...)
return args[0].(Id), err
return args[0], err
}

func (d *base) DeleteSql(model *Model) (string, []interface{}) {
Expand Down Expand Up @@ -410,12 +410,12 @@ func (d *base) CreateIndexSql(name, table string, unique bool, columns ...string
return strings.Join(a, " ")
}

func (d *base) DropIndex(hood *Hood, name string) error {
_, err := hood.Exec(d.Dialect.DropIndexSql(name))
func (d *base) DropIndex(hood *Hood, table_name string, name string) error {
_, err := hood.Exec(d.Dialect.DropIndexSql(table_name, name))
return err
}

func (d *base) DropIndexSql(name string) string {
func (d *base) DropIndexSql(table_name, name string) string {
return fmt.Sprintf("DROP INDEX %v", d.Dialect.Quote(name))
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/templates/_migration.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"github.com/eaigner/hood"
"github.com/xrangers/hood"
)

func (m *M) {{.Name}}_{{.Timestamp}}_Up(hd *hood.Hood) {
Expand All @@ -10,4 +10,4 @@ func (m *M) {{.Name}}_{{.Timestamp}}_Up(hd *hood.Hood) {

func (m *M) {{.Name}}_{{.Timestamp}}_Down(hd *hood.Hood) {
// TODO: implement
}
}
4 changes: 2 additions & 2 deletions cmd/templates/_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package main

import (
"flag"
"github.com/eaigner/hood"
"github.com/xrangers/hood"
"io/ioutil"
"log"
"os/exec"
Expand Down Expand Up @@ -56,7 +56,7 @@ func init() {
type M struct{}

type Migrations struct {
Id hood.Id
Id hood.Id `sql:"pk,autoincr"`
Current int
}

Expand Down
10 changes: 5 additions & 5 deletions dialect.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@ type Dialect interface {
QuerySql(hood *Hood) (sql string, args []interface{})

// Insert inserts the values in model and returns the inserted rows Id.
Insert(hood *Hood, model *Model) (Id, error)
Insert(hood *Hood, model *Model) (interface{}, error)

// InsertSql returns the sql for inserting the passed model.
InsertSql(model *Model) (sql string, args []interface{})

// Update updates the values in the specified model and returns the
// updated rows Id.
Update(hood *Hood, model *Model) (Id, error)
Update(hood *Hood, model *Model) (interface{}, error)

// UpdateSql returns the sql for updating the specified model.
UpdateSql(model *Model) (string, []interface{})

// Delete drops the row matching the primary key of model and returns the affected Id.
Delete(hood *Hood, model *Model) (Id, error)
Delete(hood *Hood, model *Model) (interface{}, error)

// DeleteSql returns the sql for deleting the row matching model's primary key.
DeleteSql(model *Model) (string, []interface{})
Expand Down Expand Up @@ -114,10 +114,10 @@ type Dialect interface {
CreateIndexSql(name, table string, unique bool, columns ...string) string

// DropIndex drops the index.
DropIndex(hood *Hood, name string) error
DropIndex(hood *Hood, table_name string, name string) error

// DropIndexSql returns the sql for dropping the index.
DropIndexSql(name string) string
DropIndexSql(table_name, name string) string

// KeywordNotNull returns the dialect specific keyword for 'NOT NULL'.
KeywordNotNull() string
Expand Down
2 changes: 1 addition & 1 deletion dialects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ func DoTestSaveDeleteAllAndHooks(t *testing.T, info dialectInfo) {
t.Fatal("wrong id", x)
}

hd.SaveAll(&models) // force update for hooks test
hd.SaveAll(&models) // force update for hooks test

_, err = hd.DeleteAll(&models)
if err != nil {
Expand Down
92 changes: 75 additions & 17 deletions hood.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ func (field *ModelField) NotNull() bool {
return ok
}

// NotNull tests if the field is declared as NOT NULL
func (field *ModelField) AutoIncr() bool {
_, ok := field.SqlTags["autoincr"]
return ok
}

// Default returns the default value for the field
func (field *ModelField) Default() string {
return field.SqlTags["default"]
Expand Down Expand Up @@ -569,7 +575,7 @@ L:
"package db",
"",
"import (",
"\t\"github.com/eaigner/hood\"",
"\t\"github.com/xrangers/hood\"",
}
if timeRequired {
head = append(head, "\t\"time\"")
Expand Down Expand Up @@ -759,6 +765,9 @@ func (hood *Hood) FindSql(out interface{}, query string, args ...interface{}) er

// Exec executes a raw sql query.
func (hood *Hood) Exec(query string, args ...interface{}) (sql.Result, error) {
if hood.dryRun {
return nil, nil
}
hood.mutex.Lock()
defer hood.mutex.Unlock()
defer hood.Reset()
Expand Down Expand Up @@ -862,11 +871,61 @@ func callModelMethod(f interface{}, methodName string, isPrefix bool) error {
return nil
}

// INSERT

func (hood *Hood) Insert(f interface{}) (interface{}, error) {
var (
id int64 = -1
err error
)
model, err := interfaceToModel(f)
if err != nil {
return id, err
}
err = model.Validate()
if err != nil {
return id, err
}
if model.Pk == nil {
panic("no primary key field")
}
err = callModelMethod(f, "BeforeInsert", false)
if err != nil {
return id, err
}
now := time.Now()
for _, f := range model.Fields {
switch f.Value.(type) {
case Created, Updated:
f.Value = now
}
}
ifd, err := hood.Dialect.Insert(hood, model)
id, _ = ifd.(int64)
if err == nil {
err = callModelMethod(f, "AfterInsert", false)
}

if id != -1 {
// update model id after save
structValue := reflect.Indirect(reflect.ValueOf(f))
for i := 0; i < structValue.NumField(); i++ {
field := structValue.Field(i)
switch field.Interface().(type) {
case Created:
field.Set(reflect.ValueOf(Created{now}))
}
}
}
return id, err
}

// Save performs an INSERT, or UPDATE if the passed structs Id is set.
func (hood *Hood) Save(f interface{}) (Id, error) {
func (hood *Hood) Save(f interface{}) (interface{}, error) {
var (
id Id = -1
id int64 = -1
err error
ifd interface{}
)
model, err := interfaceToModel(f)
if err != nil {
Expand Down Expand Up @@ -896,7 +955,7 @@ func (hood *Hood) Save(f interface{}) (Id, error) {
f.Value = now
}
}
id, err = hood.Dialect.Update(hood, model)
ifd, err = hood.Dialect.Update(hood, model)
if err == nil {
err = callModelMethod(f, "AfterUpdate", false)
}
Expand All @@ -911,22 +970,21 @@ func (hood *Hood) Save(f interface{}) (Id, error) {
f.Value = now
}
}
id, err = hood.Dialect.Insert(hood, model)
ifd, err = hood.Dialect.Insert(hood, model)
id, _ = ifd.(int64)
if err == nil {
err = callModelMethod(f, "AfterInsert", false)
}
}
if err == nil {
err = callModelMethod(f, "AfterSave", false)
}
if id != -1 {
if id != -1 || ifd != nil {
// update model id after save
structValue := reflect.Indirect(reflect.ValueOf(f))
for i := 0; i < structValue.NumField(); i++ {
field := structValue.Field(i)
switch field.Interface().(type) {
case Id:
field.SetInt(int64(id))
case Updated:
field.Set(reflect.ValueOf(Updated{now}))
case Created:
Expand All @@ -936,10 +994,10 @@ func (hood *Hood) Save(f interface{}) (Id, error) {
}
}
}
return id, err
return ifd, err
}

func (hood *Hood) doAll(f interface{}, doFunc func(f2 interface{}) (Id, error)) ([]Id, error) {
func (hood *Hood) doAll(f interface{}, doFunc func(f2 interface{}) (interface{}, error)) ([]interface{}, error) {
panicMsg := "expected pointer to struct slice *[]struct"
if reflect.TypeOf(f).Kind() != reflect.Ptr {
panic(panicMsg)
Expand All @@ -949,7 +1007,7 @@ func (hood *Hood) doAll(f interface{}, doFunc func(f2 interface{}) (Id, error))
}
sliceValue := reflect.ValueOf(f).Elem()
sliceLen := sliceValue.Len()
ids := make([]Id, 0, sliceLen)
ids := make([]interface{}, 0, sliceLen)
for i := 0; i < sliceLen; i++ {
id, err := doFunc(sliceValue.Index(i).Addr().Interface())
if err != nil {
Expand All @@ -961,14 +1019,14 @@ func (hood *Hood) doAll(f interface{}, doFunc func(f2 interface{}) (Id, error))
}

// SaveAll performs an INSERT or UPDATE on a slice of structs.
func (hood *Hood) SaveAll(f interface{}) ([]Id, error) {
return hood.doAll(f, func(f2 interface{}) (Id, error) {
func (hood *Hood) SaveAll(f interface{}) ([]interface{}, error) {
return hood.doAll(f, func(f2 interface{}) (interface{}, error) {
return hood.Save(f2)
})
}

// Delete deletes the row matching the specified structs Id.
func (hood *Hood) Delete(f interface{}) (Id, error) {
func (hood *Hood) Delete(f interface{}) (interface{}, error) {
model, err := interfaceToModel(f)
if err != nil {
return -1, err
Expand All @@ -988,8 +1046,8 @@ func (hood *Hood) Delete(f interface{}) (Id, error) {
}

// DeleteAll deletes the rows matching the specified struct slice Ids.
func (hood *Hood) DeleteAll(f interface{}) ([]Id, error) {
return hood.doAll(f, func(f2 interface{}) (Id, error) {
func (hood *Hood) DeleteAll(f interface{}) ([]interface{}, error) {
return hood.doAll(f, func(f2 interface{}) (interface{}, error) {
return hood.Delete(f2)
})
}
Expand Down Expand Up @@ -1241,7 +1299,7 @@ func (hood *Hood) DropIndex(table interface{}, name string) error {
if hood.dryRun {
return nil
}
return hood.Dialect.DropIndex(hood, name)
return hood.Dialect.DropIndex(hood, tn, name)
}

func (hood *Hood) substituteMarkers(query string) string {
Expand Down
3 changes: 3 additions & 0 deletions install.sample.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
go run cmd/gen/templates.go
go build -o $GOPATH/bin/hood github.com/xrangers/hood/cmd
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/sh
go run cmd/gen/templates.go
go build -o $GOPATH/bin/hood github.com/eaigner/hood/cmd
go build -o $GOPATH/bin/hood github.com/xrangers/hood/cmd
Loading