susumu.yata
null+****@clear*****
Fri May 1 03:41:36 JST 2015
susumu.yata 2015-05-01 03:41:36 +0900 (Fri, 01 May 2015) New Revision: 420033e1c2eb350425ff567080fda71f65c9b361 https://github.com/groonga/grnxx/commit/420033e1c2eb350425ff567080fda71f65c9b361 Message: Gnx: implement CreateColumn() and FindColumn(). Modified files: go3/gnx/grn.go go3/gnx/grn_cgo.c go3/gnx/grn_cgo.h go3/gnx/grn_test.go go3/gnx/options.go Modified: go3/gnx/grn.go (+248 -18) =================================================================== --- go3/gnx/grn.go 2015-04-28 19:27:43 +0900 (13c492e) +++ go3/gnx/grn.go 2015-05-01 03:41:36 +0900 (506a3cd) @@ -358,7 +358,48 @@ func (db *GrnDB) FindTable(name string) (*GrnTable, error) { return nil, err } } - table := newGrnTable(db, obj, name, keyType, keyTable) + 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 } @@ -389,37 +430,26 @@ type GrnTable struct { 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) *GrnTable { + 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 } -// CreateColumn() creates a column. -func (table *GrnTable) CreateColumn(name string, valueType string, options *ColumnOptions) (*GrnColumn, error) { - // TODO - return nil, fmt.Errorf("not supported yet") -} - -// FindColumn() finds a column. -func (table *GrnTable) FindColumn(name string) (*GrnColumn, error) { - if column, ok := table.columns[name]; ok { - return column, nil - } - // TODO - return nil, fmt.Errorf("not supported yet") -} - // InsertVoid() inserts an empty row. func (table *GrnTable) InsertVoid() (bool, Int, error) { if table.keyType != VoidID { @@ -527,8 +557,208 @@ func (table *GrnTable) InsertRow(key interface{}) (bool, Int, error) { } } +// 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 { - obj *C.grn_obj + 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 } Modified: go3/gnx/grn_cgo.c (+68 -0) =================================================================== --- go3/gnx/grn_cgo.c 2015-04-28 19:27:43 +0900 (e2b7523) +++ go3/gnx/grn_cgo.c 2015-05-01 03:41:36 +0900 (c120c36) @@ -44,6 +44,9 @@ grn_bool grn_cgo_table_get_key_info(grn_ctx *ctx, grn_obj *table, 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; } @@ -60,6 +63,71 @@ grn_bool grn_cgo_table_get_key_info(grn_ctx *ctx, grn_obj *table, 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: { + 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: { + 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; Modified: go3/gnx/grn_cgo.h (+11 -5) =================================================================== --- go3/gnx/grn_cgo.h 2015-04-28 19:27:43 +0900 (886f814) +++ go3/gnx/grn_cgo.h 2015-05-01 03:41:36 +0900 (a9a25da) @@ -17,16 +17,22 @@ typedef struct { 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_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(). Modified: go3/gnx/grn_test.go (+135 -0) =================================================================== --- go3/gnx/grn_test.go 2015-04-28 19:27:43 +0900 (e9b99e4) +++ go3/gnx/grn_test.go 2015-05-01 03:41:36 +0900 (3762a1d) @@ -43,6 +43,19 @@ func createTempGrnTable(tb testing.TB, name string, options *TableOptions) ( return dirPath, dbPath, db, table } +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) @@ -66,6 +79,13 @@ func testGrnDBCreateTableWithKey(t *testing.T, keyType string) { removeTempGrnDB(t, dirPath, db) } +func testGrnDBCreateTableWithValue(t *testing.T, valueType string) { + options := NewTableOptions() + options.ValueType = valueType + dirPath, _, db, _ := createTempGrnTable(t, "Table", options) + removeTempGrnDB(t, dirPath, db) +} + func testGrnDBCreateTableRef(t *testing.T, keyType string) { options := NewTableOptions() options.TableType = PatTable @@ -90,6 +110,11 @@ func TestGrnDBCreateTable(t *testing.T) { testGrnDBCreateTableWithKey(t, "GeoPoint") testGrnDBCreateTableWithKey(t, "Text") + testGrnDBCreateTableWithValue(t, "Bool") + testGrnDBCreateTableWithValue(t, "Int") + testGrnDBCreateTableWithValue(t, "Float") + testGrnDBCreateTableWithValue(t, "GeoPoint") + testGrnDBCreateTableRef(t, "Bool") testGrnDBCreateTableRef(t, "Int") testGrnDBCreateTableRef(t, "Float") @@ -156,3 +181,113 @@ func TestGrnTableInsertRow(t *testing.T) { testGrnTableInsertRow(t, "GeoPoint") 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 TestGrnTableCreateColumn(t *testing.T) { + testGrnTableCreateScalarColumn(t, "Bool") + testGrnTableCreateScalarColumn(t, "Int") + testGrnTableCreateScalarColumn(t, "Float") + testGrnTableCreateScalarColumn(t, "GeoPoint") + testGrnTableCreateScalarColumn(t, "Text") + + testGrnTableCreateVectorColumn(t, "Bool") + testGrnTableCreateVectorColumn(t, "Int") + testGrnTableCreateVectorColumn(t, "Float") + testGrnTableCreateVectorColumn(t, "GeoPoint") + testGrnTableCreateVectorColumn(t, "Text") + + testGrnTableCreateScalarRefColumn(t, "Bool") + testGrnTableCreateScalarRefColumn(t, "Int") + testGrnTableCreateScalarRefColumn(t, "Float") + testGrnTableCreateScalarRefColumn(t, "GeoPoint") + testGrnTableCreateScalarRefColumn(t, "Text") + + testGrnTableCreateVectorRefColumn(t, "Bool") + testGrnTableCreateVectorRefColumn(t, "Int") + testGrnTableCreateVectorRefColumn(t, "Float") + testGrnTableCreateVectorRefColumn(t, "GeoPoint") + testGrnTableCreateVectorRefColumn(t, "Text") +} Modified: go3/gnx/options.go (+4 -0) =================================================================== --- go3/gnx/options.go 2015-04-28 19:27:43 +0900 (6be939d) +++ go3/gnx/options.go 2015-05-01 03:41:36 +0900 (4d984fb) @@ -27,6 +27,8 @@ type TableOptions struct { 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 @@ -62,6 +64,8 @@ type ColumnOptions struct { Source string } +// NewColumnOptions() creates a new ColumnOptions object with the default +// settings. func NewColumnOptions() *ColumnOptions { var options ColumnOptions return &options -------------- next part -------------- HTML����������������������������...Download