susumu.yata
null+****@clear*****
Fri Jun 19 14:34:28 JST 2015
susumu.yata 2015-06-19 14:34:28 +0900 (Fri, 19 Jun 2015) New Revision: ba4fc4ff7efc4beb4018e9d9489a5dcee67c96f7 https://github.com/groonga/grngo/commit/ba4fc4ff7efc4beb4018e9d9489a5dcee67c96f7 Message: Copy files from groonga/grnxx/go3/gnx. * TODO * The package name must be changed to grngo. * gnx.go is not requried. * GetValues() should be removed. * The prefix "Grn" should be removed. Added files: gnx.go grn.go grn_cgo.c grn_cgo.h grn_test.go options.go Added: gnx.go (+87 -0) 100644 =================================================================== --- /dev/null +++ gnx.go 2015-06-19 14:34:28 +0900 (bc7b068) @@ -0,0 +1,87 @@ +package gnx + +/* +#cgo pkg-config: groonga +*/ +import "C" + +import ( + "fmt" + "math" +) + +// -- Data types -- + +type Bool uint8 +type Int int64 +type Float float64 +type GeoPoint struct{ Latitude, Longitude int32 } +type Text []byte + +//type BoolVector []Bool +//type IntVector []Int +//type FloatVector []Float +//type GeoPointVector []GeoPoint +//type TextVector []Text + +const ( + True = Bool(3) + False = Bool(0) +) + +func NullBool() Bool { return Bool(1) } +func NullInt() Int { return Int(math.MinInt64) } +func NullFloat() Float { return Float(math.NaN()) } +func NullGeoPoint() GeoPoint { return GeoPoint{math.MinInt32, math.MinInt32} } +func NullText() Text { return nil } + +//func NullBoolVector() BoolVector { return nil } +//func NullIntVector() IntVector { return nil } +//func NullFloatVector() FloatVector { return nil } +//func NullGeoPointVector() GeoPointVector { return nil } +//func NullTextVector() TextVector { return nil } + +type TypeID int + +const ( + VoidID = TypeID(iota) + BoolID + IntID + FloatID + GeoPointID + TextID +// BoolVectorID +// IntVectorID +// FloatVectorID +// GeoPointVectorID +// TextVectorID +) + +func (id TypeID) String() string { + switch id { + case VoidID: + return "Void" + case BoolID: + return "Bool" + case IntID: + return "Int" + case FloatID: + return "Float" + case GeoPointID: + return "GeoPoint" + case TextID: + return "Text" +// case BoolVectorID: +// return "BoolVector" +// case IntVectorID: +// return "IntVector" +// case FloatVectorID: +// return "FloatVector" +// case GeoPointVectorID: +// return "GeoPointVector" +// case TextVectorID: +// return "TextVector" + default: + return fmt.Sprintf("TypeID(%d)", id) + } +} Added: grn.go (+1219 -0) 100644 =================================================================== --- /dev/null +++ grn.go 2015-06-19 14:34:28 +0900 (06024c7) @@ -0,0 +1,1219 @@ +package gnx + +/* +#cgo pkg-config: groonga +#include "grn_cgo.h" +*/ +import "C" + +import ( + "fmt" + "reflect" + "strings" + "unsafe" +) + +// -- Groonga -- + +// grnInitCount is a counter for automatically initializing and finalizing +// Groonga. +var grnInitCount = 0 + +// DisableGrnInitCount() disables grnInitCount. +// This is useful if you want to manyally initialize and finalize Groonga. +func DisableGrnInitCount() { + grnInitCount = -1 +} + +// GrnInit() initializes Groonga if needed. +// grnInitCount is incremented and when it changes from 0 to 1, Groonga is +// initialized. +func GrnInit() error { + switch grnInitCount { + case -1: // Disabled. + return nil + case 0: + if rc := C.grn_init(); rc != C.GRN_SUCCESS { + return fmt.Errorf("grn_init() failed: rc = %d", rc) + } + } + grnInitCount++ + return nil +} + +// GrnFin() finalizes Groonga if needed. +// grnInitCount is decremented and when it changes from 1 to 0, Groonga is +// finalized. +func GrnFin() error { + switch grnInitCount { + case -1: // Disabled. + return nil + case 0: + return fmt.Errorf("Groonga is not initialized yet") + case 1: + if rc := C.grn_fin(); rc != C.GRN_SUCCESS { + return fmt.Errorf("grn_fin() failed: rc = %d", rc) + } + } + grnInitCount-- + return nil +} + +// openGrnCtx() allocates memory for grn_ctx and initializes it. +func openGrnCtx() (*C.grn_ctx, error) { + if err := GrnInit(); err != nil { + return nil, err + } + ctx := C.grn_ctx_open(0) + if ctx == nil { + GrnFin() + return nil, fmt.Errorf("grn_ctx_open() failed") + } + return ctx, nil +} + +// closeGrnCtx() finalizes grn_ctx and frees allocated memory. +func closeGrnCtx(ctx *C.grn_ctx) error { + rc := C.grn_ctx_close(ctx) + GrnFin() + if rc != C.GRN_SUCCESS { + return fmt.Errorf("grn_ctx_close() failed: rc = %d", rc) + } + return nil +} + +// -- GrnDB -- + +type GrnDB struct { + ctx *C.grn_ctx + obj *C.grn_obj + tables map[string]*GrnTable +} + +// newGrnDB() creates a new GrnDB object. +func newGrnDB(ctx *C.grn_ctx, obj *C.grn_obj) *GrnDB { + return &GrnDB{ctx, obj, make(map[string]*GrnTable)} +} + +// CreateGrnDB() creates a Groonga database and returns a handle to it. +// A temporary database is created if path is empty. +func CreateGrnDB(path string) (*GrnDB, error) { + ctx, err := openGrnCtx() + if err != nil { + return nil, err + } + var cPath *C.char + if path != "" { + cPath = C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + } + obj := C.grn_db_create(ctx, cPath, nil) + if obj == nil { + closeGrnCtx(ctx) + errMsg := C.GoString(&ctx.errbuf[0]) + return nil, fmt.Errorf("grn_db_create() failed: err = %s", errMsg) + } + return newGrnDB(ctx, obj), nil +} + +// OpenGrnDB() opens an existing Groonga database and returns a handle. +func OpenGrnDB(path string) (*GrnDB, error) { + ctx, err := openGrnCtx() + if err != nil { + return nil, err + } + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + obj := C.grn_db_open(ctx, cPath) + if obj == nil { + closeGrnCtx(ctx) + errMsg := C.GoString(&ctx.errbuf[0]) + return nil, fmt.Errorf("grn_db_open() failed: err = %s", errMsg) + } + return newGrnDB(ctx, obj), nil +} + +// Close() closes a handle. +func (db *GrnDB) Close() error { + rc := C.grn_obj_close(db.ctx, db.obj) + if rc != C.GRN_SUCCESS { + closeGrnCtx(db.ctx) + return fmt.Errorf("grn_obj_close() failed: rc = %d", rc) + } + return closeGrnCtx(db.ctx) +} + +// Send() sends a raw command. +// The given command must be well-formed. +func (db *GrnDB) Send(command string) error { + commandBytes := []byte(command) + var cCommand *C.char + if len(commandBytes) != 0 { + cCommand = (*C.char)(unsafe.Pointer(&commandBytes[0])) + } + rc := C.grn_ctx_send(db.ctx, cCommand, C.uint(len(commandBytes)), 0) + switch { + case rc != C.GRN_SUCCESS: + errMsg := C.GoString(&db.ctx.errbuf[0]) + return fmt.Errorf("grn_ctx_send() failed: rc = %d, err = %s", rc, errMsg) + case db.ctx.rc != C.GRN_SUCCESS: + errMsg := C.GoString(&db.ctx.errbuf[0]) + return fmt.Errorf("grn_ctx_send() failed: ctx.rc = %d, err = %s", + db.ctx.rc, errMsg) + } + return nil +} + +// SendEx() sends a command with separated options. +func (db *GrnDB) SendEx(name string, options map[string]string) error { + if name == "" { + return fmt.Errorf("invalid command: name = <%s>", name) + } + for _, r := range name { + if (r != '_') && (r < 'a') && (r > 'z') { + return fmt.Errorf("invalid command: name = <%s>", name) + } + } + commandParts := []string{name} + for key, value := range options { + if key == "" { + return fmt.Errorf("invalid option: key = <%s>", key) + } + for _, r := range key { + if (r != '_') && (r < 'a') && (r > 'z') { + return fmt.Errorf("invalid option: key = <%s>", key) + } + } + value = strings.Replace(value, "\\", "\\\\", -1) + value = strings.Replace(value, "'", "\\'", -1) + commandParts = append(commandParts, fmt.Sprintf("--%s '%s'", key, value)) + } + return db.Send(strings.Join(commandParts, " ")) +} + +// Recv() receives the result of commands sent by Send(). +func (db *GrnDB) Recv() ([]byte, error) { + var resultBuffer *C.char + var resultLength C.uint + var flags C.int + rc := C.grn_ctx_recv(db.ctx, &resultBuffer, &resultLength, &flags) + switch { + case rc != C.GRN_SUCCESS: + errMsg := C.GoString(&db.ctx.errbuf[0]) + return nil, fmt.Errorf( + "grn_ctx_recv() failed: rc = %d, err = %s", rc, errMsg) + case db.ctx.rc != C.GRN_SUCCESS: + errMsg := C.GoString(&db.ctx.errbuf[0]) + return nil, fmt.Errorf( + "grn_ctx_recv() failed: ctx.rc = %d, err = %s", db.ctx.rc, errMsg) + } + result := C.GoBytes(unsafe.Pointer(resultBuffer), C.int(resultLength)) + return result, nil +} + +// Query() sends a raw command and receive the result. +func (db *GrnDB) Query(command string) ([]byte, error) { + if err := db.Send(command); err != nil { + result, _ := db.Recv() + return result, err + } + return db.Recv() +} + +// QueryEx() sends a command with separated options and receives the result. +func (db *GrnDB) QueryEx(name string, options map[string]string) ( + []byte, error) { + if err := db.SendEx(name, options); err != nil { + result, _ := db.Recv() + return result, err + } + return db.Recv() +} + +// CreateTable() creates a table. +func (db *GrnDB) CreateTable(name string, options *TableOptions) (*GrnTable, error) { + if options == nil { + options = NewTableOptions() + } + optionsMap := make(map[string]string) + optionsMap["name"] = name + switch options.TableType { + case ArrayTable: + optionsMap["flags"] = "TABLE_NO_KEY" + case HashTable: + optionsMap["flags"] = "TABLE_HASH_KEY" + case PatTable: + optionsMap["flags"] = "TABLE_PAT_KEY" + case DatTable: + optionsMap["flags"] = "TABLE_DAT_KEY" + default: + return nil, fmt.Errorf("undefined table type: options = %+v", options) + } + if options.WithSIS { + optionsMap["flags"] += "|KEY_WITH_SIS" + } + if options.KeyType != "" { + switch options.KeyType { + case "Bool": + optionsMap["key_type"] = "Bool" + case "Int": + optionsMap["key_type"] = "Int64" + case "Float": + optionsMap["key_type"] = "Float" + case "GeoPoint": + optionsMap["key_type"] = "WGS84GeoPoint" + case "Text": + optionsMap["key_type"] = "ShortText" + default: + if _, err := db.FindTable(options.KeyType); err != nil { + return nil, fmt.Errorf("unsupported key type: options = %+v", options) + } + optionsMap["key_type"] = options.KeyType + } + } + if options.ValueType != "" { + switch options.ValueType { + case "Bool": + optionsMap["value_type"] = "Bool" + case "Int": + optionsMap["value_type"] = "Int64" + case "Float": + optionsMap["value_type"] = "Float" + case "GeoPoint": + optionsMap["value_type"] = "WGS84GeoPoint" + default: + if _, err := db.FindTable(options.ValueType); err != nil { + return nil, fmt.Errorf("unsupported value type: options = %+v", + options) + } + optionsMap["value_type"] = options.ValueType + } + } + if options.DefaultTokenizer != "" { + optionsMap["default_tokenizer"] = options.DefaultTokenizer + } + if options.Normalizer != "" { + optionsMap["normalizer"] = options.Normalizer + } + if len(options.TokenFilters) != 0 { + optionsMap["token_filters"] = strings.Join(options.TokenFilters, ",") + } + bytes, err := db.QueryEx("table_create", optionsMap) + if err != nil { + return nil, err + } + if string(bytes) != "true" { + return nil, fmt.Errorf("table_create failed: name = <%s>", name) + } + return db.FindTable(name) +} + +// FindTable() finds a table. +func (db *GrnDB) FindTable(name string) (*GrnTable, error) { + if table, ok := db.tables[name]; ok { + return table, nil + } + nameBytes := []byte(name) + var cName *C.char + if len(nameBytes) != 0 { + cName = (*C.char)(unsafe.Pointer(&nameBytes[0])) + } + obj := C.grn_cgo_find_table(db.ctx, cName, C.int(len(nameBytes))) + if obj == nil { + return nil, fmt.Errorf("table not found: name = <%s>", name) + } + var keyInfo C.grn_cgo_type_info + if ok := C.grn_cgo_table_get_key_info(db.ctx, obj, &keyInfo); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_table_get_key_info() failed: name = <%s>", + name) + } + // Check the key type. + var keyType TypeID + switch keyInfo.data_type { + case C.GRN_DB_VOID: + keyType = VoidID + case C.GRN_DB_BOOL: + keyType = BoolID + case C.GRN_DB_INT64: + keyType = IntID + case C.GRN_DB_FLOAT: + keyType = FloatID + case C.GRN_DB_WGS84_GEO_POINT: + keyType = GeoPointID + case C.GRN_DB_SHORT_TEXT: + keyType = TextID + default: + return nil, fmt.Errorf("unsupported key type: data_type = %d", + keyInfo.data_type) + } + // Find the destination table if the key is table reference. + var keyTable *GrnTable + if keyInfo.ref_table != nil { + if keyType == VoidID { + return nil, fmt.Errorf("reference to void: name = <%s>", name) + } + cKeyTableName := C.grn_cgo_table_get_name(db.ctx, keyInfo.ref_table) + if cKeyTableName == nil { + return nil, fmt.Errorf("grn_cgo_table_get_name() failed") + } + defer C.free(unsafe.Pointer(cKeyTableName)) + var err error + keyTable, err = db.FindTable(C.GoString(cKeyTableName)) + if err != nil { + return nil, err + } + } + var valueInfo C.grn_cgo_type_info + if ok := C.grn_cgo_table_get_value_info(db.ctx, obj, &valueInfo); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_table_get_value_info() failed: name = <%s>", + name) + } + // Check the value type. + var valueType TypeID + switch valueInfo.data_type { + case C.GRN_DB_VOID: + valueType = VoidID + case C.GRN_DB_BOOL: + valueType = BoolID + case C.GRN_DB_INT64: + valueType = IntID + case C.GRN_DB_FLOAT: + valueType = FloatID + case C.GRN_DB_WGS84_GEO_POINT: + valueType = GeoPointID + case C.GRN_DB_SHORT_TEXT: + valueType = TextID + default: + return nil, fmt.Errorf("unsupported value type: data_type = %d", + valueInfo.data_type) + } + // Find the destination table if the value is table reference. + var valueTable *GrnTable + if valueInfo.ref_table != nil { + if valueType == VoidID { + return nil, fmt.Errorf("reference to void: name = <%s>", name) + } + cValueTableName := C.grn_cgo_table_get_name(db.ctx, valueInfo.ref_table) + if cValueTableName == nil { + return nil, fmt.Errorf("grn_cgo_table_get_name() failed") + } + defer C.free(unsafe.Pointer(cValueTableName)) + var err error + valueTable, err = db.FindTable(C.GoString(cValueTableName)) + if err != nil { + return nil, err + } + } + table := newGrnTable(db, obj, name, keyType, keyTable, valueType, valueTable) + db.tables[name] = table + return table, nil +} + +// InsertRow() inserts a row. +func (db *GrnDB) InsertRow(tableName string, key interface{}) (bool, Int, error) { + table, err := db.FindTable(tableName) + if err != nil { + return false, NullInt(), err + } + return table.InsertRow(key) +} + +// CreateColumn() creates a column. +func (db *GrnDB) CreateColumn(tableName, columnName string, valueType string, + options *ColumnOptions) (*GrnColumn, error) { + table, err := db.FindTable(tableName) + if err != nil { + return nil, err + } + return table.CreateColumn(columnName, valueType, options) +} + +// FindColumn() finds a column. +func (db *GrnDB) FindColumn(tableName, columnName string) (*GrnColumn, error) { + table, err := db.FindTable(tableName) + if err != nil { + return nil, err + } + return table.FindColumn(columnName) +} + +// -- GrnTable -- + +type GrnTable struct { + db *GrnDB + obj *C.grn_obj + name string + keyType TypeID + keyTable *GrnTable + valueType TypeID + valueTable *GrnTable + columns map[string]*GrnColumn +} + +// newGrnTable() creates a new GrnTable object. +func newGrnTable(db *GrnDB, obj *C.grn_obj, name string, keyType TypeID, + keyTable *GrnTable, valueType TypeID, valueTable *GrnTable) *GrnTable { + var table GrnTable + table.db = db + table.obj = obj + table.name = name + table.keyType = keyType + table.keyTable = keyTable + table.valueType = valueType + table.valueTable = valueTable + table.columns = make(map[string]*GrnColumn) + return &table +} + +// insertVoid() inserts an empty row. +func (table *GrnTable) insertVoid() (bool, Int, error) { + if table.keyType != VoidID { + return false, NullInt(), fmt.Errorf("key type conflict") + } + rowInfo := C.grn_cgo_table_insert_void(table.db.ctx, table.obj) + if rowInfo.id == C.GRN_ID_NIL { + return false, NullInt(), fmt.Errorf("grn_cgo_table_insert_void() failed") + } + return rowInfo.inserted == C.GRN_TRUE, Int(rowInfo.id), nil +} + +// insertBool() inserts a row with Bool key. +func (table *GrnTable) insertBool(key Bool) (bool, Int, error) { + if table.keyType != BoolID { + return false, NullInt(), fmt.Errorf("key type conflict") + } + grnKey := C.grn_bool(C.GRN_FALSE) + if key == True { + grnKey = C.grn_bool(C.GRN_TRUE) + } + rowInfo := C.grn_cgo_table_insert_bool(table.db.ctx, table.obj, grnKey) + if rowInfo.id == C.GRN_ID_NIL { + return false, NullInt(), fmt.Errorf("grn_cgo_table_insert_bool() failed") + } + return rowInfo.inserted == C.GRN_TRUE, Int(rowInfo.id), nil +} + +// insertInt() inserts a row with Int key. +func (table *GrnTable) insertInt(key Int) (bool, Int, error) { + if table.keyType != IntID { + return false, NullInt(), fmt.Errorf("key type conflict") + } + grnKey := C.int64_t(key) + rowInfo := C.grn_cgo_table_insert_int(table.db.ctx, table.obj, grnKey) + if rowInfo.id == C.GRN_ID_NIL { + return false, NullInt(), fmt.Errorf("grn_cgo_table_insert_int() failed") + } + return rowInfo.inserted == C.GRN_TRUE, Int(rowInfo.id), nil +} + +// insertFloat() inserts a row with Float key. +func (table *GrnTable) insertFloat(key Float) (bool, Int, error) { + if table.keyType != FloatID { + return false, NullInt(), fmt.Errorf("key type conflict") + } + grnKey := C.double(key) + rowInfo := C.grn_cgo_table_insert_float(table.db.ctx, table.obj, grnKey) + if rowInfo.id == C.GRN_ID_NIL { + return false, NullInt(), fmt.Errorf("grn_cgo_table_insert_float() failed") + } + return rowInfo.inserted == C.GRN_TRUE, Int(rowInfo.id), nil +} + +// insertGeoPoint() inserts a row with GeoPoint key. +func (table *GrnTable) insertGeoPoint(key GeoPoint) (bool, Int, error) { + if table.keyType != GeoPointID { + return false, NullInt(), fmt.Errorf("key type conflict") + } + grnKey := C.grn_geo_point{C.int(key.Latitude), C.int(key.Longitude)} + rowInfo := C.grn_cgo_table_insert_geo_point(table.db.ctx, table.obj, grnKey) + if rowInfo.id == C.GRN_ID_NIL { + return false, NullInt(), fmt.Errorf("grn_cgo_table_insert_geo_point() failed") + } + return rowInfo.inserted == C.GRN_TRUE, Int(rowInfo.id), nil +} + +// insertText() inserts a row with Text key. +func (table *GrnTable) insertText(key Text) (bool, Int, error) { + if table.keyType != TextID { + return false, NullInt(), fmt.Errorf("key type conflict") + } + var grnKey C.grn_cgo_text + if len(key) != 0 { + grnKey.ptr = (*C.char)(unsafe.Pointer(&key[0])) + grnKey.size = C.size_t(len(key)) + } + rowInfo := C.grn_cgo_table_insert_text(table.db.ctx, table.obj, &grnKey) + if rowInfo.id == C.GRN_ID_NIL { + return false, NullInt(), fmt.Errorf("grn_cgo_table_insert_text() failed") + } + return rowInfo.inserted == C.GRN_TRUE, Int(rowInfo.id), nil +} + +// InsertRow() inserts a row. +// The first return value specifies whether a row is inserted or not. +// The second return value is the ID of the inserted or found row. +func (table *GrnTable) InsertRow(key interface{}) (bool, Int, error) { + switch value := key.(type) { + case nil: + return table.insertVoid() + case Bool: + return table.insertBool(value) + case Int: + return table.insertInt(value) + case Float: + return table.insertFloat(value) + case GeoPoint: + return table.insertGeoPoint(value) + case Text: + return table.insertText(value) + default: + return false, NullInt(), fmt.Errorf( + "unsupported key type: typeName = <%s>", reflect.TypeOf(key).Name()) + } +} + +// CreateColumn() creates a column. +func (table *GrnTable) CreateColumn(name string, valueType string, + options *ColumnOptions) (*GrnColumn, error) { + if options == nil { + options = NewColumnOptions() + } + optionsMap := make(map[string]string) + optionsMap["table"] = table.name + optionsMap["name"] = name + switch valueType { + case "Bool": + optionsMap["type"] = "Bool" + case "Int": + optionsMap["type"] = "Int64" + case "Float": + optionsMap["type"] = "Float" + case "GeoPoint": + optionsMap["type"] = "WGS84GeoPoint" + case "Text": + optionsMap["type"] = "LongText" + default: + if _, err := table.db.FindTable(valueType); err != nil { + return nil, fmt.Errorf("unsupported value type: valueType = %s", valueType) + } + optionsMap["type"] = valueType + } + switch options.ColumnType { + case ScalarColumn: + optionsMap["flags"] = "COLUMN_SCALAR" + case VectorColumn: + optionsMap["flags"] = "COLUMN_VECTOR" + case IndexColumn: + optionsMap["flags"] = "COLUMN_INDEX" + default: + return nil, fmt.Errorf("undefined column type: options = %+v", options) + } + switch options.CompressionType { + case NoCompression: + case ZlibCompression: + optionsMap["flags"] = "|COMPRESS_ZLIB" + case LzoCompression: + optionsMap["flags"] = "|COMRESS_LZO" + default: + return nil, fmt.Errorf("undefined compression type: options = %+v", options) + } + if options.WithSection { + optionsMap["flags"] += "|WITH_SECTION" + } + if options.WithWeight { + optionsMap["flags"] += "|WITH_WEIGHT" + } + if options.WithPosition { + optionsMap["flags"] += "|WITH_POSITION" + } + if options.Source != "" { + optionsMap["source"] = options.Source + } + bytes, err := table.db.QueryEx("column_create", optionsMap) + if err != nil { + return nil, err + } + if string(bytes) != "true" { + return nil, fmt.Errorf("column_create failed: name = <%s>", name) + } + return table.FindColumn(name) +} + +// findColumn() finds a column. +func (table *GrnTable) findColumn(name string) (*GrnColumn, error) { + if column, ok := table.columns[name]; ok { + return column, nil + } + nameBytes := []byte(name) + var cName *C.char + if len(nameBytes) != 0 { + cName = (*C.char)(unsafe.Pointer(&nameBytes[0])) + } + obj := C.grn_obj_column(table.db.ctx, table.obj, cName, C.uint(len(name))) + if obj == nil { + return nil, fmt.Errorf("grn_obj_column() failed: table = %+v, name = <%s>", table, name) + } + var valueType TypeID + var valueTable *GrnTable + var isVector bool + switch name { + case "_id": + valueType = IntID + case "_key": + valueType = table.keyType + valueTable = table.keyTable + case "_value": + valueType = table.valueType + valueTable = table.valueTable + default: + var valueInfo C.grn_cgo_type_info + if ok := C.grn_cgo_column_get_value_info(table.db.ctx, obj, &valueInfo); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_value_info() failed: name = <%s>", + name) + } + // Check the value type. + switch valueInfo.data_type { + case C.GRN_DB_BOOL: + valueType = BoolID + case C.GRN_DB_INT64: + valueType = IntID + case C.GRN_DB_FLOAT: + valueType = FloatID + case C.GRN_DB_WGS84_GEO_POINT: + valueType = GeoPointID + case C.GRN_DB_SHORT_TEXT, C.GRN_DB_LONG_TEXT: + valueType = TextID + default: + return nil, fmt.Errorf("unsupported value type: data_type = %d", + valueInfo.data_type) + } + isVector = valueInfo.dimension > 0 + // Find the destination table if the value is table reference. + if valueInfo.ref_table != nil { + if valueType == VoidID { + return nil, fmt.Errorf("reference to void: name = <%s>", name) + } + cValueTableName := C.grn_cgo_table_get_name(table.db.ctx, valueInfo.ref_table) + if cValueTableName == nil { + return nil, fmt.Errorf("grn_cgo_table_get_name() failed") + } + defer C.free(unsafe.Pointer(cValueTableName)) + var err error + valueTable, err = table.db.FindTable(C.GoString(cValueTableName)) + if err != nil { + return nil, err + } + } + } + column := newGrnColumn(table, obj, name, valueType, isVector, valueTable) + table.columns[name] = column + return column, nil +} + +// FindColumn() finds a column. +func (table *GrnTable) FindColumn(name string) (*GrnColumn, error) { + if column, ok := table.columns[name]; ok { + return column, nil + } + delimPos := strings.IndexByte(name, '.') + if delimPos == -1 { + return table.findColumn(name) + } + columnNames := strings.Split(name, ".") + column, err := table.findColumn(columnNames[0]) + if err != nil { + return nil, err + } + isVector := column.isVector + valueTable := column.valueTable + for _, columnName := range columnNames[1:] { + if column.valueTable == nil { + return nil, fmt.Errorf("not table reference: column.name = <%s>", column.name) + } + column, err = column.valueTable.findColumn(columnName) + if err != nil { + return nil, err + } + if column.isVector { + if isVector { + return nil, fmt.Errorf("vector of vector is not supported") + } + isVector = true + } + } + nameBytes := []byte(name) + var cName *C.char + if len(nameBytes) != 0 { + cName = (*C.char)(unsafe.Pointer(&nameBytes[0])) + } + obj := C.grn_obj_column(table.db.ctx, table.obj, cName, C.uint(len(name))) + if obj == nil { + return nil, fmt.Errorf("grn_obj_column() failed: name = <%s>", name) + } + column = newGrnColumn(table, obj, name, column.valueType, isVector, valueTable) + table.columns[name] = column + return column, nil +} + +// -- GrnColumn -- + +type GrnColumn struct { + table *GrnTable + obj *C.grn_obj + name string + valueType TypeID + isVector bool + valueTable *GrnTable +} + +// newGrnColumn() creates a new GrnColumn object. +func newGrnColumn(table *GrnTable, obj *C.grn_obj, name string, + valueType TypeID, isVector bool, valueTable *GrnTable) *GrnColumn { + var column GrnColumn + column.table = table + column.obj = obj + column.name = name + column.valueType = valueType + column.isVector = isVector + column.valueTable = valueTable + return &column +} + +// setBool() assigns a Bool value. +func (column *GrnColumn) setBool(id Int, value Bool) error { + if (column.valueType != BoolID) || column.isVector { + return fmt.Errorf("value type conflict") + } + var grnValue C.grn_bool = C.GRN_FALSE + if value == True { + grnValue = C.GRN_TRUE + } + if ok := C.grn_cgo_column_set_bool(column.table.db.ctx, column.obj, + C.grn_id(id), grnValue); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_bool() failed") + } + return nil +} + +// setInt() assigns an Int value. +func (column *GrnColumn) setInt(id Int, value Int) error { + if (column.valueType != IntID) || column.isVector { + return fmt.Errorf("value type conflict") + } + grnValue := C.int64_t(value) + if ok := C.grn_cgo_column_set_int(column.table.db.ctx, column.obj, + C.grn_id(id), grnValue); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_int() failed") + } + return nil +} + +// setFloat() assigns a Float value. +func (column *GrnColumn) setFloat(id Int, value Float) error { + if (column.valueType != FloatID) || column.isVector { + return fmt.Errorf("value type conflict") + } + grnValue := C.double(value) + if ok := C.grn_cgo_column_set_float(column.table.db.ctx, column.obj, + C.grn_id(id), grnValue); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_float() failed") + } + return nil +} + +// setGeoPoint() assigns a GeoPoint value. +func (column *GrnColumn) setGeoPoint(id Int, value GeoPoint) error { + if (column.valueType != GeoPointID) || column.isVector { + return fmt.Errorf("value type conflict") + } + grnValue := C.grn_geo_point{C.int(value.Latitude), C.int(value.Longitude)} + if ok := C.grn_cgo_column_set_geo_point(column.table.db.ctx, column.obj, + C.grn_id(id), grnValue); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_geo_point() failed") + } + return nil +} + +// setText() assigns a Text value. +func (column *GrnColumn) setText(id Int, value Text) error { + if (column.valueType != TextID) || column.isVector { + return fmt.Errorf("value type conflict") + } + var grnValue C.grn_cgo_text + if len(value) != 0 { + grnValue.ptr = (*C.char)(unsafe.Pointer(&value[0])) + grnValue.size = C.size_t(len(value)) + } + if ok := C.grn_cgo_column_set_text(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_text() failed") + } + return nil +} + +// setBoolVector() assigns a Bool vector. +func (column *GrnColumn) setBoolVector(id Int, value []Bool) error { + grnValue := make([]C.grn_bool, len(value)) + for i, v := range value { + if v == True { + grnValue[i] = C.GRN_TRUE + } + } + var grnVector C.grn_cgo_vector + if len(grnValue) != 0 { + grnVector.ptr = unsafe.Pointer(&grnValue[0]) + grnVector.size = C.size_t(len(grnValue)) + } + if ok := C.grn_cgo_column_set_bool_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_bool_vector() failed") + } + return nil +} + +// setIntVector() assigns an Int vector. +func (column *GrnColumn) setIntVector(id Int, value []Int) error { + var grnVector C.grn_cgo_vector + if len(value) != 0 { + grnVector.ptr = unsafe.Pointer(&value[0]) + grnVector.size = C.size_t(len(value)) + } + if ok := C.grn_cgo_column_set_int_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_int_vector() failed") + } + return nil +} + +// setFloatVector() assigns a Float vector. +func (column *GrnColumn) setFloatVector(id Int, value []Float) error { + var grnVector C.grn_cgo_vector + if len(value) != 0 { + grnVector.ptr = unsafe.Pointer(&value[0]) + grnVector.size = C.size_t(len(value)) + } + if ok := C.grn_cgo_column_set_float_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_float_vector() failed") + } + return nil +} + +// setGeoPointVector() assigns a GeoPoint vector. +func (column *GrnColumn) setGeoPointVector(id Int, value []GeoPoint) error { + var grnVector C.grn_cgo_vector + if len(value) != 0 { + grnVector.ptr = unsafe.Pointer(&value[0]) + grnVector.size = C.size_t(len(value)) + } + if ok := C.grn_cgo_column_set_geo_point_vector(column.table.db.ctx, + column.obj, C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_geo_point_vector() failed") + } + return nil +} + +// setTextVector() assigns a Text vector. +func (column *GrnColumn) setTextVector(id Int, value []Text) error { + grnValue := make([]C.grn_cgo_text, len(value)) + for i, v := range value { + if len(v) != 0 { + grnValue[i].ptr = (*C.char)(unsafe.Pointer(&v[0])) + grnValue[i].size = C.size_t(len(v)) + } + } + var grnVector C.grn_cgo_vector + if len(grnValue) != 0 { + grnVector.ptr = unsafe.Pointer(&grnValue[0]) + grnVector.size = C.size_t(len(grnValue)) + } + if ok := C.grn_cgo_column_set_text_vector(column.table.db.ctx, + column.obj, C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return fmt.Errorf("grn_cgo_column_set_text_vector() failed") + } + return nil +} + +// SetValue() assigns a value. +func (column *GrnColumn) SetValue(id Int, value interface{}) error { + switch v := value.(type) { + case Bool: + return column.setBool(id, v) + case Int: + return column.setInt(id, v) + case Float: + return column.setFloat(id, v) + case GeoPoint: + return column.setGeoPoint(id, v) + case Text: + return column.setText(id, v) + case []Bool: + return column.setBoolVector(id, v) + case []Int: + return column.setIntVector(id, v) + case []Float: + return column.setFloatVector(id, v) + case []GeoPoint: + return column.setGeoPointVector(id, v) + case []Text: + return column.setTextVector(id, v) + default: + return fmt.Errorf("unsupported value type: name = <%s>", + reflect.TypeOf(value).Name()) + } +} + +// getBool() gets a Bool value. +func (column *GrnColumn) getBool(id Int) (interface{}, error) { + var grnValue C.grn_bool + if ok := C.grn_cgo_column_get_bool(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_bool() failed") + } + if grnValue == C.GRN_TRUE { + return True, nil + } else { + return False, nil + } +} + +// getInt() gets an Int value. +func (column *GrnColumn) getInt(id Int) (interface{}, error) { + var grnValue C.int64_t + if ok := C.grn_cgo_column_get_int(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_int() failed") + } + return Int(grnValue), nil +} + +// getFloat() gets a Float value. +func (column *GrnColumn) getFloat(id Int) (interface{}, error) { + var grnValue C.double + if ok := C.grn_cgo_column_get_float(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_float() failed") + } + return Float(grnValue), nil +} + +// getGeoPoint() gets a GeoPoint value. +func (column *GrnColumn) getGeoPoint(id Int) (interface{}, error) { + var grnValue C.grn_geo_point + if ok := C.grn_cgo_column_get_geo_point(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_geo_point() failed") + } + return GeoPoint{int32(grnValue.latitude), int32(grnValue.longitude)}, nil +} + +// getText() gets a Text value. +func (column *GrnColumn) getText(id Int) (interface{}, error) { + var grnValue C.grn_cgo_text + if ok := C.grn_cgo_column_get_text(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_text() failed") + } + if grnValue.size == 0 { + return make(Text, 0), nil + } + value := make(Text, int(grnValue.size)) + grnValue.ptr = (*C.char)(unsafe.Pointer(&value[0])) + if ok := C.grn_cgo_column_get_text(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_text() failed") + } + return value, nil +} + +// getBoolVector() gets a BoolVector. +func (column *GrnColumn) getBoolVector(id Int) (interface{}, error) { + var grnVector C.grn_cgo_vector + if ok := C.grn_cgo_column_get_bool_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_bool_vector() failed") + } + if grnVector.size == 0 { + return make([]Bool, 0), nil + } + grnValue := make([]C.grn_bool, int(grnVector.size)) + grnVector.ptr = unsafe.Pointer(&grnValue[0]) + if ok := C.grn_cgo_column_get_bool_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_bool_vector() failed") + } + value := make([]Bool, int(grnVector.size)) + for i, v := range grnValue { + if v == C.GRN_TRUE { + value[i] = True + } + } + return value, nil +} + +// getIntVector() gets a IntVector. +func (column *GrnColumn) getIntVector(id Int) (interface{}, error) { + var grnValue C.grn_cgo_vector + if ok := C.grn_cgo_column_get_int_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_int_vector() failed") + } + if grnValue.size == 0 { + return make([]Int, 0), nil + } + value := make([]Int, int(grnValue.size)) + grnValue.ptr = unsafe.Pointer(&value[0]) + if ok := C.grn_cgo_column_get_int_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_int_vector() failed") + } + return value, nil +} + +// getFloatVector() gets a FloatVector. +func (column *GrnColumn) getFloatVector(id Int) (interface{}, error) { + var grnValue C.grn_cgo_vector + if ok := C.grn_cgo_column_get_float_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_float_vector() failed") + } + if grnValue.size == 0 { + return make([]Float, 0), nil + } + value := make([]Float, int(grnValue.size)) + grnValue.ptr = unsafe.Pointer(&value[0]) + if ok := C.grn_cgo_column_get_float_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_float_vector() failed") + } + return value, nil +} + +// getGeoPointVector() gets a GeoPointVector. +func (column *GrnColumn) getGeoPointVector(id Int) (interface{}, error) { + var grnValue C.grn_cgo_vector + if ok := C.grn_cgo_column_get_geo_point_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_geo_point_vector() failed") + } + if grnValue.size == 0 { + return make([]GeoPoint, 0), nil + } + value := make([]GeoPoint, int(grnValue.size)) + grnValue.ptr = unsafe.Pointer(&value[0]) + if ok := C.grn_cgo_column_get_geo_point_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnValue); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_geo_point_vector() failed") + } + return value, nil +} + +// getTextVector() gets a TextVector. +func (column *GrnColumn) getTextVector(id Int) (interface{}, error) { + var grnVector C.grn_cgo_vector + if ok := C.grn_cgo_column_get_text_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_text_vector() failed") + } + if grnVector.size == 0 { + return make([]Text, 0), nil + } + grnValues := make([]C.grn_cgo_text, int(grnVector.size)) + grnVector.ptr = unsafe.Pointer(&grnValues[0]) + if ok := C.grn_cgo_column_get_text_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_text_vector() failed") + } + value := make([]Text, int(grnVector.size)) + for i, grnValue := range grnValues { + if grnValue.size != 0 { + value[i] = make(Text, int(grnValue.size)) + grnValues[i].ptr = (*C.char)(unsafe.Pointer(&value[i][0])) + } + } + if ok := C.grn_cgo_column_get_text_vector(column.table.db.ctx, column.obj, + C.grn_id(id), &grnVector); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_text_vector() failed") + } + return value, nil +} + +// GetValue() gets a value. +// TODO: GetValue() should use allocated spaces for better performance. +func (column *GrnColumn) GetValue(id Int) (interface{}, error) { + if !column.isVector { + switch column.valueType { + case BoolID: + return column.getBool(id) + case IntID: + return column.getInt(id) + case FloatID: + return column.getFloat(id) + case GeoPointID: + return column.getGeoPoint(id) + case TextID: + return column.getText(id) + } + } else { + switch column.valueType { + case BoolID: + return column.getBoolVector(id) + case IntID: + return column.getIntVector(id) + case FloatID: + return column.getFloatVector(id) + case GeoPointID: + return column.getGeoPointVector(id) + case TextID: + return column.getTextVector(id) + } + } + return nil, fmt.Errorf("undefined value type: valueType = %d", column.valueType) +} + +func (column *GrnColumn) getBools(ids []Int) (interface{}, error) { + grnValues := make([]C.grn_bool, len(ids)) + if ok := C.grn_cgo_column_get_bools(column.table.db.ctx, column.obj, + C.size_t(len(ids)), (*C.int64_t)(unsafe.Pointer(&ids[0])), + &grnValues[0]); ok != C.GRN_TRUE { + return nil, fmt.Errorf("grn_cgo_column_get_bools() failed") + } + values := make([]Bool, len(ids)) + for i, _ := range values { + if grnValues[i] == C.GRN_TRUE { + values[i] = True + } + } + return values, nil +} + +func (column *GrnColumn) GetValues(ids []Int) (interface{}, error) { + if !column.isVector { + switch column.valueType { + case BoolID: + return column.getBools(ids) +// case IntID: +// return column.getInts(ids) +// case FloatID: +// return column.getFloats(ids) +// case GeoPointID: +// return column.getGeoPoints(ids) +// case TextID: +// return column.getTexts(ids) + } + } else { +// switch column.valueType { +// case BoolID: +// return column.getBoolVectors(ids) +// case IntID: +// return column.getIntVectors(ids) +// case FloatID: +// return column.getFloatVectors(ids) +// case GeoPointID: +// return column.getGeoPointVectors(ids) +// case TextID: +// return column.getTextVectors(ids) +// } + } + return nil, fmt.Errorf("undefined value type: valueType = %d", column.valueType) +} Added: grn_cgo.c (+483 -0) 100644 =================================================================== --- /dev/null +++ grn_cgo.c 2015-06-19 14:34:28 +0900 (89de52b) @@ -0,0 +1,483 @@ +#include "grn_cgo.h" + +#include <string.h> + +#define GRN_CGO_MAX_DATA_TYPE_ID GRN_DB_WGS84_GEO_POINT + +grn_obj *grn_cgo_find_table(grn_ctx *ctx, const char *name, int name_len) { + grn_obj *obj = grn_ctx_get(ctx, name, name_len); + if (!obj) { + return NULL; + } + switch (obj->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_NO_KEY: { + return obj; + } + default: { + // The object is not a table. + return NULL; + } + } +} + +// grn_cgo_init_type_info() initializes the members of type_info. +// The initialized type info specifies a valid Void type. +static void grn_cgo_init_type_info(grn_cgo_type_info *type_info) { + type_info->data_type = GRN_DB_VOID; + type_info->dimension = 0; + type_info->ref_table = NULL; +} + +grn_bool grn_cgo_table_get_key_info(grn_ctx *ctx, grn_obj *table, + grn_cgo_type_info *key_info) { + grn_cgo_init_type_info(key_info); + while (table) { + switch (table->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: { + if (table->header.domain <= GRN_CGO_MAX_DATA_TYPE_ID) { + key_info->data_type = table->header.domain; + return GRN_TRUE; + } + table = grn_ctx_at(ctx, table->header.domain); + if (!table) { + return GRN_FALSE; + } + if (!key_info->ref_table) { + key_info->ref_table = table; + } + break; + } + case GRN_TABLE_NO_KEY: { + // GRN_DB_VOID, if the table has no key. + return GRN_TRUE; + } + default: { + // The object is not a table. + return GRN_FALSE; + } + } + } + return GRN_FALSE; +} + +grn_bool grn_cgo_table_get_value_info(grn_ctx *ctx, grn_obj *table, + grn_cgo_type_info *value_info) { + grn_cgo_init_type_info(value_info); + if (!table) { + return GRN_FALSE; + } + switch (table->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_NO_KEY: { + grn_id range = grn_obj_get_range(ctx, table); + if (range <= GRN_CGO_MAX_DATA_TYPE_ID) { + value_info->data_type = range; + return GRN_TRUE; + } + value_info->ref_table = grn_ctx_at(ctx, range); + grn_cgo_type_info key_info; + if (!grn_cgo_table_get_key_info(ctx, value_info->ref_table, &key_info)) { + return GRN_FALSE; + } + value_info->data_type = key_info.data_type; + return GRN_TRUE; + } + default: { + // The object is not a table. + return GRN_FALSE; + } + } +} + +grn_bool grn_cgo_column_get_value_info(grn_ctx *ctx, grn_obj *column, + grn_cgo_type_info *value_info) { + grn_cgo_init_type_info(value_info); + if (!column) { + return GRN_FALSE; + } + switch (column->header.type) { + case GRN_COLUMN_FIX_SIZE: { + break; + } + case GRN_COLUMN_VAR_SIZE: { + grn_obj_flags type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK; + if (type == GRN_OBJ_COLUMN_VECTOR) { + ++value_info->dimension; + } + break; + } + default: { + // The object is not a data column. + return GRN_FALSE; + } + } + grn_id range = grn_obj_get_range(ctx, column); + if (range <= GRN_CGO_MAX_DATA_TYPE_ID) { + value_info->data_type = range; + return GRN_TRUE; + } + value_info->ref_table = grn_ctx_at(ctx, range); + grn_cgo_type_info key_info; + if (!grn_cgo_table_get_key_info(ctx, value_info->ref_table, &key_info)) { + return GRN_FALSE; + } + value_info->data_type = key_info.data_type; + return GRN_TRUE; +} + +char *grn_cgo_table_get_name(grn_ctx *ctx, grn_obj *table) { + if (!table) { + return NULL; + } + switch (table->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_NO_KEY: { + break; + } + default: { + // The object is not a table. + return NULL; + } + } + char buf[GRN_TABLE_MAX_KEY_SIZE]; + int len = grn_obj_name(ctx, table, buf, GRN_TABLE_MAX_KEY_SIZE); + if (len <= 0) { + return NULL; + } + char *table_name = (char *)malloc(len + 1); + if (!table_name) { + return NULL; + } + memcpy(table_name, buf, len); + table_name[len] = '\0'; + return table_name; +} + +// grn_cgo_table_insert_row() calls grn_table_add() and converts the result. +static grn_cgo_row_info grn_cgo_table_insert_row( + grn_ctx *ctx, grn_obj *table, const void *key_ptr, size_t key_size) { + grn_cgo_row_info row_info; + int inserted; + row_info.id = grn_table_add(ctx, table, key_ptr, key_size, &inserted); + row_info.inserted = inserted ? GRN_TRUE : GRN_FALSE; + return row_info; +} + +grn_cgo_row_info grn_cgo_table_insert_void(grn_ctx *ctx, grn_obj *table) { + return grn_cgo_table_insert_row(ctx, table, NULL, 0); +} + +grn_cgo_row_info grn_cgo_table_insert_bool(grn_ctx *ctx, grn_obj *table, + grn_bool key) { + return grn_cgo_table_insert_row(ctx, table, &key, sizeof(key)); +} + +grn_cgo_row_info grn_cgo_table_insert_int(grn_ctx *ctx, grn_obj *table, + int64_t key) { + return grn_cgo_table_insert_row(ctx, table, &key, sizeof(key)); +} + +grn_cgo_row_info grn_cgo_table_insert_float(grn_ctx *ctx, grn_obj *table, + double key) { + return grn_cgo_table_insert_row(ctx, table, &key, sizeof(key)); +} + +grn_cgo_row_info grn_cgo_table_insert_geo_point(grn_ctx *ctx, grn_obj *table, + grn_geo_point key) { + return grn_cgo_table_insert_row(ctx, table, &key, sizeof(key)); +} + +grn_cgo_row_info grn_cgo_table_insert_text(grn_ctx *ctx, grn_obj *table, + const grn_cgo_text *key) { + return grn_cgo_table_insert_row(ctx, table, key->ptr, key->size); +} + +grn_bool grn_cgo_column_set_bool(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_bool value) { + grn_obj obj; + GRN_BOOL_INIT(&obj, 0); + GRN_BOOL_SET(ctx, &obj, value); + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_int(grn_ctx *ctx, grn_obj *column, + grn_id id, int64_t value) { + grn_obj obj; + GRN_INT64_INIT(&obj, 0); + GRN_INT64_SET(ctx, &obj, value); + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_float(grn_ctx *ctx, grn_obj *column, + grn_id id, double value) { + grn_obj obj; + GRN_FLOAT_INIT(&obj, 0); + GRN_FLOAT_SET(ctx, &obj, value); + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_geo_point(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_geo_point value) { + grn_obj obj; + GRN_WGS84_GEO_POINT_INIT(&obj, 0); + GRN_GEO_POINT_SET(ctx, &obj, value.latitude, value.longitude); + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_text(grn_ctx *ctx, grn_obj *column, + grn_id id, const grn_cgo_text *value) { + grn_obj obj; + GRN_TEXT_INIT(&obj, 0); + if (value) { + GRN_TEXT_SET(ctx, &obj, value->ptr, value->size); + } else { + GRN_TEXT_SET(ctx, &obj, NULL, 0); + } + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_bool_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value) { + grn_obj obj; + GRN_BOOL_INIT(&obj, GRN_OBJ_VECTOR); + size_t i; + for (i = 0; i < value->size; i++) { + GRN_BOOL_SET_AT(ctx, &obj, i, ((const grn_bool *)value->ptr)[i]); + } + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_int_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value) { + grn_obj obj; + GRN_INT64_INIT(&obj, GRN_OBJ_VECTOR); + size_t i; + for (i = 0; i < value->size; i++) { + GRN_INT64_SET_AT(ctx, &obj, i, ((const int64_t *)value->ptr)[i]); + } + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_float_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value) { + grn_obj obj; + GRN_FLOAT_INIT(&obj, GRN_OBJ_VECTOR); + size_t i; + for (i = 0; i < value->size; i++) { + GRN_FLOAT_SET_AT(ctx, &obj, i, ((const double *)value->ptr)[i]); + } + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_geo_point_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value) { + grn_obj obj; + GRN_WGS84_GEO_POINT_INIT(&obj, GRN_OBJ_VECTOR); + size_t i; + const grn_geo_point *values = (const grn_geo_point *)value->ptr; + for (i = 0; i < value->size; i++) { + grn_bulk_write(ctx, &obj, (const char *)&values[i], sizeof(values[i])); + } + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_set_text_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value) { + grn_obj obj; + GRN_TEXT_INIT(&obj, GRN_OBJ_VECTOR); + size_t i; + const grn_cgo_text *values = (const grn_cgo_text *)value->ptr; + for (i = 0; i < value->size; i++) { + grn_vector_add_element(ctx, &obj, values[i].ptr, values[i].size, + 0, obj.header.domain); + } + grn_rc rc = grn_obj_set_value(ctx, column, id, &obj, GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &obj); + return rc == GRN_SUCCESS; +} + +grn_bool grn_cgo_column_get_bool(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_bool *value) { + grn_obj value_obj; + GRN_BOOL_INIT(&value_obj, 0); + grn_obj_get_value(ctx, column, id, &value_obj); + *value = GRN_BOOL_VALUE(&value_obj); + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_int(grn_ctx *ctx, grn_obj *column, + grn_id id, int64_t *value) { + grn_obj value_obj; + GRN_INT64_INIT(&value_obj, 0); + grn_obj_get_value(ctx, column, id, &value_obj); + *value = GRN_INT64_VALUE(&value_obj); + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_float(grn_ctx *ctx, grn_obj *column, + grn_id id, double *value) { + grn_obj value_obj; + GRN_FLOAT_INIT(&value_obj, 0); + grn_obj_get_value(ctx, column, id, &value_obj); + *value = GRN_FLOAT_VALUE(&value_obj); + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_geo_point(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_geo_point *value) { + grn_obj value_obj; + GRN_WGS84_GEO_POINT_INIT(&value_obj, 0); + grn_obj_get_value(ctx, column, id, &value_obj); + GRN_GEO_POINT_VALUE(&value_obj, value->latitude, value->longitude); + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_text(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_text *value) { + grn_obj value_obj; + GRN_TEXT_INIT(&value_obj, 0); + grn_obj_get_value(ctx, column, id, &value_obj); + size_t size = GRN_TEXT_LEN(&value_obj); + if (size <= value->size) { + memcpy(value->ptr, GRN_TEXT_VALUE(&value_obj), size); + } + value->size = size; + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_bool_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value) { + grn_obj value_obj; + GRN_BOOL_INIT(&value_obj, GRN_OBJ_VECTOR); + grn_obj_get_value(ctx, column, id, &value_obj); + size_t size_in_bytes = GRN_BULK_VSIZE(&value_obj); + size_t size = size_in_bytes / sizeof(grn_bool); + if (size <= value->size) { + memcpy(value->ptr, GRN_BULK_HEAD(&value_obj), size_in_bytes); + } + value->size = size; + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_int_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value) { + grn_obj value_obj; + GRN_INT64_INIT(&value_obj, GRN_OBJ_VECTOR); + grn_obj_get_value(ctx, column, id, &value_obj); + size_t size_in_bytes = GRN_BULK_VSIZE(&value_obj); + size_t size = size_in_bytes / sizeof(int64_t); + if (size <= value->size) { + memcpy(value->ptr, GRN_BULK_HEAD(&value_obj), size_in_bytes); + } + value->size = size; + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_float_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value) { + grn_obj value_obj; + GRN_FLOAT_INIT(&value_obj, GRN_OBJ_VECTOR); + grn_obj_get_value(ctx, column, id, &value_obj); + size_t size_in_bytes = GRN_BULK_VSIZE(&value_obj); + size_t size = size_in_bytes / sizeof(double); + if (size <= value->size) { + memcpy(value->ptr, GRN_BULK_HEAD(&value_obj), size_in_bytes); + } + value->size = size; + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_geo_point_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value) { + grn_obj value_obj; + GRN_WGS84_GEO_POINT_INIT(&value_obj, GRN_OBJ_VECTOR); + grn_obj_get_value(ctx, column, id, &value_obj); + size_t size_in_bytes = GRN_BULK_VSIZE(&value_obj); + size_t size = size_in_bytes / sizeof(grn_geo_point); + if (size <= value->size) { + memcpy(value->ptr, GRN_BULK_HEAD(&value_obj), size_in_bytes); + } + value->size = size; + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_text_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value) { + grn_obj value_obj; + GRN_TEXT_INIT(&value_obj, GRN_OBJ_VECTOR); + grn_obj_get_value(ctx, column, id, &value_obj); + size_t size = grn_vector_size(ctx, &value_obj); + if (size <= value->size) { + size_t i; + for (i = 0; i < size; i++) { + // NOTE: grn_vector_get_element() assigns the address of the text body + // to text_ptr, but the body may be overwritten in the next call. + const char *text_ptr; + unsigned int text_size = grn_vector_get_element(ctx, &value_obj, i, + &text_ptr, NULL, NULL); + grn_cgo_text *text = &((grn_cgo_text *)value->ptr)[i]; + if (text_size <= text->size) { + memcpy(text->ptr, text_ptr, text_size); + } + text->size = text_size; + } + } + value->size = size; + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} + +grn_bool grn_cgo_column_get_bools(grn_ctx *ctx, grn_obj *column, size_t n, + const int64_t *ids, grn_bool *values) { + grn_obj value_obj; + GRN_BOOL_INIT(&value_obj, 0); + size_t i; + for (i = 0; i < n; i++) { + GRN_BULK_REWIND(&value_obj); + grn_obj_get_value(ctx, column, (grn_id)ids[i], &value_obj); + values[i] = GRN_BOOL_VALUE(&value_obj); + } + GRN_OBJ_FIN(ctx, &value_obj); + return GRN_TRUE; +} Added: grn_cgo.h (+148 -0) 100644 =================================================================== --- /dev/null +++ grn_cgo.h 2015-06-19 14:34:28 +0900 (53bb968) @@ -0,0 +1,148 @@ +#ifndef GRN_CGO_H +#define GRN_CGO_H + +#include <stdint.h> +#include <stdlib.h> + +#include <groonga.h> + +typedef struct { + char *ptr; + size_t size; +} grn_cgo_text; + +typedef struct { + void *ptr; + size_t size; +} grn_cgo_vector; + +// grn_cgo_find_table() finds a table with the given name. +// If found, an object associated with the table is returned. +// If not found, NULL is returned. +grn_obj *grn_cgo_find_table(grn_ctx *ctx, const char *name, int name_len); + +typedef struct { + grn_id data_type; // Data type (GRN_DB_VOID, GRN_DB_BOOL, etc.). + // If the type is table reference, the key type of the + // referenced table is stored. + int dimension; // Vector depth, 0 means the type is scalar. + grn_obj *ref_table; // The referenced table of table reference. +} grn_cgo_type_info; + +// grn_cgo_table_get_key_info() gets information of the table key. +grn_bool grn_cgo_table_get_key_info(grn_ctx *ctx, grn_obj *table, + grn_cgo_type_info *key_info); +// grn_cgo_table_get_value_info() gets information of the table value. +grn_bool grn_cgo_table_get_value_info(grn_ctx *ctx, grn_obj *table, + grn_cgo_type_info *value_info); +// grn_cgo_column_get_value_info() gets information of the column value. +grn_bool grn_cgo_column_get_value_info(grn_ctx *ctx, grn_obj *column, + grn_cgo_type_info *value_info); + +// grn_cgo_table_get_name() returns the name of table. +// On success, a non-NULL pointer is returned and it must be freed by free(). +// On failure, NULL is returned. +char *grn_cgo_table_get_name(grn_ctx *ctx, grn_obj *table); + +typedef struct { + grn_id id; // Row ID, GRN_ID_NIL means the info is invalid. + grn_bool inserted; // Inserted or not. +} grn_cgo_row_info; + +// grn_cgo_table_insert_void() inserts an empty row. +grn_cgo_row_info grn_cgo_table_insert_void(grn_ctx *ctx, grn_obj *table); +// grn_cgo_table_insert_bool() inserts a row with Bool key. +grn_cgo_row_info grn_cgo_table_insert_bool(grn_ctx *ctx, grn_obj *table, + grn_bool key); +// grn_cgo_table_insert_int() inserts a row with Int key. +grn_cgo_row_info grn_cgo_table_insert_int(grn_ctx *ctx, grn_obj *table, + int64_t key); +// grn_cgo_table_insert_float() inserts a row with Float key. +grn_cgo_row_info grn_cgo_table_insert_float(grn_ctx *ctx, grn_obj *table, + double key); +// grn_cgo_table_insert_geo_point() inserts a row with GeoPoint key. +grn_cgo_row_info grn_cgo_table_insert_geo_point(grn_ctx *ctx, grn_obj *table, + grn_geo_point key); +// grn_cgo_table_insert_text() inserts a row with Text key. +grn_cgo_row_info grn_cgo_table_insert_text(grn_ctx *ctx, grn_obj *table, + const grn_cgo_text *key); + +// grn_cgo_column_set_bool() assigns a Bool value. +grn_bool grn_cgo_column_set_bool(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_bool value); +// grn_cgo_column_set_int() assigns an Int value. +grn_bool grn_cgo_column_set_int(grn_ctx *ctx, grn_obj *column, + grn_id id, int64_t value); +// grn_cgo_column_set_float() assigns a Float value. +grn_bool grn_cgo_column_set_float(grn_ctx *ctx, grn_obj *column, + grn_id id, double value); +// grn_cgo_column_set_geo_point() assigns a GeoPoint value. +grn_bool grn_cgo_column_set_geo_point(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_geo_point value); +// grn_cgo_column_set_text() assigns a Text value. +grn_bool grn_cgo_column_set_text(grn_ctx *ctx, grn_obj *column, + grn_id id, const grn_cgo_text *value); +// grn_cgo_column_set_bool_vector() assigns a Bool vector. +grn_bool grn_cgo_column_set_bool_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value); +// grn_cgo_column_set_int_vector() assigns an Int vector. +grn_bool grn_cgo_column_set_int_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value); +// grn_cgo_column_set_float_vector() assigns a Float vector. +grn_bool grn_cgo_column_set_float_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value); +// grn_cgo_column_set_geo_point_vector() assigns a GeoPoint vector. +grn_bool grn_cgo_column_set_geo_point_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value); +// grn_cgo_column_set_text_vector() assigns a Text vector. +// value must refer to an array of grn_cgo_text. +grn_bool grn_cgo_column_set_text_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, + const grn_cgo_vector *value); + +// grn_cgo_column_get_X_vector() sets *(X *)(value.ptr)[i] if value->size >= +// the actual vector size. +// In the case of Text, bodies are copied to (X *)(value.ptr)[i].ptr if +// (X *)(value.ptr)[i].size >= the actual body size. +// Then, grn_cgo_column_get_X_vector() sets value->size. + +// grn_cgo_column_get_bool() gets a stored Bool value. +grn_bool grn_cgo_column_get_bool(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_bool *value); +// grn_cgo_column_get_int() gets a stored Int value. +grn_bool grn_cgo_column_get_int(grn_ctx *ctx, grn_obj *column, + grn_id id, int64_t *value); +// grn_cgo_column_get_float() gets a stored Float value. +grn_bool grn_cgo_column_get_float(grn_ctx *ctx, grn_obj *column, + grn_id id, double *value); +// grn_cgo_column_get_geo_point() gets a stored GeoPoint value. +grn_bool grn_cgo_column_get_geo_point(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_geo_point *value); +// grn_cgo_column_get_text() gets a stored Text value. +grn_bool grn_cgo_column_get_text(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_text *value); +// grn_cgo_column_get_bool_vector() gets a stored Bool vector. +grn_bool grn_cgo_column_get_bool_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value); +// grn_cgo_column_get_int_vector() gets a stored Int vector. +grn_bool grn_cgo_column_get_int_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value); +// grn_cgo_column_get_float_vector() gets a stored Float vector. +grn_bool grn_cgo_column_get_float_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value); +// grn_cgo_column_get_geo_point_vector() gets a stored GeoPoint vector. +grn_bool grn_cgo_column_get_geo_point_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value); +// grn_cgo_column_get_text_vector() gets a stored Text vector. +// value must refer to an array of grn_cgo_text. +grn_bool grn_cgo_column_get_text_vector(grn_ctx *ctx, grn_obj *column, + grn_id id, grn_cgo_vector *value); + +grn_bool grn_cgo_column_get_bools(grn_ctx *ctx, grn_obj *column, size_t n, + const int64_t *ids, grn_bool *values); + +#endif // GRN_CGO_H Added: grn_test.go (+1128 -0) 100644 =================================================================== --- /dev/null +++ grn_test.go 2015-06-19 14:34:28 +0900 (1a190f6) @@ -0,0 +1,1128 @@ +package gnx + +import ( + "fmt" + "io/ioutil" + "math/rand" + "os" + "reflect" + "strconv" + "testing" +) + +// createTempGrnDB() creates a database for tests. +// The database must be removed with removeTempGrnDB(). +func createTempGrnDB(tb testing.TB) (string, string, *GrnDB) { + dirPath, err := ioutil.TempDir("", "grn_test") + if err != nil { + tb.Fatalf("ioutil.TempDir() failed: %v", err) + } + dbPath := dirPath + "/db" + db, err := CreateGrnDB(dbPath) + if err != nil { + os.RemoveAll(dirPath) + tb.Fatalf("CreateGrnDB() failed: %v", err) + } + return dirPath, dbPath, db +} + +// removeTempGrnDB() removes a database created with createTempGrnDB(). +func removeTempGrnDB(tb testing.TB, dirPath string, db *GrnDB) { + if err := db.Close(); err != nil { + os.RemoveAll(dirPath) + tb.Fatalf("GrnDB.Close() failed: %v", err) + } + if err := os.RemoveAll(dirPath); err != nil { + tb.Fatalf("os.RemoveAll() failed: %v", err) + } +} + +// createTempGrnTable() creates a database and a table for tests. +// createTempGrnTable() uses createTempGrnDB() to create a database, so the +// database must be removed with removeTempGrnDB(). +func createTempGrnTable(tb testing.TB, name string, options *TableOptions) ( + string, string, *GrnDB, *GrnTable) { + dirPath, dbPath, db := createTempGrnDB(tb) + table, err := db.CreateTable(name, options) + if err != nil { + removeTempGrnDB(tb, dirPath, db) + tb.Fatalf("GrnDB.CreateTable() failed: %v", err) + } + return dirPath, dbPath, db, table +} + +// createTempGrnColumn() creates a database, a table, and a column for tests. +// createTempGrnColumn() uses createTempGrnDB() to create a database, so the +// database must be removed with removeTempGrnDB(). +func createTempGrnColumn(tb testing.TB, tableName string, + tableOptions *TableOptions, columnName string, valueType string, + columnOptions *ColumnOptions) ( + string, string, *GrnDB, *GrnTable, *GrnColumn) { + dirPath, dbPath, db, table := createTempGrnTable(tb, tableName, tableOptions) + column, err := table.CreateColumn(columnName, valueType, columnOptions) + if err != nil { + removeTempGrnDB(tb, dirPath, db) + tb.Fatalf("GrnDB.CreateTable() failed: %v", err) + } + return dirPath, dbPath, db, table, column +} + +func TestCreateGrnDB(t *testing.T) { + dirPath, _, db := createTempGrnDB(t) + removeTempGrnDB(t, dirPath, db) +} + +func TestOpenGrnDB(t *testing.T) { + dirPath, dbPath, db := createTempGrnDB(t) + db2, err := OpenGrnDB(dbPath) + if err != nil { + t.Fatalf("OpenGrnDB() failed: %v", err) + } + db2.Close() + removeTempGrnDB(t, dirPath, db) +} + +func testGrnDBCreateTableWithKey(t *testing.T, keyType string) { + options := NewTableOptions() + options.TableType = PatTable + options.KeyType = keyType + dirPath, _, db, _ := createTempGrnTable(t, "Table", options) + removeTempGrnDB(t, dirPath, db) +} + +func testGrnDBCreateTableWithValue(t *testing.T, valueType string) { + options := NewTableOptions() + options.TableType = PatTable + options.ValueType = valueType + dirPath, _, db, _ := createTempGrnTable(t, "Table", options) + removeTempGrnDB(t, dirPath, db) +} + +func testGrnDBCreateTableWithRefKey(t *testing.T, keyType string) { + options := NewTableOptions() + options.TableType = PatTable + options.KeyType = keyType + dirPath, _, db, _ := createTempGrnTable(t, "To", options) + defer removeTempGrnDB(t, dirPath, db) + + options = NewTableOptions() + options.TableType = PatTable + options.KeyType = "To" + _, err := db.CreateTable("From", options) + if err != nil { + t.Fatalf("GrnDB.CreateTable() failed: %v", err) + } +} + +func testGrnDBCreateTableWithRefValue(t *testing.T, keyType string) { + options := NewTableOptions() + options.TableType = PatTable + options.KeyType = keyType + dirPath, _, db, _ := createTempGrnTable(t, "To", options) + defer removeTempGrnDB(t, dirPath, db) + + options = NewTableOptions() + options.ValueType = "" + _, err := db.CreateTable("From", options) + if err != nil { + t.Fatalf("GrnDB.CreateTable() failed: %v", err) + } +} + +func TestGrnDBCreateTableWithoutKeyValue(t *testing.T) { + dirPath, _, db, _ := createTempGrnTable(t, "Table", nil) + removeTempGrnDB(t, dirPath, db) +} + +func TestGrnDBCreateTableWithBoolKey(t *testing.T) { + testGrnDBCreateTableWithKey(t, "Bool") +} + +func TestGrnDBCreateTableWithIntKey(t *testing.T) { + testGrnDBCreateTableWithKey(t, "Int") +} + +func TestGrnDBCreateTableWithFloatKey(t *testing.T) { + testGrnDBCreateTableWithKey(t, "Float") +} + +func TestGrnDBCreateTableWithGeoPointKey(t *testing.T) { + testGrnDBCreateTableWithKey(t, "GeoPoint") +} + +func TestGrnDBCreateTableWithTextKey(t *testing.T) { + testGrnDBCreateTableWithKey(t, "Text") +} + +func TestGrnDBCreateTableWithBoolValue(t *testing.T) { + testGrnDBCreateTableWithValue(t, "Bool") +} + +func TestGrnDBCreateTableWithIntValue(t *testing.T) { + testGrnDBCreateTableWithValue(t, "Int") +} + +func TestGrnDBCreateTableWithFloatValue(t *testing.T) { + testGrnDBCreateTableWithValue(t, "Float") +} + +func TestGrnDBCreateTableWithGeoPointValue(t *testing.T) { + testGrnDBCreateTableWithValue(t, "GeoPoint") +} + +func TestGrnDBCreateTableWithBoolRefKey(t *testing.T) { + testGrnDBCreateTableWithRefKey(t, "Bool") +} + +func TestGrnDBCreateTableWithIntRefKey(t *testing.T) { + testGrnDBCreateTableWithRefKey(t, "Int") +} + +func TestGrnDBCreateTableWithFloatRefKey(t *testing.T) { + testGrnDBCreateTableWithRefKey(t, "Float") +} + +func TestGrnDBCreateTableWithGeoPointRefKey(t *testing.T) { + testGrnDBCreateTableWithRefKey(t, "GeoPoint") +} + +func TestGrnDBCreateTableWithTextRefKey(t *testing.T) { + testGrnDBCreateTableWithRefKey(t, "Text") +} + +func TestGrnDBCreateTableWithBoolRefValue(t *testing.T) { + testGrnDBCreateTableWithRefValue(t, "Bool") +} + +func TestGrnDBCreateTableWithIntRefValue(t *testing.T) { + testGrnDBCreateTableWithRefValue(t, "Int") +} + +func TestGrnDBCreateTableWithFloatRefValue(t *testing.T) { + testGrnDBCreateTableWithRefValue(t, "Float") +} + +func TestGrnDBCreateTableWithGeoPointRefValue(t *testing.T) { + testGrnDBCreateTableWithRefValue(t, "GeoPoint") +} + +func TestGrnDBCreateTableWithTextRefValue(t *testing.T) { + testGrnDBCreateTableWithRefValue(t, "Text") +} + +func generateRandomKey(keyType string) interface{} { + switch keyType { + case "Bool": + if (rand.Int() & 1) == 1 { + return True + } else { + return False + } + case "Int": + return Int(rand.Int63()) + case "Float": + return Float(rand.Float64()) + case "GeoPoint": + const ( + MinLatitude = 73531000 + MaxLatitude = 164006000 + MinLongitude = 439451000 + MaxLongitude = 554351000 + ) + latitude := MinLatitude + rand.Intn(MaxLatitude-MinLatitude+1) + longitude := MinLongitude + rand.Intn(MaxLongitude-MinLongitude+1) + return GeoPoint{int32(latitude), int32(longitude)} + case "Text": + return Text(strconv.Itoa(rand.Int())) + default: + return nil + } +} + +func testGrnTableInsertRow(t *testing.T, keyType string) { + options := NewTableOptions() + if keyType != "" { + options.TableType = PatTable + } + options.KeyType = keyType + dirPath, _, db, table := createTempGrnTable(t, "Table", options) + defer removeTempGrnDB(t, dirPath, db) + + count := 0 + for i := 0; i < 100; i++ { + inserted, _, err := table.InsertRow(generateRandomKey(keyType)) + if err != nil { + t.Fatalf("GrnTable.InsertRow() failed: %v", err) + } + if inserted { + count++ + } + } + t.Logf("keyType = <%s>, count = %d", keyType, count) +} + +func TestGrnTableInsertRowWithoutKey(t *testing.T) { + testGrnTableInsertRow(t, "") +} + +func TestGrnTableInsertRowWithBoolKey(t *testing.T) { + testGrnTableInsertRow(t, "Bool") +} + +func TestGrnTableInsertRowWithIntKey(t *testing.T) { + testGrnTableInsertRow(t, "Int") +} + +func TestGrnTableInsertRowWithFloatKey(t *testing.T) { + testGrnTableInsertRow(t, "Float") +} + +func TestGrnTableInsertRowWithGeoPointKey(t *testing.T) { + testGrnTableInsertRow(t, "GeoPoint") +} + +func TestGrnTableInsertRowWithTextKey(t *testing.T) { + testGrnTableInsertRow(t, "Text") +} + +func testGrnTableCreateScalarColumn(t *testing.T, valueType string) { + dirPath, _, db, table, _ := + createTempGrnColumn(t, "Table", nil, "Value", valueType, nil) + defer removeTempGrnDB(t, dirPath, db) + + if column, err := table.FindColumn("_id"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("_id: %+v", column) + } + if column, err := table.FindColumn("Value"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value: %+v", column) + } +} + +func testGrnTableCreateVectorColumn(t *testing.T, valueType string) { + options := NewColumnOptions() + options.ColumnType = VectorColumn + dirPath, _, db, table, _ := + createTempGrnColumn(t, "Table", nil, "Value", valueType, options) + defer removeTempGrnDB(t, dirPath, db) + + if column, err := table.FindColumn("_id"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("_id: %+v", column) + } + if column, err := table.FindColumn("Value"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value: %+v", column) + } +} + +func testGrnTableCreateScalarRefColumn(t *testing.T, keyType string) { + options := NewTableOptions() + options.TableType = PatTable + options.KeyType = keyType + dirPath, _, db, table, _ := + createTempGrnColumn(t, "Table", options, "Value", "Table", nil) + defer removeTempGrnDB(t, dirPath, db) + + if column, err := table.FindColumn("Value"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value: %+v", column) + } + if column, err := table.FindColumn("Value._id"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value._id: %+v", column) + } + if column, err := table.FindColumn("Value._key"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value._key: %+v", column) + } +} + +func testGrnTableCreateVectorRefColumn(t *testing.T, keyType string) { + tableOptions := NewTableOptions() + tableOptions.TableType = PatTable + tableOptions.KeyType = keyType + columnOptions := NewColumnOptions() + columnOptions.ColumnType = VectorColumn + dirPath, _, db, table, _ := + createTempGrnColumn(t, "Table", tableOptions, "Value", "Table", columnOptions) + defer removeTempGrnDB(t, dirPath, db) + + if column, err := table.FindColumn("Value"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value: %+v", column) + } + if column, err := table.FindColumn("Value._id"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value._id: %+v", column) + } + if column, err := table.FindColumn("Value._key"); err != nil { + t.Fatalf("Table.FindColumn() failed: %v", err) + } else { + t.Logf("Value._key: %+v", column) + } +} + +func TestGrnTableCreateColumnForBool(t *testing.T) { + testGrnTableCreateScalarColumn(t, "Bool") +} + +func TestGrnTableCreateColumnForInt(t *testing.T) { + testGrnTableCreateScalarColumn(t, "Int") +} + +func TestGrnTableCreateColumnForFloat(t *testing.T) { + testGrnTableCreateScalarColumn(t, "Float") +} + +func TestGrnTableCreateColumnForGeoPoint(t *testing.T) { + testGrnTableCreateScalarColumn(t, "GeoPoint") +} + +func TestGrnTableCreateColumnForText(t *testing.T) { + testGrnTableCreateScalarColumn(t, "Text") +} + +func TestGrnTableCreateColumnForBoolVector(t *testing.T) { + testGrnTableCreateVectorColumn(t, "Bool") +} + +func TestGrnTableCreateColumnForIntVector(t *testing.T) { + testGrnTableCreateVectorColumn(t, "Int") +} + +func TestGrnTableCreateColumnForFloatVector(t *testing.T) { + testGrnTableCreateVectorColumn(t, "Float") +} + +func TestGrnTableCreateColumnForGeoPointVector(t *testing.T) { + testGrnTableCreateVectorColumn(t, "GeoPoint") +} + +func TestGrnTableCreateColumnForTextVector(t *testing.T) { + testGrnTableCreateVectorColumn(t, "Text") +} + +func TestGrnTableCreateColumnForRefToBool(t *testing.T) { + testGrnTableCreateScalarRefColumn(t, "Bool") +} + +func TestGrnTableCreateColumnForRefToInt(t *testing.T) { + testGrnTableCreateScalarRefColumn(t, "Int") +} + +func TestGrnTableCreateColumnForRefToFloat(t *testing.T) { + testGrnTableCreateScalarRefColumn(t, "Float") +} + +func TestGrnTableCreateColumnForRefToGeoPoint(t *testing.T) { + testGrnTableCreateScalarRefColumn(t, "GeoPoint") +} + +func TestGrnTableCreateColumnForRefToText(t *testing.T) { + testGrnTableCreateScalarRefColumn(t, "Text") +} + +func TestGrnTableCreateColumnForRefToBoolVector(t *testing.T) { + testGrnTableCreateVectorRefColumn(t, "Bool") +} + +func TestGrnTableCreateColumnForRefToIntVector(t *testing.T) { + testGrnTableCreateVectorRefColumn(t, "Int") +} + +func TestGrnTableCreateColumnForRefToFloatVector(t *testing.T) { + testGrnTableCreateVectorRefColumn(t, "Float") +} + +func TestGrnTableCreateColumnForRefToGeoPointVector(t *testing.T) { + testGrnTableCreateVectorRefColumn(t, "GeoPoint") +} + +func TestGrnTableCreateColumnForRefToTextVector(t *testing.T) { + testGrnTableCreateVectorRefColumn(t, "Text") +} + +func generateRandomValue(valueType string) interface{} { + switch valueType { + case "Bool": + if (rand.Int() & 1) == 1 { + return True + } else { + return False + } + case "Int": + return Int(rand.Int63()) + case "Float": + return Float(rand.Float64()) + case "GeoPoint": + const ( + MinLatitude = 73531000 + MaxLatitude = 164006000 + MinLongitude = 439451000 + MaxLongitude = 554351000 + ) + latitude := MinLatitude + rand.Intn(MaxLatitude-MinLatitude+1) + longitude := MinLongitude + rand.Intn(MaxLongitude-MinLongitude+1) + return GeoPoint{int32(latitude), int32(longitude)} + case "Text": + return Text(strconv.Itoa(rand.Int())) + default: + return nil + } +} + +func generateRandomVectorValue(valueType string) interface{} { + size := rand.Int() % 10 + switch valueType { + case "Bool": + value := make([]Bool, size) + for i := 0; i < size; i++ { + if (rand.Int() & 1) == 1 { + value[i] = True + } + } + return value + case "Int": + value := make([]Int, size) + for i := 0; i < size; i++ { + value[i] = Int(rand.Int63()) + } + return value + case "Float": + value := make([]Float, size) + for i := 0; i < size; i++ { + value[i] = Float(rand.Float64()) + } + return value + case "GeoPoint": + const ( + MinLatitude = 73531000 + MaxLatitude = 164006000 + MinLongitude = 439451000 + MaxLongitude = 554351000 + ) + value := make([]GeoPoint, size) + for i := 0; i < size; i++ { + latitude := MinLatitude + rand.Intn(MaxLatitude-MinLatitude+1) + longitude := MinLongitude + rand.Intn(MaxLongitude-MinLongitude+1) + value[i] = GeoPoint{int32(latitude), int32(longitude)} + } + return value + case "Text": + value := make([]Text, size) + for i := 0; i < size; i++ { + value[i] = Text(strconv.Itoa(rand.Int())) + } + return value + default: + return nil + } +} + +func testGrnColumnSetValueForScalar(t *testing.T, valueType string) { + dirPath, _, db, table, column := + createTempGrnColumn(t, "Table", nil, "Value", valueType, nil) + defer removeTempGrnDB(t, dirPath, db) + + for i := 0; i < 100; i++ { + _, id, err := table.InsertRow(nil) + if err != nil { + t.Fatalf("GrnTable.InsertRow() failed: %v", err) + } + if err := column.SetValue(id, generateRandomValue(valueType)); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + } + + bytes, _ := db.Query("select Table --limit 3") + t.Logf("valueType = <%s>, result = %s", valueType, string(bytes)) +} + +func testGrnColumnSetValueForVector(t *testing.T, valueType string) { + options := NewColumnOptions() + options.ColumnType = VectorColumn + dirPath, _, db, table, column := + createTempGrnColumn(t, "Table", nil, "Value", valueType, options) + defer removeTempGrnDB(t, dirPath, db) + + for i := 0; i < 100; i++ { + _, id, err := table.InsertRow(nil) + if err != nil { + t.Fatalf("GrnTable.InsertRow() failed: %v", err) + } + if err := column.SetValue(id, generateRandomVectorValue(valueType)); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + } + + bytes, _ := db.Query("select Table --limit 3") + t.Logf("valueType = <%s>, result = %s", valueType, string(bytes)) +} + +func TestGrnColumnSetValueForBool(t *testing.T) { + testGrnColumnSetValueForScalar(t, "Bool") +} + +func TestGrnColumnSetValueForInt(t *testing.T) { + testGrnColumnSetValueForScalar(t, "Int") +} + +func TestGrnColumnSetValueForFloat(t *testing.T) { + testGrnColumnSetValueForScalar(t, "Float") +} + +func TestGrnColumnSetValueForGeoPoint(t *testing.T) { + testGrnColumnSetValueForScalar(t, "GeoPoint") +} + +func TestGrnColumnSetValueForText(t *testing.T) { + testGrnColumnSetValueForScalar(t, "Text") +} + +func TestGrnColumnSetValueForBoolVector(t *testing.T) { + testGrnColumnSetValueForVector(t, "Bool") +} + +func TestGrnColumnSetValueForIntVector(t *testing.T) { + testGrnColumnSetValueForVector(t, "Int") +} + +func TestGrnColumnSetValueForFloatVector(t *testing.T) { + testGrnColumnSetValueForVector(t, "Float") +} + +func TestGrnColumnSetValueForGeoPointVector(t *testing.T) { + testGrnColumnSetValueForVector(t, "GeoPoint") +} + +func TestGrnColumnSetValueForTextVector(t *testing.T) { + testGrnColumnSetValueForVector(t, "Text") +} + +func testGrnColumnGetValueForScalar(t *testing.T, valueType string) { + dirPath, _, db, table, column := + createTempGrnColumn(t, "Table", nil, "Value", valueType, nil) + defer removeTempGrnDB(t, dirPath, db) + + for i := 0; i < 100; i++ { + _, id, err := table.InsertRow(nil) + if err != nil { + t.Fatalf("GrnTable.InsertRow() failed: %v", err) + } + value := generateRandomValue(valueType) + if err := column.SetValue(id, value); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + if storedValue, err := column.GetValue(id); err != nil { + t.Fatalf("GrnColumn.GetValue() failed: %v", err) + } else if !reflect.DeepEqual(value, storedValue) { + t.Fatalf("GrnColumn.GetValue() failed: value = %v, storedValue = %v", + value, storedValue) + } + } +} + +func testGrnColumnGetValueForVector(t *testing.T, valueType string) { + options := NewColumnOptions() + options.ColumnType = VectorColumn + dirPath, _, db, table, column := + createTempGrnColumn(t, "Table", nil, "Value", valueType, options) + defer removeTempGrnDB(t, dirPath, db) + + for i := 0; i < 100; i++ { + _, id, err := table.InsertRow(nil) + if err != nil { + t.Fatalf("GrnTable.InsertRow() failed: %v", err) + } + value := generateRandomVectorValue(valueType) + if err := column.SetValue(id, value); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + if storedValue, err := column.GetValue(id); err != nil { + t.Fatalf("GrnColumn.GetValue() failed: %v", err) + } else if !reflect.DeepEqual(value, storedValue) { + t.Fatalf("GrnColumn.GetValue() failed: value = %v, storedValue = %v", + value, storedValue) + } + } + + bytes, _ := db.Query("select Table --limit 3") + t.Logf("valueType = <%s>, result = %s", valueType, string(bytes)) +} + +func TestGrnColumnGetValueForBool(t *testing.T) { + testGrnColumnGetValueForScalar(t, "Bool") +} + +func TestGrnColumnGetValueForInt(t *testing.T) { + testGrnColumnGetValueForScalar(t, "Int") +} + +func TestGrnColumnGetValueForFloat(t *testing.T) { + testGrnColumnGetValueForScalar(t, "Float") +} + +func TestGrnColumnGetValueForGeoPoint(t *testing.T) { + testGrnColumnGetValueForScalar(t, "GeoPoint") +} + +func TestGrnColumnGetValueForText(t *testing.T) { + testGrnColumnGetValueForScalar(t, "Text") +} + +func TestGrnColumnGetValueForBoolVector(t *testing.T) { + testGrnColumnGetValueForVector(t, "Bool") +} + +func TestGrnColumnGetValueForIntVector(t *testing.T) { + testGrnColumnGetValueForVector(t, "Int") +} + +func TestGrnColumnGetValueForFloatVector(t *testing.T) { + testGrnColumnGetValueForVector(t, "Float") +} + +func TestGrnColumnGetValueForGeoPointVector(t *testing.T) { + testGrnColumnGetValueForVector(t, "GeoPoint") +} + +func TestGrnColumnGetValueForTextVector(t *testing.T) { + testGrnColumnGetValueForVector(t, "Text") +} + +var numTestRows = 100000 + +func benchmarkGrnColumnSetValueForScalar(b *testing.B, valueType string) { + b.StopTimer() + dirPath, _, db, table := + createTempGrnTable(b, "Table", nil) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + values := make([]interface{}, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + ids[i] = id + values[i] = generateRandomValue(valueType) + } + + for i := 0; i < b.N; i++ { + column, err := table.CreateColumn(fmt.Sprintf("V%d", i), valueType, nil) + if err != nil { + b.Fatalf("Table.CreateColumn() failed(): %s", err) + } + b.StartTimer() + for i, id := range ids { + if err := column.SetValue(id, values[i]); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + b.StopTimer() + } +} + +func benchmarkGrnColumnSetValueForVector(b *testing.B, valueType string) { + b.StopTimer() + dirPath, _, db, table := + createTempGrnTable(b, "Table", nil) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + values := make([]interface{}, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + ids[i] = id + values[i] = generateRandomVectorValue(valueType) + } + + for i := 0; i < b.N; i++ { + options := NewColumnOptions() + options.ColumnType = VectorColumn + column, err := table.CreateColumn(fmt.Sprintf("V%d", i), valueType, options) + if err != nil { + b.Fatalf("Table.CreateColumn() failed(): %s", err) + } + b.StartTimer() + for i, id := range ids { + if err := column.SetValue(id, values[i]); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + b.StopTimer() + } +} + +func BenchmarkGrnColumnSetValueForBool(b *testing.B) { + benchmarkGrnColumnSetValueForScalar(b, "Bool") +} + +func BenchmarkGrnColumnSetValueForInt(b *testing.B) { + benchmarkGrnColumnSetValueForScalar(b, "Int") +} + +func BenchmarkGrnColumnSetValueForFloat(b *testing.B) { + benchmarkGrnColumnSetValueForScalar(b, "Float") +} + +func BenchmarkGrnColumnSetValueForGeoPoint(b *testing.B) { + benchmarkGrnColumnSetValueForScalar(b, "GeoPoint") +} + +func BenchmarkGrnColumnSetValueForText(b *testing.B) { + benchmarkGrnColumnSetValueForScalar(b, "Text") +} + +func BenchmarkGrnColumnSetValueForBoolVector(b *testing.B) { + benchmarkGrnColumnSetValueForVector(b, "Bool") +} + +func BenchmarkGrnColumnSetValueForIntVector(b *testing.B) { + benchmarkGrnColumnSetValueForVector(b, "Int") +} + +func BenchmarkGrnColumnSetValueForFloatVector(b *testing.B) { + benchmarkGrnColumnSetValueForVector(b, "Float") +} + +func BenchmarkGrnColumnSetValueForGeoPointVector(b *testing.B) { + benchmarkGrnColumnSetValueForVector(b, "GeoPoint") +} + +func BenchmarkGrnColumnSetValueForTextVector(b *testing.B) { + benchmarkGrnColumnSetValueForVector(b, "Text") +} + +func benchmarkGrnColumnGetValueForScalar(b *testing.B, valueType string) { + dirPath, _, db, table, column := + createTempGrnColumn(b, "Table", nil, "Value", valueType, nil) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + if err := column.SetValue(id, generateRandomValue(valueType)); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, id := range ids { + if _, err := column.GetValue(id); err != nil { + b.Fatalf("Column.GetValue() failed: %s", err) + } + } + } +} + +func benchmarkGrnColumnGetValueForVector(b *testing.B, valueType string) { + options := NewColumnOptions() + options.ColumnType = VectorColumn + dirPath, _, db, table, column := + createTempGrnColumn(b, "Table", nil, "Value", valueType, options) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + if err := column.SetValue(id, generateRandomVectorValue(valueType)); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, id := range ids { + if _, err := column.GetValue(id); err != nil { + b.Fatalf("Column.GetValue() failed: %s", err) + } + } + } +} + +func BenchmarkGrnColumnGetValueForBool(b *testing.B) { + benchmarkGrnColumnGetValueForScalar(b, "Bool") +} + +func BenchmarkGrnColumnGetValueForInt(b *testing.B) { + benchmarkGrnColumnGetValueForScalar(b, "Int") +} + +func BenchmarkGrnColumnGetValueForFloat(b *testing.B) { + benchmarkGrnColumnGetValueForScalar(b, "Float") +} + +func BenchmarkGrnColumnGetValueForGeoPoint(b *testing.B) { + benchmarkGrnColumnGetValueForScalar(b, "GeoPoint") +} + +func BenchmarkGrnColumnGetValueForText(b *testing.B) { + benchmarkGrnColumnGetValueForScalar(b, "Text") +} + +func BenchmarkGrnColumnGetValueForBoolVector(b *testing.B) { + benchmarkGrnColumnGetValueForVector(b, "Bool") +} + +func BenchmarkGrnColumnGetValueForIntVector(b *testing.B) { + benchmarkGrnColumnGetValueForVector(b, "Int") +} + +func BenchmarkGrnColumnGetValueForFloatVector(b *testing.B) { + benchmarkGrnColumnGetValueForVector(b, "Float") +} + +func BenchmarkGrnColumnGetValueForGeoPointVector(b *testing.B) { + benchmarkGrnColumnGetValueForVector(b, "GeoPoint") +} + +func BenchmarkGrnColumnGetValueForTextVector(b *testing.B) { + benchmarkGrnColumnGetValueForVector(b, "Text") +} + +func benchmarkGrnDBSelectForScalar(b *testing.B, valueType string) { + dirPath, _, db, table, column := + createTempGrnColumn(b, "Table", nil, "Value", valueType, nil) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + if err := column.SetValue(id, generateRandomValue(valueType)); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := db.Query("select Table --output_columns Value --limit -1 --cache no") + if err != nil { + b.Fatalf("DB.Query() failed: %s", err) + } + } +} + +func benchmarkGrnDBSelectForVector(b *testing.B, valueType string) { + options := NewColumnOptions() + options.ColumnType = VectorColumn + dirPath, _, db, table, column := + createTempGrnColumn(b, "Table", nil, "Value", valueType, options) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + if err := column.SetValue(id, generateRandomVectorValue(valueType)); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + bytes, err := db.Query("select Table --output_columns Value --limit -1 --cache no") + if err != nil { + b.Fatalf("DB.Query() failed: %s", err) + } + if len(bytes) < numTestRows * 5 { + b.Fatalf("DB.Query() failed: %s", err) + } + } +} + +func BenchmarkGrnDBSelectForBool(b *testing.B) { + benchmarkGrnDBSelectForScalar(b, "Bool") +} + +func BenchmarkGrnDBSelectForInt(b *testing.B) { + benchmarkGrnDBSelectForScalar(b, "Int") +} + +func BenchmarkGrnDBSelectForFloat(b *testing.B) { + benchmarkGrnDBSelectForScalar(b, "Float") +} + +func BenchmarkGrnDBSelectForGeoPoint(b *testing.B) { + benchmarkGrnDBSelectForScalar(b, "GeoPoint") +} + +func BenchmarkGrnDBSelectForText(b *testing.B) { + benchmarkGrnDBSelectForScalar(b, "Text") +} + +func BenchmarkGrnDBSelectForBoolVector(b *testing.B) { + benchmarkGrnDBSelectForVector(b, "Bool") +} + +func BenchmarkGrnDBSelectForIntVector(b *testing.B) { + benchmarkGrnDBSelectForVector(b, "Int") +} + +func BenchmarkGrnDBSelectForFloatVector(b *testing.B) { + benchmarkGrnDBSelectForVector(b, "Float") +} + +func BenchmarkGrnDBSelectForGeoPointVector(b *testing.B) { + benchmarkGrnDBSelectForVector(b, "GeoPoint") +} + +func BenchmarkGrnDBSelectForTextVector(b *testing.B) { + benchmarkGrnDBSelectForVector(b, "Text") +} + +func benchmarkGrnColumnGetValuesForScalar(b *testing.B, valueType string) { + dirPath, _, db, table, column := + createTempGrnColumn(b, "Table", nil, "Value", valueType, nil) + defer removeTempGrnDB(b, dirPath, db) + ids := make([]Int, numTestRows) + for i, _ := range ids { + _, id, err := table.InsertRow(nil) + if err != nil { + b.Fatalf("Table.InsertRow() failed: %s", err) + } + if err := column.SetValue(id, generateRandomValue(valueType)); err != nil { + b.Fatalf("Column.SetValue() failed: %s", err) + } + ids[i] = id + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := column.GetValues(ids); err != nil { + b.Fatalf("Column.GetValues() failed: %s", err) + } + } +} + +func BenchmarkGrnColumnGetValuesForBool(b *testing.B) { + benchmarkGrnColumnGetValuesForScalar(b, "Bool") +} + +func TestBuildDB(t *testing.T) { + db, err := CreateGrnDB("/tmp/groonga/db") + if err != nil { + t.Fatalf("CreateGrnDB() failed: %v", err) + } +// tableOptions := NewTableOptions() +// tableOptions.TableType = PatTable +// tableOptions.KeyType = "Int" +// table, err := db.CreateTable("Table", tableOptions) +// if err != nil { +// t.Fatalf("GrnDB.CreateTable() failed: %v", err) +// } + table, err := db.CreateTable("Table", nil) + if err != nil { + t.Fatalf("GrnDB.CreateTable() failed: %v", err) + } + column, err := table.CreateColumn("X", "Int", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } + column2, err := table.CreateColumn("Y", "Int", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } + column3, err := table.CreateColumn("Z", "Int", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } + column4, err := table.CreateColumn("A", "Bool", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } + column5, err := table.CreateColumn("B", "Float", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } +// column6, err := table.CreateColumn("C", "Time", nil) +// if err != nil { +// t.Fatalf("GrnDB.CreateColumn() failed: %v", err) +// } + column7, err := table.CreateColumn("D", "Text", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } + column8, err := table.CreateColumn("E", "GeoPoint", nil) + if err != nil { + t.Fatalf("GrnDB.CreateColumn() failed: %v", err) + } + const MaxID = 1000000 + for { +// _, id, err := table.InsertRow(Int(rand.Int63() % 100000000)) + _, id, err := table.InsertRow(nil) + if err != nil { + t.Fatalf("GrnTable.InsertRow() failed: %v", err) + } + if (id == MaxID) { + break + } + } + for id := 1; id <= MaxID; id++ { + if err := column.SetValue(Int(id), Int(rand.Int63() % 100)); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + if err := column2.SetValue(Int(id), Int(rand.Int63() % 100)); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + if err := column3.SetValue(Int(id), Int(rand.Int63() % 100)); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + boolValue := False + if (rand.Int() & 1) == 1 { + boolValue = True + } + if err := column4.SetValue(Int(id), boolValue); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + if err := column5.SetValue(Int(id), Float(float64(rand.Int31()))); err != nil { +// if err := column5.SetValue(Int(id), Float(rand.Float64())); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } +// if err := column6.SetValue(Int(id), Int(rand.Int63() / 1000)); err != nil { +// t.Fatalf("GrnColumn.SetValue() failed: %v", err) +// } + if err := column7.SetValue(Int(id), Text(strconv.Itoa(rand.Int()) + "\nABC")); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + const ( + MinLatitude = 73531000 + MaxLatitude = 164006000 + MinLongitude = 439451000 + MaxLongitude = 554351000 + ) + latitude := MinLatitude + rand.Intn(MaxLatitude-MinLatitude+1) + longitude := MinLongitude + rand.Intn(MaxLongitude-MinLongitude+1) + geoValue := GeoPoint{int32(latitude), int32(longitude)} + if err := column8.SetValue(Int(id), geoValue); err != nil { + t.Fatalf("GrnColumn.SetValue() failed: %v", err) + } + } +} Added: options.go (+72 -0) 100644 =================================================================== --- /dev/null +++ options.go 2015-06-19 14:34:28 +0900 (4d984fb) @@ -0,0 +1,72 @@ +package gnx + +//import ( +// "" +//) + +// -- TableOptions -- + +// Constants for TableOptions. +type TableType int + +const ( + ArrayTable = TableType(iota) + HashTable + PatTable + DatTable +) + +// http://groonga.org/docs/reference/commands/table_create.html +type TableOptions struct { + TableType + WithSIS bool // KEY_WITH_SIS + KeyType string // http://groonga.org/docs/reference/types.html + ValueType string // http://groonga.org/docs/reference/types.html + DefaultTokenizer string // http://groonga.org/docs/reference/tokenizers.html + Normalizer string // http://groonga.org/docs/reference/normalizers.html + TokenFilters []string // http://groonga.org/docs/reference/token_filters.html +} + +// NewTableOptions() creates a new TableOptions object with the default +// settings. +func NewTableOptions() *TableOptions { + var options TableOptions + return &options +} + +// -- ColumnOptions -- + +// Constants for ColumnOptions. +type ColumnType int + +const ( + ScalarColumn = ColumnType(iota) + VectorColumn + IndexColumn +) + +// Constants for ColumnOptions. +type CompressionType int + +const ( + NoCompression = CompressionType(iota) + ZlibCompression + LzoCompression +) + +// http://groonga.org/ja/docs/reference/commands/column_create.html +type ColumnOptions struct { + ColumnType + CompressionType + WithSection bool // WITH_SECTION + WithWeight bool // WITH_WEIGHT + WithPosition bool // WITH_POSITION + Source string +} + +// NewColumnOptions() creates a new ColumnOptions object with the default +// settings. +func NewColumnOptions() *ColumnOptions { + var options ColumnOptions + return &options +}