Susumu Yata
null+****@clear*****
Tue Dec 22 14:07:35 JST 2015
Susumu Yata 2015-12-22 14:07:35 +0900 (Tue, 22 Dec 2015) New Revision: 39eaac215d7fac8a49074a4e262069e6cde8b30c https://github.com/groonga/grngo/commit/39eaac215d7fac8a49074a4e262069e6cde8b30c Message: Add Load() that is experimental. Modified files: grngo.go grngo_test.go Modified: grngo.go (+199 -0) =================================================================== --- grngo.go 2015-12-05 14:17:19 +0900 (43a390c) +++ grngo.go 2015-12-22 14:07:35 +0900 (a99e9c3) @@ -6,6 +6,7 @@ package grngo import "C" import ( + "bytes" "fmt" "reflect" "strings" @@ -682,6 +683,25 @@ func (db *DB) FindTable(name string) (*Table, error) { return table, nil } +type LoadOptions struct { + IfExists string +} + +// NewLoadOptions returns a new LoadOptions with the default settings. +func NewLoadOptions() *LoadOptions { + options := new(LoadOptions) + return options +} + +// (Experimental) Load loads values. +func (db *DB) Load(tableName string, values interface{}, options *LoadOptions) ([]byte, error) { + table, err := db.FindTable(tableName) + if err != nil { + return nil, err + } + return table.Load(values, options) +} + // InsertRow finds or inserts a row. func (db *DB) InsertRow(tableName string, key interface{}) (inserted bool, id uint32, err error) { table, err := db.FindTable(tableName) @@ -760,6 +780,185 @@ func newTable(db *DB, c *C.grngo_table, name string) *Table { return &table } +// genLoadHead generates the head line of a load command. +func (table *Table) genLoadHead(options *LoadOptions) (string, error) { + line := fmt.Sprintf("load --table %s", table.name) + if options.IfExists != "" { + value := strings.Replace(options.IfExists, "\\", "\\\\", -1) + value = strings.Replace(value, "\"", "\\\"", -1) + line += fmt.Sprintf(" --ifexists '%s'", value) + } + return line, nil +} + +// writeLoadColumns writes columns of a load command. +func (table *Table) writeLoadColumns(buf *bytes.Buffer, valueType reflect.Type) error { + if err := buf.WriteByte('['); err != nil { + return err + } + needsDelimiter := false + for i := 0; i < valueType.NumField(); i++ { + field := valueType.Field(i) + columnName := field.Tag.Get("grngo") + if columnName == "" { + continue + } + if needsDelimiter { + if err := buf.WriteByte(','); err != nil { + return err + } + } else { + needsDelimiter = true + } + if _, err := fmt.Fprintf(buf, "\"%s\"", columnName); err != nil { + return err + } + } + if err := buf.WriteByte(']'); err != nil { + return err + } + return nil +} + +// writeLoadValue writes a value of a load command. +func (table *Table) writeLoadValue(buf *bytes.Buffer, value *reflect.Value) error { + if err := buf.WriteByte('['); err != nil { + return err + } + valueType := value.Type() + needsDelimiter := false + for i := 0; i < valueType.NumField(); i++ { + field := valueType.Field(i) + tag := field.Tag.Get("grngo") + if tag == "" { + continue + } + if needsDelimiter { + if err := buf.WriteByte(','); err != nil { + return err + } + } else { + needsDelimiter = true + } + fieldValue := value.Field(i) + switch fieldValue.Kind() { + case reflect.Bool: + if _, err := fmt.Fprint(buf, fieldValue.Bool()); err != nil { + return err + } + case reflect.Int64: + if _, err := fmt.Fprint(buf, fieldValue.Int()); err != nil { + return err + } + case reflect.Float64: + if _, err := fmt.Fprint(buf, fieldValue.Float()); err != nil { + return err + } + case reflect.String: + str := fieldValue.String() + str = strings.Replace(str, "\\", "\\\\", -1) + str = strings.Replace(str, "\"", "\\\"", -1) + if _, err := fmt.Fprintf(buf, "\"%s\"", str); err != nil { + return err + } + case reflect.Slice: + switch field.Type.Elem().Kind() { + case reflect.Uint8: + str := string(fieldValue.Bytes()) + str = strings.Replace(str, "\\", "\\\\", -1) + str = strings.Replace(str, "\"", "\\\"", -1) + if _, err := fmt.Fprintf(buf, "\"%s\"", str); err != nil { + return err + } + default: + // TODO: Support other types! + return fmt.Errorf("unsupported data kind") + } + default: + // TODO: Support other types! + return fmt.Errorf("unsupported data kind") + } + } + if err := buf.WriteByte(']'); err != nil { + return err + } + return nil +} + +// genLoadBody generates the body line of a load command. +func (table *Table) genLoadBody(values interface{}) (string, error) { + buf := new(bytes.Buffer) + if err := buf.WriteByte('['); err != nil { + return "", err + } + value := reflect.ValueOf(values) + switch value.Kind() { + case reflect.Struct: + case reflect.Ptr: + value := value.Elem() + if value.Kind() != reflect.Struct { + return "", fmt.Errorf("invalid values") + } + if err := table.writeLoadColumns(buf, value.Type()); err != nil { + return "", err + } + if err := table.writeLoadValue(buf, &value); err != nil { + return "", err + } + case reflect.Slice: + if value.Len() == 0 { + return "", fmt.Errorf("invalid values") + } + valueType := value.Type().Elem() + if valueType.Kind() != reflect.Struct { + return "", fmt.Errorf("invalid values") + } + if err := table.writeLoadColumns(buf, valueType); err != nil { + return "", err + } + for i := 0; i < value.Len(); i++ { + if err := buf.WriteByte(','); err != nil { + return "", err + } + v := value.Index(i) + if err := table.writeLoadValue(buf, &v); err != nil { + return "", err + } + } + } + if err := buf.WriteByte(']'); err != nil { + return "", err + } + return buf.String(), nil +} + +// (Experimental) Load loads values. +// +// Implicit conversion for Time is not supported. +// GeoPoint is not supported. +// Vector types are not supported. +func (table *Table) Load(values interface{}, options *LoadOptions) ([]byte, error) { + if options == nil { + options = NewLoadOptions() + } + headLine, err := table.genLoadHead(options) + if err != nil { + return nil, err + } + bodyLine, err := table.genLoadBody(values) + if err != nil { + return nil, err + } + lines := []string{ headLine, bodyLine } + for _, line := range lines { + if err := table.db.Send(line); err != nil { + result, _ := table.db.Recv() + return result, err + } + } + return table.db.Recv() +} + // InsertRow finds or inserts a row. func (table *Table) InsertRow(key interface{}) (inserted bool, id uint32, err error) { var rc C.grn_rc Modified: grngo_test.go (+37 -0) =================================================================== --- grngo_test.go 2015-12-05 14:17:19 +0900 (6866100) +++ grngo_test.go 2015-12-22 14:07:35 +0900 (f5645d1) @@ -255,6 +255,43 @@ func removeTempDB(tb testing.TB, dirPath string, db *DB) { // Tests. +type Rec struct { + A bool `grngo:"A"` + B int64 `grngo:"B"` + C float64 `grngo:"C"` + D string `grngo:"D"` + E []byte `grngo:"E"` +} + +func TestLoad(t *testing.T) { + dirPath, _, db, _ := createTempTable(t, "Table", nil) + defer removeTempDB(t, dirPath, db) + if _, err := db.CreateColumn("Table", "A", "Bool", nil); err != nil { + t.Fatal("DB.CreateColumn() failed:", err) + } + if _, err := db.CreateColumn("Table", "B", "Int64", nil); err != nil { + t.Fatal("DB.CreateColumn() failed:", err) + } + if _, err := db.CreateColumn("Table", "C", "Float", nil); err != nil { + t.Fatal("DB.CreateColumn() failed:", err) + } + if _, err := db.CreateColumn("Table", "D", "Text", nil); err != nil { + t.Fatal("DB.CreateColumn() failed:", err) + } + if _, err := db.CreateColumn("Table", "E", "Text", nil); err != nil { + t.Fatal("DB.CreateColumn() failed:", err) + } + var recs []Rec + recs = append(recs, Rec{false, 100, 1.23, "ABC", []byte("123")}) + recs = append(recs, Rec{true, 200, 2.34, "BCD", []byte("456")}) + recs = append(recs, Rec{false, 300, 3.45, "C\"DE", []byte("789")}) + _, err := db.Load("Table", recs, nil) + if err != nil { + t.Fatalf("DB.Load() failed: %v", err) + } +// bytes, _ := db.Query("select Table") +} + func TestDB(t *testing.T) { dirPath, dbPath, db := createTempDB(t) defer os.RemoveAll(dirPath) -------------- next part -------------- HTML����������������������������... Download