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

Back to archive index

susumu.yata null+****@clear*****
Wed Apr 1 08:37:07 JST 2015


susumu.yata	2015-04-01 08:37:07 +0900 (Wed, 01 Apr 2015)

  New Revision: 225f0b390e92e9431e4c53995b3388adbe9d3ad8
  https://github.com/groonga/grnxx/commit/225f0b390e92e9431e4c53995b3388adbe9d3ad8

  Message:
    Gnx: add a new version.

  Added files:
    go2/gnx/gnx.go
    go2/gnxTest.go

  Added: go2/gnx/gnx.go (+594 -0) 100644
===================================================================
--- /dev/null
+++ go2/gnx/gnx.go    2015-04-01 08:37:07 +0900 (dc16fca)
@@ -0,0 +1,594 @@
+package gnx
+
+/*
+#cgo pkg-config: groonga
+#include <groonga.h>
+#include <stdlib.h>
+*/
+import "C"
+
+import (
+	"encoding/json" // FIXME
+	"fmt"
+	"io/ioutil"
+	"math"
+	"os"
+	"strconv"
+	"strings"
+	"unsafe"
+)
+
+// -- Data types --
+
+type Bool uint8
+type Int int64
+type Float float64
+//type GeoPoint struct {
+//	Latitude  int32
+//	Longitude int32
+//}
+type Text []byte
+type BoolVector []Bool
+type IntVector []Int
+type FloatVector []Float
+//type GeoPointVector []GeoPoint
+type TextVector []Text
+
+const (
+	FALSE = Bool(0)
+	TRUE  = Bool(3)
+)
+
+type Valuer interface {
+	IsNA() bool
+}
+
+func NABool() Bool {
+	return Bool(1)
+}
+func NAInt() Int {
+	return Int(math.MinInt64)
+}
+func NAFloat() Float {
+	return Float(math.NaN())
+}
+//func NAGeoPoint() GeoPoint {
+//	return GeoPoint{math.MinInt32, math.MinInt32}
+//}
+func NAText() Text {
+	return nil
+}
+func NABoolVector() BoolVector {
+	return nil
+}
+func NAIntVector() IntVector {
+	return nil
+}
+func NAFloatVector() FloatVector {
+	return nil
+}
+//func NAGeoPointVector() GeoPointVector {
+//	return nil
+//}
+func NATextVector() TextVector {
+	return nil
+}
+
+func (this Bool) IsNA() bool {
+	return this == 1
+}
+func (this Int) IsNA() bool {
+	return this == math.MinInt64
+}
+func (this Float) IsNA() bool {
+	return math.IsNaN(float64(this))
+}
+//func (this GeoPoint) IsNA() bool {
+//	return this.Latitude == math.MinInt32
+//}
+func (this Text) IsNA() bool {
+	return this == nil
+}
+func (this BoolVector) IsNA() bool {
+	return this == nil
+}
+func (this IntVector) IsNA() bool {
+	return this == nil
+}
+func (this FloatVector) IsNA() bool {
+	return this == nil
+}
+//func (this GeoPointVector) IsNA() bool {
+//	return this == nil
+//}
+func (this TextVector) IsNA() bool {
+	return this == nil
+}
+
+// -- GroongaDB --
+
+type GroongaDB struct {
+	ctx *C.grn_ctx
+}
+
+var groongaInitCount = 0
+
+func DisableGroongaInitCount() {
+	groongaInitCount = -1
+}
+
+func initGroonga() error {
+	switch groongaInitCount {
+	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)
+		}
+	}
+	groongaInitCount++
+	return nil
+}
+
+func finGroonga() error {
+	switch groongaInitCount {
+	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)
+		}
+	}
+	groongaInitCount--
+	return nil
+}
+
+func CreateGroongaDB(path string) (*GroongaDB, error) {
+	if err := initGroonga(); err != nil {
+		return nil, err
+	}
+	ctx := C.grn_ctx_open(0)
+	if ctx == nil {
+		finGroonga()
+		return nil, fmt.Errorf("grn_ctx_open() failed")
+	}
+	cPath := C.CString(path)
+	defer C.free(unsafe.Pointer(cPath))
+	if db := C.grn_db_create(ctx, cPath, nil); db == nil {
+		C.grn_ctx_close(ctx)
+		finGroonga()
+		message := C.GoString(&ctx.errbuf[0])
+		return nil, fmt.Errorf("grn_db_create() failed: err = %s", message)
+	}
+	return &GroongaDB{ctx}, nil
+}
+
+// Create a DB in a temporary directory.
+//
+// Example:
+// db, dir, err := CreateTempGroonggnx.aDB("", "gnx")
+// if err != nil {
+//   log.Fatalln(err)
+// }
+// defer os.RemoveAll(dir)
+// defer db.Close()
+// ...
+func createTempGroongaDB(dir, prefix string) (*GroongaDB, string, error) {
+	tempDir, err := ioutil.TempDir(dir, prefix)
+	if err != nil {
+		return nil, "", err
+	}
+	db, err := CreateGroongaDB(tempDir + "/db")
+	if err != nil {
+		os.RemoveAll(tempDir)
+		return nil, "", err
+	}
+	return db, tempDir, nil
+}
+
+func OpenGroongaDB(path string) (*GroongaDB, error) {
+	if err := initGroonga(); err != nil {
+		return nil, err
+	}
+	ctx := C.grn_ctx_open(0)
+	if ctx == nil {
+		finGroonga()
+		return nil, fmt.Errorf("grn_ctx_open() failed")
+	}
+	cPath := C.CString(path)
+	defer C.free(unsafe.Pointer(cPath))
+	if db := C.grn_db_open(ctx, cPath); db == nil {
+		C.grn_ctx_close(ctx)
+		finGroonga()
+		message := C.GoString(&ctx.errbuf[0])
+		return nil, fmt.Errorf("grn_db_create() failed: err = %s", message)
+	}
+	return &GroongaDB{ctx}, nil
+}
+
+func (db *GroongaDB) Close() error {
+	if db.ctx == nil {
+		return nil
+	}
+	if rc := C.grn_ctx_close(db.ctx); rc != C.GRN_SUCCESS {
+		return fmt.Errorf("grn_ctx_close() failed: rc = %d", rc)
+	}
+	db.ctx = nil
+	finGroonga()
+	return nil
+}
+
+func (db *GroongaDB) Send(command string) error {
+	cCommand := C.CString(command)
+	defer C.free(unsafe.Pointer(cCommand))
+	rc := C.grn_ctx_send(db.ctx, cCommand, C.uint(len(command)), 0)
+	switch {
+	case rc != C.GRN_SUCCESS:
+		return fmt.Errorf(
+			"grn_ctx_send() failed: rc = %d, err = %s",
+			rc, C.GoString(&db.ctx.errbuf[0]))
+	case db.ctx.rc != C.GRN_SUCCESS:
+		return fmt.Errorf(
+			"grn_ctx_send() failed: ctx.rc = %d, err = %s",
+			db.ctx.rc, C.GoString(&db.ctx.errbuf[0]))
+	}
+	return nil
+}
+
+func (db *GroongaDB) 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:
+		return nil, fmt.Errorf(
+			"grn_ctx_recv() failed: rc = %d, err = %s",
+			rc, C.GoString(&db.ctx.errbuf[0]))
+	case db.ctx.rc != C.GRN_SUCCESS:
+		return nil, fmt.Errorf(
+			"grn_ctx_recv() failed: ctx.rc = %d, err = %s",
+			db.ctx.rc, C.GoString(&db.ctx.errbuf[0]))
+	}
+	result := C.GoBytes(unsafe.Pointer(resultBuffer), C.int(resultLength))
+	return result, nil
+}
+
+func (db *GroongaDB) Query(command string) ([]byte, error) {
+	err := db.Send(command)
+	if err != nil {
+		_, _ = db.Recv()
+		return nil, err
+	}
+	return db.Recv()
+}
+
+func (db *GroongaDB) load(
+	tableName string, columnNames []string, records [][]Valuer) (int, error) {
+	jsonRecords, err := json.Marshal(records)
+	if err != nil {
+		return 0, err
+	}
+	command := fmt.Sprintf("load --table '%s' --columns '%s' --values '%s'",
+		tableName, strings.Join(columnNames, ","), string(jsonRecords))
+	bytes, err := db.Query(command)
+	if err != nil {
+		return 0, err
+	}
+	return strconv.Atoi(string(bytes))
+}
+
+func (db *GroongaDB) loadMap(
+	tableName string, recordMaps []map[string]Valuer) (int, error) {
+	jsonRecords, err := json.Marshal(recordMaps)
+	if err != nil {
+		return 0, err
+	}
+	command := fmt.Sprintf("load --table '%s' --values '%s'",
+		tableName, string(jsonRecords))
+	bytes, err := db.Query(command)
+	if err != nil {
+		return 0, err
+	}
+	return strconv.Atoi(string(bytes))
+}
+
+func (db *GroongaDB) loadC(
+	tableName string, columnNames []string,
+	columnarRecords []interface{}) (int, error) {
+	numValues := -1
+	for _, columnarValues := range columnarRecords {
+		var thisLen int
+		switch values := columnarValues.(type) {
+		case []Bool:
+			thisLen = len(values)
+		case []Int:
+			thisLen = len(values)
+		case []Float:
+			thisLen = len(values)
+		case []Text:
+			thisLen = len(values)
+		case []BoolVector:
+			thisLen = len(values)
+		case []IntVector:
+			thisLen = len(values)
+		case []FloatVector:
+			thisLen = len(values)
+		case []TextVector:
+			thisLen = len(values)
+		}
+		if numValues == -1 {
+			numValues = thisLen
+		} else if thisLen != numValues {
+			return 0, fmt.Errorf("length conflict: numValues = %d, thisLen = %d",
+				numValues, thisLen)
+		}
+	}
+	if numValues == -1 {
+		numValues = 0
+	}
+
+	// TODO: Use cgo for JSON encoding.
+	records := make([][]Valuer, numValues)
+	for i := 0; i < numValues; i++ {
+		records[i] = make([]Valuer, len(columnNames))
+	}
+	for i, columnarValues := range columnarRecords {
+		switch values := columnarValues.(type) {
+		case []Bool:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []Int:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []Float:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []Text:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []BoolVector:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []IntVector:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []FloatVector:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		case []TextVector:
+			for j, value := range values {
+				records[j][i] = value
+			}
+		}
+	}
+	return db.load(tableName, columnNames, records)
+}
+
+func (db *GroongaDB) loadCMap(
+	tableName string, columnarRecordsMap map[string]interface{}) (int, error) {
+	columnNames := make([]string, len(columnarRecordsMap))
+	columnarRecords := make([]interface{}, len(columnarRecordsMap))
+	i := 0
+	for columnName, columnarValues := range columnarRecordsMap {
+		columnNames[i] = columnName
+		columnarRecords[i] = columnarValues
+		i++
+	}
+	return db.loadC(tableName, columnNames, columnarRecords)
+}
+
+// -- DB --
+
+type DB struct {
+	groongaDBs []*GroongaDB
+}
+
+func CreateDB(path string, n int) (*DB, error) {
+	if n <= 0 {
+		return nil, fmt.Errorf("invalid parameter: n = %d", n)
+	}
+	groongaDBs := make([]*GroongaDB, n)
+	for i := 0; i < n; i++ {
+		dbPath := path
+		if i != 0 {
+			dbPath += strconv.Itoa(i)
+		}
+		groongaDB, err := CreateGroongaDB(dbPath)
+		if err != nil {
+			for j := 0; j < i; j++ {
+				groongaDBs[j].Close()
+			}
+			return nil, err
+		}
+		groongaDBs[i] = groongaDB
+	}
+	return &DB{groongaDBs}, nil
+}
+
+func CreateTempDB(dir, prefix string, n int) (*DB, string, error) {
+	if n <= 0 {
+		return nil, "", fmt.Errorf("invalid parameter: n = %d", n)
+	}
+	tempDir, err := ioutil.TempDir(dir, prefix)
+	if err != nil {
+		return nil, "", err
+	}
+	db, err := CreateDB(tempDir+"/db", n)
+	if err != nil {
+		os.RemoveAll(tempDir)
+		return nil, "", err
+	}
+	return db, tempDir, nil
+}
+
+func OpenDB(path string) (*DB, error) {
+	var groongaDBs []*GroongaDB
+	for i := 0; ; i++ {
+		dbPath := path
+		if i != 0 {
+			dbPath += strconv.Itoa(i)
+		}
+		groongaDB, err := OpenGroongaDB(dbPath)
+		if err != nil {
+			if i == 0 {
+				return nil, err
+			}
+			break
+		}
+		groongaDBs = append(groongaDBs, groongaDB)
+	}
+	return &DB{groongaDBs}, nil
+}
+
+func (db *DB) Close() error {
+	for i := 0; i < len(db.groongaDBs); i++ {
+		db.groongaDBs[i].Close()
+	}
+	return nil
+}
+
+func (db *DB) GroongaQuery(i int, command string) ([]byte, error) {
+	if (i < 0) || (i >= len(db.groongaDBs)) {
+		return nil, fmt.Errorf("invalid DB: i = %d", i)
+	}
+	return db.groongaDBs[i].Query(command)
+}
+
+func (db *DB) checkTableName(tableName string) error {
+	for _, c := range tableName {
+		if (c != '_') && ((c < 'A') && (c > 'Z')) && ((c < 'a') && (c > 'Z')) {
+			return fmt.Errorf("Invalid rune in table name")
+		}
+	}
+	return nil
+}
+
+func (db *DB) checkColumnName(columnName string) error {
+	for _, c := range columnName {
+		if (c != '_') && ((c < 'A') && (c > 'Z')) && ((c < 'a') && (c > 'Z')) {
+			return fmt.Errorf("Invalid rune in column name")
+		}
+	}
+	return nil
+}
+
+func (db *DB) loadN(
+	tableName string, columnNames []string, records [][]Valuer) (int, error) {
+	return 0, nil
+}
+
+func (db *DB) loadMapN(
+	tableName string, records []map[string]Valuer) (int, error) {
+	return 0, nil
+}
+
+func (db *DB) loadCN(
+	tableName string, columnNames []string, records []interface{}) (int, error) {
+	return 0, nil
+}
+
+func (db *DB) loadCMapN(
+	tableName string, records map[string]interface{}) (int, error) {
+	return 0, nil
+}
+
+func (db *DB) Load(
+	tableName string, columnNames []string, records [][]Valuer) (int, error) {
+	// Check arguments.
+	if err := db.checkTableName(tableName); err != nil {
+		return 0, err
+	}
+	for _, columnName := range columnNames {
+		if err := db.checkColumnName(columnName); err != nil {
+			return 0, err
+		}
+	}
+	for _, record := range records {
+		if len(record) != len(columnNames) {
+			return 0, fmt.Errorf(
+				"length conflict: len(columnNames) = %d, len(record) = %d",
+				len(columnNames), len(record))
+		}
+	}
+
+	if len(db.groongaDBs) == 1 {
+		return db.groongaDBs[0].load(tableName, columnNames, records)
+	} else {
+		return db.loadN(tableName, columnNames, records)
+	}
+}
+
+func (db *DB) LoadMap(
+	tableName string, recordMaps []map[string]Valuer) (int, error) {
+	// Check arguments.
+	if err := db.checkTableName(tableName); err != nil {
+		return 0, err
+	}
+	for _, recordMap := range recordMaps {
+		for columnName, _ := range recordMap {
+			if err := db.checkColumnName(columnName); err != nil {
+				return 0, err
+			}
+		}
+	}
+
+	if len(db.groongaDBs) == 1 {
+		return db.groongaDBs[0].loadMap(tableName, recordMaps)
+	} else {
+		return db.loadMapN(tableName, recordMaps)
+	}
+}
+
+func (db *DB) LoadC(
+	tableName string, columnNames []string,
+	columnarRecords []interface{}) (int, error) {
+	// Check arguments.
+	if err := db.checkTableName(tableName); err != nil {
+		return 0, err
+	}
+	for _, columnName := range columnNames {
+		if err := db.checkColumnName(columnName); err != nil {
+			return 0, err
+		}
+	}
+	if len(columnNames) != len(columnarRecords) {
+		return 0, fmt.Errorf(
+			"length conflict: len(columnNames) = %d, len(columnarRecords) = %d",
+			len(columnNames), len(columnarRecords))
+	}
+
+	if len(db.groongaDBs) == 1 {
+		return db.groongaDBs[0].loadC(tableName, columnNames, columnarRecords)
+	} else {
+		return db.loadCN(tableName, columnNames, columnarRecords)
+	}
+}
+
+func (db *DB) LoadCMap(
+	tableName string, columnarRecordsMap map[string]interface{}) (int, error) {
+	// Check arguments.
+	if err := db.checkTableName(tableName); err != nil {
+		return 0, err
+	}
+	for columnName, _ := range columnarRecordsMap {
+		if err := db.checkColumnName(columnName); err != nil {
+			return 0, err
+		}
+	}
+
+	if len(db.groongaDBs) == 1 {
+		return db.groongaDBs[0].loadCMap(tableName, columnarRecordsMap)
+	} else {
+		return db.loadCMapN(tableName, columnarRecordsMap)
+	}
+}

  Added: go2/gnxTest.go (+84 -0) 100644
