Skip to content

Commit

Permalink
Revert "Revert "Merge pull request #4 from nelsam/foreign_keys""
Browse files Browse the repository at this point in the history
This reverts commit fc3ce5a.  Decided foreign key support shouldn't be in master just yet.  Should still be in foreign_keys branch.
  • Loading branch information
nelsam committed Sep 14, 2014
1 parent fc3ce5a commit 48fbc57
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 25 deletions.
11 changes: 6 additions & 5 deletions db_map.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package gorp_queries

import (
"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
"github.com/nelsam/gorp_queries/interfaces"
"github.com/nelsam/gorp_queries/query_plans"
)
Expand All @@ -11,7 +11,7 @@ type SqlExecutor interface {
Query(interface{}) interfaces.Query
}

// DbMap embeds "github.com/coopernurse/gorp".DbMap and adds query
// DbMap embeds "github.com/nelsam/gorp".DbMap and adds query
// methods to it.
type DbMap struct {
gorp.DbMap
Expand Down Expand Up @@ -55,7 +55,7 @@ func (m *DbMap) Query(target interface{}) interfaces.Query {
return query_plans.Query(gorpMap, gorpMap, target)
}

// Begin acts just like "github.com/coopernurse/gorp".DbMap.Begin,
// Begin acts just like "github.com/nelsam/gorp".DbMap.Begin,
// except that its return type is gorp_queries.Transaction.
func (m *DbMap) Begin() (*Transaction, error) {
t, err := m.DbMap.Begin()
Expand All @@ -65,7 +65,7 @@ func (m *DbMap) Begin() (*Transaction, error) {
return &Transaction{Transaction: *t, dbmap: m}, nil
}

// Transaction embeds "github.com/coopernurse/gorp".Transaction and
// Transaction embeds "github.com/nelsam/gorp".Transaction and
// adds query methods to it.
type Transaction struct {
gorp.Transaction
Expand All @@ -75,5 +75,6 @@ type Transaction struct {
// Query runs a query within a transaction. See DbMap.Query for full
// documentation.
func (t *Transaction) Query(target interface{}) interfaces.Query {
return query_plans.Query(&t.dbmap.DbMap, &t.Transaction, target)
gorpMap := &t.dbmap.DbMap
return query_plans.Query(gorpMap, &t.Transaction, target)
}
2 changes: 1 addition & 1 deletion db_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"reflect"
"testing"

"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
_ "github.com/mattn/go-sqlite3"
"github.com/nelsam/gorp_queries/interfaces"
"github.com/nelsam/gorp_queries/query_plans"
Expand Down
2 changes: 1 addition & 1 deletion dialects/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dialects

import (
"fmt"
"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
)

type MySQLDialect struct {
Expand Down
2 changes: 1 addition & 1 deletion dialects/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dialects

import (
"fmt"
"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
)

type SqliteDialect struct {
Expand Down
40 changes: 37 additions & 3 deletions extensions/postgres_query_plans.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
package extensions

import (
"github.com/coopernurse/gorp"
"github.com/nelsam/gorp_queries/query_plans"
"github.com/nelsam/gorp_queries/interfaces"
"bytes"

"github.com/nelsam/gorp"
"github.com/nelsam/gorp_queries/filters"
"github.com/nelsam/gorp_queries/interfaces"
"github.com/nelsam/gorp_queries/query_plans"
)

type pgJsonSelectWrapper struct {
actualValue interface{}
subElements []string
}

func (wrapper pgJsonSelectWrapper) ActualValue() interface{} {
return wrapper.actualValue
}

func (wrapper pgJsonSelectWrapper) WrapSql(sqlValue string) string {
buf := bytes.Buffer{}
buf.WriteString(sqlValue)
for idx, elem := range wrapper.subElements {
isLast := idx == len(wrapper.subElements)-1
buf.WriteString("::json->")
if isLast {
buf.WriteString(">")
}
buf.WriteString("'")
buf.WriteString(elem)
buf.WriteString("'")
}
return buf.String()
}

func PgJsonField(actualValue interface{}, subElements ...string) filters.SqlWrapper {
return pgJsonSelectWrapper{
actualValue: actualValue,
subElements: subElements,
}
}

type PostgresAssigner interface {
Assign(fieldPtr interface{}, value interface{}) PostgresAssignQuery
}
Expand Down
2 changes: 1 addition & 1 deletion filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"errors"
"reflect"

"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
)

type SqlWrapper interface {
Expand Down
70 changes: 70 additions & 0 deletions foreign_keys/foreign_key_converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package foreign_keys

import (
"reflect"

"github.com/nelsam/gorp"
)

// ForeignKeyConverter is a gorp.TypeConverter that converts
// foreign keys.
type ForeignKeyConverter struct {
SubConverter gorp.TypeConverter
}

// ToDb will attempt to typecast the passed in value to Keyer and
// return the value's key.
func (converter ForeignKeyConverter) ToDb(parent, value interface{}) (dbValue interface{}, err error) {
if converter.SubConverter != nil {
defer func() {
if err == nil {
dbValue, err = converter.SubConverter.ToDb(dbValue)
}
}()
}
// Use reflect to check if value is nil. This works around
// situations where value's type is not nil, but its value is.
refValue := reflect.ValueOf(value)
if (refValue.Kind() == reflect.Ptr || refValue.Kind() == reflect.Interface) && refValue.IsNil() {
return nil, nil
}
if keyer, ok := value.(Keyer); ok {
return keyer.Key(), nil
}
return value, nil
}

// FromDb takes a pointer to a field within a struct. If the field's
// Kind is reflect.Interface and its value is nil, FromDb will attempt
// to initialize it using the registered foreign key relationships.
func (converter ForeignKeyConverter) FromDb(parent, target interface{}) (scanner gorp.CustomScanner, convert bool) {
if converter.SubConverter != nil {
defer func() {
if convert == false {
scanner, convert = converter.SubConverter.FromDb(target)
}
}()
}

targetVal := reflect.ValueOf(target).Elem()
if targetVal.Kind() == reflect.Interface && targetVal.IsNil() {
if init, err := Lookup(parent, target); err == nil {
targetVal.Set(reflect.ValueOf(init))
convert = true

scanner.Target = init
holderPtrVal := reflect.New(reflect.TypeOf(init.Key()))
scanner.Holder = holderPtrVal.Interface()
scanner.Binder = func(interface{}, interface{}) error {
if holderPtrVal.IsNil() {
targetVal.Set(reflect.ValueOf(nil))
return nil
}
holderVal := holderPtrVal.Elem()
return init.SetKey(holderVal.Interface())
}
}
}

return
}
12 changes: 7 additions & 5 deletions foreign_keys/foreign_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func Register(parent, fieldPtrOrName interface{}, fkey Keyer) (registerErr error
func namesFor(parent, fieldPtrOrName interface{}) (parentPath, fieldName string, err error) {
parentVal := reflect.ValueOf(parent)
parentType := parentVal.Type()
for parentType.Kind() == reflect.Ptr {
if parentType.Kind() == reflect.Ptr {
parentType = parentType.Elem()
if parentVal.IsNil() {
parentVal.Set(reflect.New(parentType))
Expand All @@ -77,12 +77,14 @@ func namesFor(parent, fieldPtrOrName interface{}) (parentPath, fieldName string,
field := parentVal.Field(i)
fieldType := parentType.Field(i)
if fieldType.Anonymous {
if _, fieldName, err = namesFor(field.Interface(), fieldPtrOrName); err == nil {
if _, fieldName, err = namesFor(field.Addr().Interface(), fieldPtrOrName); err == nil {
return
}
} else {
if fieldType.PkgPath == "" && field.Addr().Interface() == fieldPtrOrName {
fieldName = fieldType.Name
return
}
} else if field.Addr().Interface() == fieldPtrOrName {
fieldName = fieldType.Name
return
}
}
return "", "", errors.New("The requested field pointer was not found on the parent value")
Expand Down
2 changes: 1 addition & 1 deletion query_plans/extensions.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package query_plans

import (
"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
"errors"
)

Expand Down
19 changes: 13 additions & 6 deletions query_plans/query_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"reflect"
"strings"

"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
"github.com/nelsam/gorp_queries/dialects"
"github.com/nelsam/gorp_queries/filters"
"github.com/nelsam/gorp_queries/interfaces"
Expand Down Expand Up @@ -175,12 +175,22 @@ func (plan *QueryPlan) mapTable(targetVal reflect.Value) (*gorp.TableMap, error)
// passing the address of a field that has been overridden is
// difficult to do accidentally.
func (plan *QueryPlan) mapColumns(table *gorp.TableMap, value reflect.Value) (err error) {
queryableFields, err := plan.mapColumnsCount(table, value)
if err != nil {
return err
}
if queryableFields == 0 {
return errors.New("No fields in the target struct are mappable.")
}
return nil
}

func (plan *QueryPlan) mapColumnsCount(table *gorp.TableMap, value reflect.Value) (queryableFields int, err error) {
value = value.Elem()
valueType := value.Type()
if plan.colMap == nil {
plan.colMap = make(structColumnMap, 0, value.NumField())
}
queryableFields := 0
quotedTableName := plan.dbMap.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)
for i := 0; i < value.NumField(); i++ {
fieldType := valueType.Field(i)
Expand All @@ -189,7 +199,7 @@ func (plan *QueryPlan) mapColumns(table *gorp.TableMap, value reflect.Value) (er
if fieldVal.Kind() != reflect.Ptr {
fieldVal = fieldVal.Addr()
}
plan.mapColumns(table, fieldVal)
queryableFields, err = plan.mapColumnsCount(table, fieldVal)
} else if fieldType.PkgPath == "" {
col := table.ColMap(fieldType.Name)
quotedCol := plan.dbMap.Dialect.QuoteField(col.ColumnName)
Expand All @@ -205,9 +215,6 @@ func (plan *QueryPlan) mapColumns(table *gorp.TableMap, value reflect.Value) (er
}
}
}
if queryableFields == 0 {
return errors.New("No fields in the target struct are mappable.")
}
return
}

Expand Down
14 changes: 13 additions & 1 deletion query_plans/query_plans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strings"
"testing"

"github.com/coopernurse/gorp"
"github.com/nelsam/gorp"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
Expand Down Expand Up @@ -35,6 +35,11 @@ type ValidStruct struct {
ExportedValue string
}

type ValidStructWithNoUniqueDbFields struct {
ValidStruct
AnotherTransientValue string `db:"-"`
}

type Invoice struct {
Id int64
Created int64
Expand Down Expand Up @@ -112,6 +117,7 @@ type DbTestSuite struct {
func (suite *DbTestSuite) SetupSuite() {
suite.Map.AddTable(InvalidStruct{})
suite.Map.AddTable(ValidStruct{})
suite.Map.AddTable(ValidStructWithNoUniqueDbFields{})
suite.Map.AddTable(OverriddenInvoice{}).SetKeys(false, "Id")
if err := suite.Map.CreateTablesIfNotExists(); !suite.NoError(err) {
suite.T().FailNow()
Expand Down Expand Up @@ -245,6 +251,12 @@ func (suite *QueryPlanTestSuite) TestQueryPlan_NonFieldPtr() {
"Assign(fieldPtr, value) should generate errors if fieldPtr is not a pointer to a field in ref")
}

func (suite *QueryPlanTestSuite) TestQueryPlan_AllDbFieldsEmbedded() {
q := suite.getQueryPlanFor(ValidStructWithNoUniqueDbFields{})
suite.Equal(0, len(q.Errors),
"Query(ref) should not generate errors if ref only has db fields in an embedded struct")
}

type QueryLanguageTestSuite struct {
DbTestSuite
Ref *OverriddenInvoice
Expand Down
1 change: 1 addition & 0 deletions targeted_wrappers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package gorp_queries

0 comments on commit 48fbc57

Please sign in to comment.