good morning!!!!

Skip to content
Snippets Groups Projects
Commit 65ee6161 authored by Peter Kieltyka's avatar Peter Kieltyka
Browse files

WIP; wrapped option types

parent 14195f41
Branches
Tags
No related merge requests found
...@@ -64,3 +64,12 @@ CREATE TABLE composite_keys ( ...@@ -64,3 +64,12 @@ CREATE TABLE composite_keys (
some_val varchar(255) default '', some_val varchar(255) default '',
primary key (code, user_id) primary key (code, user_id)
); );
DROP TABLE IF EXISTS option_types;
CREATE TABLE option_types (
id serial primary key,
name varchar(255) default '',
tags varchar(64)[],
settings jsonb
);
...@@ -25,6 +25,7 @@ import ( ...@@ -25,6 +25,7 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"log"
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
...@@ -1651,6 +1652,69 @@ func TestDataTypes(t *testing.T) { ...@@ -1651,6 +1652,69 @@ func TestDataTypes(t *testing.T) {
} }
} }
func TestOptionTypes(t *testing.T) {
var err error
var sess db.Database
var optionTypes db.Collection
if sess, err = db.Open(Adapter, settings); err != nil {
t.Fatal(err)
}
defer sess.Close()
if optionTypes, err = sess.Collection("option_types"); err != nil {
t.Fatal(err)
}
if err = optionTypes.Truncate(); err != nil {
t.Fatal(err)
}
type optionType struct {
ID int64 `db:"id,omitempty"`
Name string `db:"name"`
Tags []string `db:"tags,stringarray"`
Settings map[string]interface{} `db:"settings,json"`
}
item1 := optionType{
Name: "Food",
Tags: []string{"toronto", "pizza"},
Settings: map[string]interface{}{"a": 1, "b": 2},
}
id, err := optionTypes.Append(item1)
if err != nil {
t.Fatal(err)
}
if pk, ok := id.(int64); !ok || pk == 0 {
t.Fatalf("Expecting an ID.")
}
// TODO: test this with pointer types, etc........
// and then... lets do some benchmarking on this ...
var item1Chk optionType
if err := optionTypes.Find(db.Cond{"id": id}).One(&item1Chk); err != nil {
t.Fatal(err)
}
log.Println("===>", item1Chk)
if item1Chk.Settings["a"].(float64) != 1 { // float64 because of json..
t.Fatalf("Expecting Settings['a'] of jsonb value to be 1")
}
// NOTE: sqltyp.StringArray is boched.. it's including the "," which it shouldn't
// no matter... we dont care for this here anyways...
if item1Chk.Tags[0] != "toronto," {
t.Fatalf("Expecting first element of Tags stringarray to be 'toronto'")
}
}
// We are going to benchmark the engine, so this is no longed needed. // We are going to benchmark the engine, so this is no longed needed.
func TestDisableDebug(t *testing.T) { func TestDisableDebug(t *testing.T) {
os.Setenv(db.EnvEnableDebug, "") os.Setenv(db.EnvEnableDebug, "")
......
...@@ -22,9 +22,10 @@ ...@@ -22,9 +22,10 @@
package sqlutil package sqlutil
import ( import (
"errors"
"reflect" "reflect"
"github.com/pressly/reeler/lib/sqltyp"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/jmoiron/sqlx/reflectx" "github.com/jmoiron/sqlx/reflectx"
"upper.io/db" "upper.io/db"
...@@ -150,16 +151,49 @@ func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect ...@@ -150,16 +151,49 @@ func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect
case reflect.Struct: case reflect.Struct:
values := make([]interface{}, len(columns)) values := make([]interface{}, len(columns))
fields := rows.Mapper.TraversalsByName(itemT, columns) typeMap := rows.Mapper.TypeMap(itemT)
fieldMap := typeMap.FieldMap()
wrappedValues := map[reflect.Value]interface{}{}
if err = fieldsByTraversal(item, fields, values, true); err != nil { for i, k := range columns {
return item, err fi, ok := fieldMap[k]
if !ok {
values[i] = new(interface{})
continue
}
f := reflectx.FieldByIndexes(item, fi.Index)
if _, ok := fi.Options["stringarray"]; ok {
values[i] = &sqltyp.StringArray{}
wrappedValues[f] = values[i]
} else if _, ok := fi.Options["json"]; ok {
values[i] = &JsonType{}
wrappedValues[f] = values[i]
} else {
values[i] = f.Addr().Interface()
}
if u, ok := values[i].(db.Unmarshaler); ok {
values[i] = scanner{u}
}
} }
if err = rows.Scan(values...); err != nil { if err = rows.Scan(values...); err != nil {
return item, err return item, err
} }
for f, v := range wrappedValues {
// log.Println("**", f, v)
switch t := v.(type) {
case *sqltyp.StringArray:
f.Set(reflect.ValueOf(*t))
case *JsonType:
f.Set(reflect.ValueOf((*t).V))
default:
}
}
case reflect.Map: case reflect.Map:
columns, err := rows.Columns() columns, err := rows.Columns()
...@@ -188,34 +222,3 @@ func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect ...@@ -188,34 +222,3 @@ func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect
return item, nil return item, nil
} }
func fieldsByTraversal(v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error {
v = reflect.Indirect(v)
if v.Kind() != reflect.Struct {
return errors.New("argument not a struct")
}
for i, traversal := range traversals {
if len(traversal) == 0 {
values[i] = new(interface{})
continue
}
f := reflectx.FieldByIndexes(v, traversal)
if ptrs {
values[i] = f.Addr().Interface()
} else {
values[i] = f.Interface()
}
// Provides compatibility with db.Unmarshaler
if u, ok := values[i].(db.Unmarshaler); ok {
values[i] = scanner{u}
}
}
return nil
}
...@@ -23,6 +23,10 @@ package sqlutil ...@@ -23,6 +23,10 @@ package sqlutil
import ( import (
"database/sql" "database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"upper.io/db" "upper.io/db"
) )
...@@ -35,3 +39,31 @@ func (u scanner) Scan(v interface{}) error { ...@@ -35,3 +39,31 @@ func (u scanner) Scan(v interface{}) error {
} }
var _ sql.Scanner = scanner{} var _ sql.Scanner = scanner{}
//------
type JsonType struct {
V interface{}
}
func (j *JsonType) Scan(src interface{}) error {
b, ok := src.([]byte)
if !ok {
return errors.New("Scan source was not []bytes")
}
v := JsonType{}
if err := json.Unmarshal(b, &v.V); err != nil {
return err
}
*j = v
return nil
}
func (j JsonType) Value() (driver.Value, error) {
b, err := json.Marshal(j.V)
if err != nil {
return nil, err
}
return b, nil
}
...@@ -23,14 +23,13 @@ package sqlutil ...@@ -23,14 +23,13 @@ package sqlutil
import ( import (
"database/sql" "database/sql"
"fmt"
"reflect" "reflect"
"regexp" "regexp"
"strings" "strings"
"github.com/jmoiron/sqlx/reflectx" "github.com/jmoiron/sqlx/reflectx"
"github.com/pressly/reeler/lib/sqltyp"
"menteslibres.net/gosexy/to"
"upper.io/db" "upper.io/db"
) )
...@@ -102,7 +101,18 @@ func (t *T) FieldValues(item interface{}) ([]string, []interface{}, error) { ...@@ -102,7 +101,18 @@ func (t *T) FieldValues(item interface{}) ([]string, []interface{}, error) {
fields = make([]string, 0, nfields) fields = make([]string, 0, nfields)
for _, fi := range fieldMap { for _, fi := range fieldMap {
value := reflectx.FieldByIndexesReadOnly(itemV, fi.Index).Interface() // log.Println("=>", fi.Name, fi.Options)
fld := reflectx.FieldByIndexesReadOnly(itemV, fi.Index)
var value interface{}
if _, ok := fi.Options["stringarray"]; ok {
value = sqltyp.StringArray(fld.Interface().([]string))
} else if _, ok := fi.Options["json"]; ok {
value = JsonType{fld.Interface()}
} else {
value = fld.Interface()
}
if _, ok := fi.Options["omitempty"]; ok { if _, ok := fi.Options["omitempty"]; ok {
if value == fi.Zero.Interface() { if value == fi.Zero.Interface() {
...@@ -128,7 +138,7 @@ func (t *T) FieldValues(item interface{}) ([]string, []interface{}, error) { ...@@ -128,7 +138,7 @@ func (t *T) FieldValues(item interface{}) ([]string, []interface{}, error) {
for i, keyV := range mkeys { for i, keyV := range mkeys {
valv := itemV.MapIndex(keyV) valv := itemV.MapIndex(keyV)
fields[i] = t.columnLike(to.String(keyV.Interface())) fields[i] = t.columnLike(fmt.Sprintf("%v", keyV.Interface()))
v, err := marshal(valv.Interface()) v, err := marshal(valv.Interface())
if err != nil { if err != nil {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment