[Groonga-commit] groonga/grnxx at d0d4cbd [master] Gnx: add a new version.

アーカイブの一覧に戻る

susumu.yata null+****@clear*****
Tue Apr 28 19:27:43 JST 2015


susumu.yata	2015-04-28 19:27:43 +0900 (Tue, 28 Apr 2015)

  New Revision: d0d4cbd11a172bb9f7169bc7b45514856c7d189a
  https://github.com/groonga/grnxx/commit/d0d4cbd11a172bb9f7169bc7b45514856c7d189a

  Message:
    Gnx: add a new version.

  Added files:
    go3/gnx/gnx.go
    go3/gnx/gnx_cgo.c
    go3/gnx/gnx_cgo.h
    go3/gnx/gnx_test.go
    go3/gnx/grn.go
    go3/gnx/grn_cgo.c
    go3/gnx/grn_cgo.h
    go3/gnx/grn_test.go
    go3/gnx/grnxx.go
    go3/gnx/grnxx_cgo.cpp
    go3/gnx/grnxx_cgo.h
    go3/gnx/options.go

  Added: go3/gnx/gnx.go (+275 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/gnx.go    2015-04-28 19:27:43 +0900 (7198302)
@@ -0,0 +1,275 @@
+package gnx
+
+/*
+#cgo pkg-config: groonga
+#include "gnx_cgo.h"
+*/
+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)
+	}
+}
+
+// -- DB --
+
+type DB struct {
+	*GrnDB
+	*GrnxxDB
+	tables map[string]*Table
+}
+
+func newDB(grnDB *GrnDB, grnxxDB *GrnxxDB) *DB {
+	return &DB{grnDB, grnxxDB, make(map[string]*Table)}
+}
+
+func CreateDB(path string) (*DB, error) {
+	grnDB, err := CreateGrnDB(path)
+	if err != nil {
+		return nil, err
+	}
+	return newDB(grnDB, nil), nil
+}
+
+func OpenDB(path string) (*DB, error) {
+	grnDB, err := OpenGrnDB(path)
+	if err != nil {
+		return nil, err
+	}
+	return newDB(grnDB, nil), nil
+}
+
+func (db *DB) Close() error {
+	var grnErr error
+	var grnxxErr error
+	if db.GrnDB != nil {
+		grnErr = db.GrnDB.Close()
+	}
+	if db.GrnxxDB != nil {
+		grnxxErr = db.GrnxxDB.Close()
+	}
+	if grnErr != nil {
+		return grnErr
+	}
+	return grnxxErr
+}
+
+func (db *DB) CreateTable(name string, options *TableOptions) (*Table, error) {
+	// TODO
+	return nil, fmt.Errorf("not supported yet")
+}
+
+func (db *DB) RemoveTable(name string) error {
+	// TODO
+	return fmt.Errorf("not supported yet")
+}
+
+func (db *DB) RenameTable(name, newName string) error {
+	// TODO
+	return fmt.Errorf("not supported yet")
+}
+
+func (db *DB) FindTable(name string) (*Table, error) {
+	if table, ok := db.tables[name]; ok {
+		return table, nil
+	}
+	// TODO
+	return nil, fmt.Errorf("not supported yet")
+}
+
+func (db *DB) FindRow(tableName string, key interface{}) (Int, error) {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return NullInt(), err
+	}
+	return table.FindRow(key)
+}
+
+func (db *DB) InsertRow(tableName string, key interface{}) (bool, Int, error) {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return false, NullInt(), err
+	}
+	return table.InsertRow(key)
+}
+
+func (db *DB) CreateColumn(tableName, columnName, valueType string,
+	options *ColumnOptions) (*Column, error) {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return nil, err
+	}
+	return table.CreateColumn(columnName, valueType, options)
+}
+
+func (db *DB) RemoveColumn(tableName, columnName string) error {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return err
+	}
+	return table.RemoveColumn(columnName)
+}
+
+func (db *DB) RenameColumn(tableName, columnName, newColumnName string) error {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return err
+	}
+	return table.RenameColumn(columnName, newColumnName)
+}
+
+func (db *DB) FindColumn(tableName, columnName string) (*Column, error) {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return nil, err
+	}
+	return table.FindColumn(columnName)
+}
+
+func (db *DB) SetValue(
+	tableName, columnName string, id Int, value interface{}) error {
+	table, err := db.FindTable(tableName)
+	if err != nil {
+		return err
+	}
+	return table.SetValue(columnName, id, value)
+}
+
+// -- Table --
+
+type Table struct {
+	*GrnTable
+	*GrnxxTable
+	columns map[string]*Column
+}
+
+func (table *Table) FindRow(key interface{}) (Int, error) {
+	// TODO
+	return NullInt(), fmt.Errorf("not supported yet")
+}
+
+func (table *Table) InsertRow(key interface{}) (bool, Int, error) {
+	// TODO
+	return false, NullInt(), fmt.Errorf("not supported yet")
+}
+
+func (table *Table) CreateColumn(
+	name, valueType string, options *ColumnOptions) (*Column, error) {
+	// TODO
+	return nil, fmt.Errorf("not supported yet")
+}
+
+func (table *Table) RemoveColumn(name string) error {
+	// TODO
+	return fmt.Errorf("not supported yet")
+}
+
+func (table *Table) RenameColumn(name, newName string) error {
+	// TODO
+	return fmt.Errorf("not supported yet")
+}
+
+func (table *Table) FindColumn(name string) (*Column, error) {
+	if column, ok := table.columns[name]; ok {
+		return column, nil
+	}
+	// TODO
+	return nil, fmt.Errorf("not supported yet")
+}
+
+func (table *Table) SetValue(
+	columnName string, id Int, value interface{}) error {
+	column, err := table.FindColumn(columnName)
+	if err != nil {
+		return err
+	}
+	return column.SetValue(id, value)
+}
+
+// -- Column --
+
+type Column struct {
+	*GrnColumn
+	*GrnxxColumn
+}
+
+func (column *Column) SetValue(id Int, value interface{}) error {
+	// TODO
+	return fmt.Errorf("not suported yet")
+}

  Added: go3/gnx/gnx_cgo.c (+1 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/gnx_cgo.c    2015-04-28 19:27:43 +0900 (3957c21)
@@ -0,0 +1 @@
+#include "gnx_cgo.h"

  Added: go3/gnx/gnx_cgo.h (+8 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/gnx_cgo.h    2015-04-28 19:27:43 +0900 (970bd6b)
@@ -0,0 +1,8 @@
+#ifndef GNX_CGO_H
+#define GNX_CGO_H
+
+#include <stdlib.h>
+
+#include <groonga.h>
+
+#endif  // GNX_CGO_H

  Added: go3/gnx/gnx_test.go (+1 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/gnx_test.go    2015-04-28 19:27:43 +0900 (fa907c6)
@@ -0,0 +1 @@
+package gnx

  Added: go3/gnx/grn.go (+534 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grn.go    2015-04-28 19:27:43 +0900 (13c492e)
@@ -0,0 +1,534 @@
+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 {
+	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
+		}
+	}
+	table := newGrnTable(db, obj, name, keyType, keyTable)
+	db.tables[name] = table
+	return table, nil
+}
+
+// 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
+	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 {
+	var table GrnTable
+	table.db = db
+	table.obj = obj
+	table.name = name
+	table.keyType = keyType
+	table.keyTable = keyTable
+	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 {
+		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 an empty row.
+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 an empty row.
+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 an empty row.
+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 an empty row.
+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 an empty row.
+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 record.
+// 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())
+	}
+}
+
+// -- GrnColumn --
+
+type GrnColumn struct {
+	obj *C.grn_obj
+}

  Added: go3/gnx/grn_cgo.c (+129 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grn_cgo.c    2015-04-28 19:27:43 +0900 (e2b7523)
@@ -0,0 +1,129 @@
+#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 (!key_info->ref_table) {
+          key_info->ref_table = table;
+        }
+        break;
+      }
+      case GRN_TABLE_NO_KEY: {
+        return GRN_TRUE;
+      }
+      default: {
+        return GRN_FALSE;
+      }
+    }
+  }
+  return GRN_FALSE;
+}
+
+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: {
+      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);
+}

  Added: go3/gnx/grn_cgo.h (+59 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grn_cgo.h    2015-04-28 19:27:43 +0900 (886f814)
@@ -0,0 +1,59 @@
+#ifndef GRN_CGO_H
+#define GRN_CGO_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <groonga.h>
+
+typedef struct {
+  const char *ptr;
+  size_t size;
+} grn_cgo_text;
+
+// 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_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);
+
+#endif  // GRN_CGO_H

  Added: go3/gnx/grn_test.go (+158 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grn_test.go    2015-04-28 19:27:43 +0900 (e9b99e4)
@@ -0,0 +1,158 @@
+package gnx
+
+import (
+	"io/ioutil"
+	"math/rand"
+	"os"
+	"strconv"
+	"testing"
+)
+
+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
+}
+
+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)
+	}
+}
+
+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
+}
+
+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 testGrnDBCreateTableRef(t *testing.T, keyType string) {
+	options := NewTableOptions()
+	options.TableType = PatTable
+	options.KeyType = keyType
+	dirPath, _, db, _ := createTempGrnTable(t, "Table", options)
+	defer removeTempGrnDB(t, dirPath, db)
+
+	options.KeyType = "Table"
+	_, err := db.CreateTable("Table2", options)
+	if err != nil {
+		t.Fatalf("GrnDB.CreateTable() failed: %v", err)
+	}
+}
+
+func TestGrnDBCreateTable(t *testing.T) {
+	dirPath, _, db, _ := createTempGrnTable(t, "Table", nil)
+	removeTempGrnDB(t, dirPath, db)
+
+	testGrnDBCreateTableWithKey(t, "Bool")
+	testGrnDBCreateTableWithKey(t, "Int")
+	testGrnDBCreateTableWithKey(t, "Float")
+	testGrnDBCreateTableWithKey(t, "GeoPoint")
+	testGrnDBCreateTableWithKey(t, "Text")
+
+	testGrnDBCreateTableRef(t, "Bool")
+	testGrnDBCreateTableRef(t, "Int")
+	testGrnDBCreateTableRef(t, "Float")
+	testGrnDBCreateTableRef(t, "GeoPoint")
+	testGrnDBCreateTableRef(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 TestGrnTableInsertRow(t *testing.T) {
+	testGrnTableInsertRow(t, "")
+	testGrnTableInsertRow(t, "Bool")
+	testGrnTableInsertRow(t, "Int")
+	testGrnTableInsertRow(t, "Float")
+	testGrnTableInsertRow(t, "GeoPoint")
+	testGrnTableInsertRow(t, "Text")
+}

  Added: go3/gnx/grnxx.go (+28 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grnxx.go    2015-04-28 19:27:43 +0900 (93222c5)
@@ -0,0 +1,28 @@
+package gnx
+
+import (
+	"fmt"
+)
+
+// -- GrnxxDB --
+
+type GrnxxDB struct {
+	// TODO
+}
+
+func (db *GrnxxDB) Close() error {
+	// TODO
+	return fmt.Errorf("not supported yet")
+}
+
+// -- GrnxxTable --
+
+type GrnxxTable struct {
+	// TODO
+}
+
+// -- GrnxxColumn --
+
+type GrnxxColumn struct {
+	// TODO
+}

  Added: go3/gnx/grnxx_cgo.cpp (+1 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grnxx_cgo.cpp    2015-04-28 19:27:43 +0900 (0fd5c26)
@@ -0,0 +1 @@
+#include "grnxx_cgo.h"

  Added: go3/gnx/grnxx_cgo.h (+6 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/grnxx_cgo.h    2015-04-28 19:27:43 +0900 (90f8462)
@@ -0,0 +1,6 @@
+#ifndef GRNXX_CGO_H
+#define GRNXX_CGO_H
+
+// TODO
+
+#endif  // GRNXX_CGO_H

  Added: go3/gnx/options.go (+68 -0) 100644
===================================================================
--- /dev/null
+++ go3/gnx/options.go    2015-04-28 19:27:43 +0900 (6be939d)
@@ -0,0 +1,68 @@
+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
+}
+
+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
+}
+
+func NewColumnOptions() *ColumnOptions {
+	var options ColumnOptions
+	return &options
+}
-------------- next part --------------
HTML����������������������������...
ダウンロード 



More information about the Groonga-commit mailing list
アーカイブの一覧に戻る