===================================================================
--- /dev/null
+++ go2/gnxTest.go    2015-04-01 08:37:07 +0900 (ee2ae2a)
@@ -0,0 +1,84 @@
+package main
+
+import "./gnx"
+import "fmt"
+import "log"
+import "os"
+
+func main() {
+	db, dir, err := gnx.CreateTempDB("", "gnxConsole", 1)
+	if err != nil {
+		log.Println(err)
+		return
+	}
+	defer os.RemoveAll(dir)
+	defer db.Close()
+
+	_, err = db.GroongaQuery(0, "table_create Table TABLE_NO_KEY")
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	_, err = db.GroongaQuery(0, "column_create Table Value COLUMN_SCALAR Int32")
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	{
+		records := make([][]gnx.Valuer, 3)
+		records[0] = []gnx.Valuer{gnx.Int(10)}
+		records[1] = []gnx.Valuer{gnx.Int(20)}
+		records[2] = []gnx.Valuer{gnx.Int(30)}
+		count, err := db.Load("Table", []string{"Value"}, records)
+		if err != nil {
+			log.Println(err)
+			return
+		}
+		fmt.Println("count:", count)
+	}
+
+	{
+		recordMaps := make([]map[string]gnx.Valuer, 3)
+		recordMaps[0] = map[string]gnx.Valuer{"Value":gnx.Int(100)}
+		recordMaps[1] = map[string]gnx.Valuer{"Value":gnx.Int(200)}
+		recordMaps[2] = map[string]gnx.Valuer{"Value":gnx.Int(300)}
+		count, err := db.LoadMap("Table", recordMaps)
+		if err != nil {
+			log.Println(err)
+			return
+		}
+		fmt.Println("count:", count)
+	}
+
+	{
+		columnarRecords := make([]interface{}, 1)
+		columnarRecords[0] = []gnx.Int{-10,-20,-30}
+		count, err := db.LoadC("Table", []string{"Value"}, columnarRecords)
+		if err != nil {
+			log.Println(err)
+			return
+		}
+		fmt.Println("count:", count)
+	}
+
+	{
+		columnarRecordsMap := make(map[string]interface{})
+		columnarRecordsMap["Value"] = []gnx.Int{-100,-200,-300}
+		count, err := db.LoadCMap("Table", columnarRecordsMap)
+		if err != nil {
+			log.Println(err)
+			return
+		}
+		fmt.Println("count:", count)
+	}
+
+	command := "select Table --limit -1"
+	jsonBytes, err := db.GroongaQuery(0, command)
+	if err != nil {
+		log.Println(err)
+		return
+	}
+	fmt.Println("result:", string(jsonBytes))
+}
-------------- next part --------------
HTML����������������������������...
Download 



More information about the Groonga-commit mailing list
Back to archive index