Document-oriented, embedded SQL database

Genji

Genji

Document-oriented, embedded, SQL database

Table of contents

Introduction

Build Status go.dev reference Slack channel Fuzz

Genji is a schemaless database that allows running SQL queries on documents.

Checkout the SQL documentation, the Go doc and the usage example in the README to get started quickly.

⚠️ Genji's API is still unstable: Database compatibility is not guaranteed before reaching v1.0.0

Features

  • Optional schemas: Genji tables are schemaless, but it is possible to add constraints on any field to ensure the coherence of data within a table.
  • Multiple Storage Engines: It is possible to store data on disk or in ram, but also to choose between B-Trees and LSM trees. Genji relies on BoltDB and Badger to manage data.
  • Transaction support: Read-only and read/write transactions are supported by default.
  • SQL and Documents: Genji mixes the best of both worlds by combining powerful SQL commands with JSON.
  • Easy to use, easy to learn: Genji was designed for simplicity in mind. It is really easy to insert and read documents of any shape.
  • Compatible with the database/sql package

Installation

Install the Genji database

go get github.com/genjidb/genji

Usage

There are two ways of using Genji, either by using Genji's API or by using the database/sql package.

Using Genji's API

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/genjidb/genji"
    "github.com/genjidb/genji/document"
)

func main() {
    // Create a database instance, here we'll store everything on-disk using the BoltDB engine
    db, err := genji.Open("my.db")
    if err != nil {
        log.Fatal(err)
    }
    // Don't forget to close the database when you're done
    defer db.Close()

    // Attach context, e.g. (*http.Request).Context().
    db = db.WithContext(context.Background())

    // Create a table. Schemas are optional, you don't need to specify one if not needed
    err = db.Exec("CREATE TABLE user")

    // Create an index
    err = db.Exec("CREATE INDEX idx_user_name ON test (name)")

    // Insert some data
    err = db.Exec("INSERT INTO user (id, name, age) VALUES (?, ?, ?)", 10, "Foo1", 15)

    // Supported values can go from simple integers to richer data types like lists or documents
    err = db.Exec(`
    INSERT INTO user (id, name, age, address, friends)
    VALUES (
        11,
        'Foo2',
        20,
        {"city": "Lyon", "zipcode": "69001"},
        ["foo", "bar", "baz"]
    )`)

    // Go structures can be passed directly
    type User struct {
        ID              uint
        Name            string
        TheAgeOfTheUser float64 `genji:"age"`
        Address         struct {
            City    string
            ZipCode string
        }
    }

    // Let's create a user
    u := User{
        ID:              20,
        Name:            "foo",
        TheAgeOfTheUser: 40,
    }
    u.Address.City = "Lyon"
    u.Address.ZipCode = "69001"

    err = db.Exec(`INSERT INTO user VALUES ?`, &u)

    // Query some documents
    res, err := db.Query("SELECT id, name, age, address FROM user WHERE age >= ?", 18)
    // always close the result when you're done with it
    defer res.Close()

    // Iterate over the results
    err = res.Iterate(func(d document.Document) error {
        // When querying an explicit list of fields, you can use the Scan function to scan them
        // in order. Note that the types don't have to match exactly the types stored in the table
        // as long as they are compatible.
        var id int
        var name string
        var age int32
        var address struct {
            City    string
            ZipCode string
        }

        err = document.Scan(d, &id, &name, &age, &address)
        if err != nil {
            return err
        }

        fmt.Println(id, name, age, address)

        // It is also possible to scan the results into a structure
        var u User
        err = document.StructScan(d, &u)
        if err != nil {
            return err
        }

        fmt.Println(u)

        // Or scan into a map
        var m map[string]interface{}
        err = document.MapScan(d, &m)
        if err != nil {
            return err
        }

        fmt.Println(m)
        return nil
    })
}

Using database/sql

// import Genji as a blank import
import _ "github.com/genjidb/genji/sql/driver"

// Create a sql/database DB instance
db, err := sql.Open("genji", "my.db")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

// Then use db as usual
res, err := db.ExecContext(...)
res, err := db.Query(...)
res, err := db.QueryRow(...)

Engines

Genji currently supports storing data in BoltDB, Badger and in-memory.

Using the BoltDB engine

import (
    "log"

    "github.com/genjidb/genji"
)

func main() {
    db, err := genji.Open("my.db")
    defer db.Close()
}

Using the memory engine

import (
    "log"

    "github.com/genjidb/genji"
)

func main() {
    db, err := genji.Open(":memory:")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

Using the Badger engine

First install the module

go get github.com/genjidb/genji/engine/badgerengine
import (
    "context"
    "log"

    "github.com/genjidb/genji"
    "github.com/genjidb/genji/engine/badgerengine"
    "github.com/dgraph-io/badger/v2"
)

func main() {
    // Create a badger engine
    ng, err := badgerengine.NewEngine(badger.DefaultOptions("mydb"))
    if err != nil {
        log.Fatal(err)
    }

    // Pass it to genji
    db, err := genji.New(context.Background(), ng)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

Genji shell

The genji command line provides an SQL shell that can be used to create, modify and consult Genji databases.

Make sure the Genji command line is installed:

go get github.com/genjidb/genji/cmd/genji

Example:

# Opening an in-memory database:
genji

# Opening a BoltDB database:
genji my.db

# Opening a Badger database:
genji --badger pathToData

Contributing

Contributions are welcome!

See ARCHITECTURE.md and CONTRIBUTING.md.

Thank you, contributors!

If you have any doubt, join the Gophers Slack channel or open an issue.

Owner
Genji
Document-oriented, embedded, SQL database
Genji
Comments
  • Database is deadlocking

    Database is deadlocking

    Hi, I've been experimenting with Genji v0.8.0 in one of my apps:

    https://github.com/simpleiot/simpleiot/blob/feature-genji2/db/genji/genji.go

    After I click around in the frontend a bit, API calls start timing out.

    I instrumented the db calls, and learned that if one of the calls starts before the previous one finishes, the API calls start timing out soon after that.

    Am I doing anything obviously wrong? I'm using boltdb backend and thought boltdb was thread safe. Do I need to wrap all db operations in transactions?

  • TIKV as the distributed storage engine

    TIKV as the distributed storage engine

  • Automate releases

    Automate releases

    This PR automates publishing new releases.

    To publish a new release, just git push release. The new version is automatically inferred based on API changes. Alternatively, it’s possible to override this behavior (e.g. if we have breaking changes in cmd/genji but not in Go API) by pushing to release-v0.13.0 branch, or manually dispatching a workflow with version inputs.

    The workflow then creates version bump commits for Genji’s submodules and tags them. In the end it creates a draft release with changelog (additionally using CHANGELOG.md if it exists). When GitHub release is published, another CI workflow kicks in that builds and uploads binaries as release assets.

    Additionally, version bump commits are only reachable from tags so it should be safe to dispatch the workflow on main branch.

    That said, this should also eliminate the need to manually manage unreleased versions in go.mod.

    workflow

    Build matrix
    Build matrix

    Closes #269

  • Support AUTO_INCREMENT.

    Support AUTO_INCREMENT.

    This a proposal draft for #43.

    SQL Server seems good because it proposes a customizable AUTO_INCREMENT with a "natural language".

    ### Default value
    CREATE TABLE foo (id INTEGER AUTO_INCREMENT);
    INSERT INTO foo VALUES {"a": "foo"};
    SELECT * FROM foo;
    { "a": "foo", "id": 1}
    
    ### Set a start index and an increment value;
    ###`AUTO_INCREMENT(startIndex, incBy)`
    ###The first value of the sequence start at 10 and the next is incremented by 5
    CREATE TABLE bar (id INTEGER AUTO_INCREMENT(10, 5));
    INSERT INTO bar VALUES {"a": "bar"};
    INSERT INTO bar VALUES {"a": "baz"};
    SELECT * FROM bar;
    { "a": "bar", "id": 10 }
    {  "a": "baz", "id": 15 }
    

    AUTO_INCREMENT have to be applied only on number value type. INTEGER and DOUBLE

    genji> CREATE TABLE foo(bar TEXT AUTO_INCREMENT);
    genji> found text, expected integer, double at line 1, char 27
    

    About ALTER TABLE table_name AUTO_INCREMENT=100, If we keep it like that, we should be able to write the both following syntaxes:

    ###For default value
    ALTER TABLE foo AUTO_INCREMENT=100;
    
    ### And this even if the creation was with default value
    ALTER TABLE foo AUTO_INCREMENT(100, 10);
    

    Thank you for your feedbacks.

  • tableInfoStore should be scoped to transaction

    tableInfoStore should be scoped to transaction

    Currently database.Database holds a reference to tableInfoStore that is shared between all transactions which, among other things, may create/rename/alter/drop tables. This violates the transaction isolation.

    Make sure INSERT is indeed isolated.
    package main
    
    import (
    	"github.com/dgraph-io/badger/v2"
    	"github.com/genjidb/genji"
    	"github.com/genjidb/genji/engine/badgerengine"
    )
    
    func main() {
    	ng, err := badgerengine.NewEngine(badger.DefaultOptions("").WithInMemory(true))
    	if err != nil { panic(err) }
    	db, err := genji.New(ng)
    	if err != nil { panic(err) }
    
    	err = db.Exec("CREATE TABLE tb (id INTEGER PRIMARY KEY)")
    	if err != nil { panic(err) }
    
    	// Does panic with "duplicate document" error unless committed.
    	for i := 0; i < 2; i++ {
    		tx, err := db.Begin(true)
    		if err != nil { panic(err) }
    		defer tx.Rollback()
    		err = tx.Exec("INSERT INTO tb (id) VALUES (?)", 42)
    		if err != nil { panic(err) }
    	}
    }
    
    Make sure that CREATE INDEX is indeed isolated.
    package main
    
    import (
    	"github.com/dgraph-io/badger/v2"
    	"github.com/genjidb/genji"
    	"github.com/genjidb/genji/engine/badgerengine"
    )
    
    func main() {
    	ng, err := badgerengine.NewEngine(badger.DefaultOptions("").WithInMemory(true))
    	if err != nil { panic(err) }
    	db, err := genji.New(ng)
    	if err != nil { panic(err) }
    
    	err = db.Exec("CREATE TABLE tb")
    	if err != nil { panic(err) }
    
    	// Does panic with "index already exists" error unless committed.
    	for i := 0; i < 2; i++ {
    		tx, err := db.Begin(true)
    		if err != nil { panic(err) }
    		err = tx.Exec("CREATE UNIQUE INDEX idx ON tb(id)")
    		if err != nil { panic(err) }
    	}
    }
    
    Reproduce the bug. Panics with "table already exists" error.
    package main
    
    import (
    	"github.com/dgraph-io/badger/v2"
    	"github.com/genjidb/genji"
    	"github.com/genjidb/genji/engine/badgerengine"
    )
    
    func main() {
    	ng, err := badgerengine.NewEngine(badger.DefaultOptions("").WithInMemory(true))
    	if err != nil { panic(err) }
    	db, err := genji.New(ng)
    	if err != nil { panic(err) }
    
    	// We never commit the transaction, so these changes should be isolated
    	// from other concurrent (but not necessarily parallel) transactions.
    	for i := 0; i < 2; i++ {
    		tx, err := db.Begin(true)
    		if err != nil { panic(err) }
    		err = tx.Exec("CREATE TABLE tb")
    		if err != nil { panic(err) }
    	}
    }
    

    Tangentially related to #210 since it needs concurrent transactions.

  • SQL driver doesn't support timestamp (time.Time)

    SQL driver doesn't support timestamp (time.Time)

    I have seen an issue about supporting time.Time in sql driver: https://github.com/genjidb/genji/issues/154.

    But it seems that time.Time still doesn't work. Parser doesn't recognize keyword TIMESTAMP (not surprisingly, no changes was made in driver in corresponding MR for that issue).

    package main
    
    import (
    	"database/sql"
    	"fmt"
    	"time"
    
    	_ "github.com/genjidb/genji/sql/driver"
    )
    
    func main() {
    	db, err := sql.Open("genji", ":memory:")
    	if err != nil {
    		panic(err)
    	}
    	defer db.Close()
    
    	_, err = db.Exec(`CREATE TABLE foo (created_at TIMESTAMP NOT NULL)`)
    	if err != nil {
    		panic(err)
    	}
           //...
    

    Prints:

    panic: found TIMESTAMP, expected ) at line 1, char 30
    

    Scanning time.Time stored as TEXT doesn't work either:

    package main
    
    import (
    	"database/sql"
    	"fmt"
    	"time"
    
    	_ "github.com/genjidb/genji/sql/driver"
    )
    
    func main() {
    	db, err := sql.Open("genji", ":memory:")
    	if err != nil {
    		panic(err)
    	}
    	defer db.Close()
    
    	_, err = db.Exec(`CREATE TABLE foo (created_at TEXT NOT NULL)`)
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = db.Exec(`INSERT INTO foo (created_at) VALUES (?)`, time.Now().UTC())
    	if err != nil {
    		panic(err)
    	}
    
    	rows, err := db.Query(`SELECT created_at FROM foo`)
    	if err != nil {
    		panic(err)
    	}
    	defer rows.Close()
    	for rows.Next() {
    		var createdAt time.Time
    		if err := rows.Scan(&createdAt); err != nil {
    			panic(err)
    		}
    		fmt.Printf("foo found: (%v)", createdAt)
    	}
    	if err := rows.Err(); err != nil {
    		panic(err)
    	}
    }
    

    Prints:

    panic: sql: Scan error on column index 0, name "created_at": unsupported Scan, storing driver.Value type string into type *time.Time
    

    Is this a bug or I misusing sql driver?

  • Add support for context.Context

    Add support for context.Context

    This PR adds context.Context support in Genji, starting with the engine package and then fixing compile errors everywhere. It doesn’t introduce any cancellation behavior though—that should be a separate, smaller, PR.

    • engine.Iterator usage now requires a it.Err() check after the loop.

      it := st.Iterator(engine.IteratorOptions{})
      defer it.Close()
      
      for it.Seek(ctx, nil); it.Valid(); it.Next(ctx) {
      	…
      }
      if err := it.Err(); err != nil {
      	…
      }
      

      Notice that Seek and Next now accept a context.Context parameter. If an error occurs, Valid returns false.

    • database.Table no longer directly implements document.Iterator since iteration may be I/O bound.

      // Before
      func (*Table) Iterate(func(d document.Document) error) error
      // After
      func (*Table) Iterator(context.Context) document.IteratorFunc
      func (*Table) Iterate(context.Context, func(d document.Document) error) error
      

    Closes #224 and #206

  • Merge key and document packages

    Merge key and document packages

    This PR removes the key package and moves its content to 3 different locations:

    • Add MarshalBinary, UnmarshalBinary and Append to Value type. These methods encode values without type information.
    • Add ValueEncoder type which can different types in the same namespace while keeping ordering between types. It is the type used for untyped indexes.
    • Add a pkg/nsb package for naturally sorted binary representation of supported variables.

    This was done because we are going to need the ValueEncoder from within the document package, which currently provokes a cyclic dependency with the key package.

  • Add Bitcask backend

    Add Bitcask backend

    KV Store: https://github.com/prologic/bitcask

    Currently the master branch is (really) unstable as I'm basically breaking everything with a sledgehammer. The engine package though (the one that contains interfaces that need to be implemented) wasn't touch in the master branch so it should be good.

    Also, I'd really like to avoid having too many dependencies with Genji (especially because of the store implementations) so I think it should be better if the Bitcask package had its own go.mod. In next release, every backends will have their own go.mod so users will be able to choose explicitly the store they want to use. This also means that you'll have to base your work on the v0.1.0 tag, which is fine I suppose, as I said there aren't that many changes in the engine on the master branch.

    Regarding tests, there is a engine/enginetest package that contains importable tests that make sure your backend is compatible, you can take a look at the other backends if you need examples.

  • msgpack codec encodes all integers type into int64 rather than picking the right type

    msgpack codec encodes all integers type into int64 rather than picking the right type

    What version of Genji are you using?

    The main branch.

    $ genji version
    Genji v0.11.0
    Genji CLI (devel)
    

    Does this issue reproduce with the latest release?

    Yes

    What did you do?

    (Preamble, I know that all sql INT* types are just document.IntegerValue under the hood, I'm talking at codec level here).

    While reading the msgpack codec, my eye was caught by https://github.com/genjidb/genji/blob/8e580748aedb60ec007df65013936287ec5dcc21/document/encoding/msgpack/codec.go#L100-L111 which says that the int types will be encoded using they msg pack counterpart, ie int8 -> int8, but the code didn't look like it handles those case.

    While digging, I found out that:

    • all integers end up as int64 anyway when reaching the codec
    • even if fed an int8, EncodeValue will end up using an int64 in msgpack because it uses case document.IntegerValue: return e.enc.EncodeInt64(v.V.(int64)) under the hood.

    ➡️ It looks to me that the comment is wrong and that the int64 encoding for all ints is actually on purpose as per what the decoder does here: https://github.com/genjidb/genji/blob/main/document/encoding/msgpack/codec.go#L221

    ❓ @asdine Is that a deliberate choice here? Is it related to how msgpack work somehow? Otherwise it looks like it's a rather low hanging fruit to have more compact integer storage.


    package main
    
    import (
    	"bytes"
    	"context"
    	"fmt"
    	"os"
    
    	"github.com/genjidb/genji"
    	"github.com/genjidb/genji/engine"
    	"github.com/genjidb/genji/engine/boltengine"
    	mp "github.com/vmihailenco/msgpack/v5"
    )
    
    func main() {
    	// so we always get the same store id every run, see line 52
    	_ = os.Remove("test.db")
    
    	db, err := genji.Open("test.db")
    	if err != nil {
    		panic(err)
    	}
    
    	err = db.Exec("CREATE TABLE foo ( a INTEGER )")
    	if err != nil {
    		panic(err)
    	}
    
    	err = db.Exec("INSERT INTO foo (a) VALUES (?)", 2)
    	if err != nil {
    		panic(err)
    	}
    
    	err = db.Close()
    	if err != nil {
    		panic(err)
    	}
    
    	ng1, err := boltengine.NewEngine("test.db", 0o600, nil)
    	if err != nil {
    		panic(err)
    	}
    
    	etx, err := ng1.Begin(context.Background(), engine.TxOptions{})
    	if err != nil {
    		panic(err)
    	}
    
    	//  func (t *tableInfoStore) Insert(tx *Transaction, tableName string, info *TableInfo) error {
    	//		(...)
    	//      fmt.Println(info.storeName)
    	store, err := etx.GetStore([]byte{116, 1})
    	if err != nil {
    		panic(err)
    	}
    
    	it := store.Iterator(engine.IteratorOptions{})
    	it.Seek(nil)
    
    	item := it.Item()
    
    	k := item.Key()
    
    	b, err := store.Get(k)
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(b)
    
    	dec := mp.NewDecoder(bytes.NewReader(b))
    
    	ii, err := dec.DecodeInterface()
    	if err != nil {
    		panic(err)
    	}
    
            // ➡️ ### What did you expect to see? ⬅️
    	fmt.Printf("%$v\n", ii)
    }
    
    

    What did you expect to see?

    [129 161 97 2]
    map[%!$(string=a):%!$(int8=2)]v
    

    What did you see instead?

    [129 161 97 211 0 0 0 0 0 0 0 2]
    map[%!$(string=a):%!$(int64=2)]v
    

    What Go version and environment are you using?

    $ go version
    go version go1.15.8 darwin/amd64
    
    go env Output
    $ go env
    GO111MODULE="on"
    GOARCH="amd64"
    GOBIN="/Users/tech/code/bin"
    GOCACHE="/Users/tech/Library/Caches/go-build"
    GOENV="/Users/tech/Library/Application Support/go/env"
    GOEXE=""
    GOFLAGS=""
    GOHOSTARCH="amd64"
    GOHOSTOS="darwin"
    GOINSECURE=""
    GOMODCACHE="/Users/tech/code/pkg/mod"
    GONOPROXY=""
    GONOSUMDB=""
    GOOS="darwin"
    GOPATH="/Users/tech/code"
    GOPRIVATE=""
    GOPROXY="https://proxy.golang.org,direct"
    GOROOT="/Users/tech/.asdf/installs/golang/1.15.8/go"
    GOSUMDB="sum.golang.org"
    GOTMPDIR=""
    GOTOOLDIR="/Users/tech/.asdf/installs/golang/1.15.8/go/pkg/tool/darwin_amd64"
    GCCGO="gccgo"
    AR="ar"
    CC="clang"
    CXX="clang++"
    CGO_ENABLED="1"
    GOMOD="/Users/tech/code/src/github.com/genjidb/play/go.mod"
    CGO_CFLAGS="-g -O2"
    CGO_CPPFLAGS=""
    CGO_CXXFLAGS="-g -O2"
    CGO_FFLAGS="-g -O2"
    CGO_LDFLAGS="-g -O2"
    PKG_CONFIG="pkg-config"
    GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/xl/dfsx6nzs4cb2y40m555mlfjh0000gn/T/go-build230471881=/tmp/go-build -gno-record-gcc-switches -fno-common"
    

  • Add dump command

    Add dump command

    $ genji dump -h
    NAME:
       genji dump - Dump a database or a table as a text file.
    
    USAGE:
       genji dump [options] dbpath
    
    DESCRIPTION:
      The dump command can dump a database as a text file.
    
      By default, the content of the database is sent to the standard output:
    
      $ genji dump my.db
      CREATE TABLE foo;
      ...
    
      It is possible to specify a list of tables to output:
    
      $ genji dump -t foo -f bar my.db
    
      The dump command can also write directly into a file:
    
      $ genji dump -f dump.sql my.db
    
    OPTIONS:
       --table value, -t value   name of the table to dump, it must already exist. Defaults to all tables.
       --file value, -f value       name of the file to output to. Defaults to STDOUT
       --help, -h                show help (default: false)
    
  • Why return a error sometimes when insert or update a record?

    Why return a error sometimes when insert or update a record?

    What version of Genji are you using?

    v0.15.1

    What did you do?

    data := UserLog{
       ...
    }
    func InsertData(db *genji.DB, sql string, data ...interface{}) error {
    	err := db.Exec(sql, data...)
    	return err
    }
    err := InsertData(db, "INSERT INTO USERLOG VALUES ?", &data)
    

    What did you expect to see?

    error is nil. It is not returned error all the time but sometimes. The error is not always the same, it seems like random string,

    Wraps: (2) "ER\xda\x05tableO\xda" not found
    Wraps: (2) "SER\x05\x02\x15J\fTER" not found
    

    Usually error will disappear if I restart program. I don't know why, I just insert a record use struct.

    What did you see instead?

    (1) attached stack trace
      -- stack trace:
      | github.com/genjidb/genji/internal/database.(*catalogCache).Get
      |     github.com/genjidb/[email protected]/internal/database/catalog.go:792
      | github.com/genjidb/genji/internal/database.(*Catalog).GetSequence
      |     github.com/genjidb/[email protected]/internal/database/catalog.go:509
      | github.com/genjidb/genji/internal/database.(*Table).generateKey
      |     github.com/genjidb/[email protected]/internal/database/table.go:186
      | github.com/genjidb/genji/internal/database.(*Table).Insert
      |     github.com/genjidb/[email protected]/internal/database/table.go:41
      | github.com/genjidb/genji/internal/stream/table.(*InsertOperator).Iterate.func1
      |     github.com/genjidb/[email protected]/internal/stream/table/insert.go:46
      | github.com/genjidb/genji/internal/stream/table.(*ValidateOperator).Iterate.func1
      |     github.com/genjidb/[email protected]/internal/stream/table/validate.go:67
      | github.com/genjidb/genji/internal/stream/docs.(*EmitOperator).Iterate
      |     github.com/genjidb/[email protected]/internal/stream/docs/emit.go:39
      | github.com/genjidb/genji/internal/stream/table.(*ValidateOperator).Iterate
      |     github.com/genjidb/[email protected]/internal/stream/table/validate.go:41
      | github.com/genjidb/genji/internal/stream/table.(*InsertOperator).Iterate
      |     github.com/genjidb/[email protected]/internal/stream/table/insert.go:30
      | github.com/genjidb/genji/internal/stream.(*DiscardOperator).Iterate
      |     github.com/genjidb/[email protected]/internal/stream/stream.go:129
      | github.com/genjidb/genji/internal/stream.(*Stream).Iterate
      |     github.com/genjidb/[email protected]/internal/stream/stream.go:34
      | github.com/genjidb/genji/internal/query/statement.(*StreamStmtIterator).Iterate
      |     github.com/genjidb/[email protected]/internal/query/statement/stream.go:70
      | github.com/genjidb/genji/internal/query/statement.(*Result).Iterate
      |     github.com/genjidb/[email protected]/internal/query/statement/statement.go:59
      | github.com/genjidb/genji.(*Result).Iterate
      |     github.com/genjidb/[email protected]/db.go:366
      | github.com/genjidb/genji.(*Statement).Exec
      |     github.com/genjidb/[email protected]/db.go:353
      | github.com/genjidb/genji.(*DB).Exec
      |     github.com/genjidb/[email protected]/db.go:160
      | runtime.goexit
      |     runtime/asm_amd64.s:1594
    Wraps: (2) "64\x05\xe6\x02\xda\x04NODE" not found
    Error types: (1) *withstack.withStack (2) *errors.NotFoundError
    
  • Import json file

    Import json file

    • Added support to import json files
    • Separated csv insert logic into InsertCSV function and moved it to "cmd/genji/dbutil/insert.go"

    Refers #444

  • Consider switching to a different K/V store

    Consider switching to a different K/V store

    Proposal

    Currently, Genji uses Pebble as its K/V store. Pebble is maintained by CockroachDB, and they have said many times that it is meant to match the roadmap of CockroachDB itself, and that features outside of that are a low priority. As I mentioned in #485, Pebble prevents Genji from compiling on 32-bit platforms such as GOARCH=386 and GOARCH=arm. It also fails to compile on riscv64. This is because Pebble uses build tags to detect 64-bit architectures, and every architecture has to be added explicitly to each file that uses them. The developers of Pebble have stated that this is not a priority for them.

    I propose a switch to a different K/V store that compiles on more platforms, such as https://github.com/etcd-io/bbolt, or the addition of an interface to allow users to choose whichever implementation they want.

    Motivation

    Nearly all other databases compile and run on 32-bit platforms. This change would alleviate all platform restrictions imposed by Pebble. There are many 32-bit machines and even some 64-bit machines that weren't able to use Genji before but will be able to if this is implemented.

  • Genji does not compile for 32-bit platforms

    Genji does not compile for 32-bit platforms

    What version of Genji are you using?

    $ genji version
    Genji v0.15.1
    Genji CLI v0.15.1
    

    What did you do?

    Attempted to compile a program importing GenjiDB with GOARCH=386

    What did you expect to see?

    Successful compilation

    What did you see instead?

    # github.com/cockroachdb/pebble/internal/batchskl
    ../../.go/pkg/mod/github.com/cockroachdb/[email protected]/internal/batchskl/skl.go:310:18: maxNodesSize (untyped int constant 4294967295) overflows int
    ../../.go/pkg/mod/github.com/cockroachdb/[email protected]/internal/batchskl/skl.go:320:16: cannot use maxNodesSize (untyped int constant 4294967295) as int value in assignment (overflows)
    

    This appears to be an issue with Pebble (cockroachdb/pebble#2120)

  • Column name

    Column name "value" causes syntax error

    What version of Genji are you using?

    0.15.1
    

    What did you do?

    First, I created a table with no schema:

    CREATE TABLE IF NOT EXISTS settings
    

    Then I tried to query the table:

    res, err := db.Query(`SELECT name, value FROM settings`)
    

    What did you expect to see?

    Given I had not populated any rows, an empty result set.

    What did you see instead?

    parsing error:

    found VALUE, expected  at line 1, char 14
    

    Given I change the column name in the select to val it works. This is what makes me think it's an issue with the parser. I believe the error returned could be improved as well. It looks like the expected value passed to the message cannot render as string.

Go fearless SQL. Sqlvet performs static analysis on raw SQL queries in your Go code base.

Sqlvet Sqlvet performs static analysis on raw SQL queries in your Go code base to surface potential runtime errors at build time. Feature highlights:

Dec 19, 2022
Database Abstraction Layer (dbal) for Go. Support SQL builder and get result easily (now only support mysql)

godbal Database Abstraction Layer (dbal) for go (now only support mysql) Motivation I wanted a DBAL that No ORM、No Reflect、Concurrency Save, support S

Nov 17, 2022
A Go (golang) package that enhances the standard database/sql package by providing powerful data retrieval methods as well as DB-agnostic query building capabilities.

ozzo-dbx Summary Description Requirements Installation Supported Databases Getting Started Connecting to Database Executing Queries Binding Parameters

Dec 31, 2022
LBADD: An experimental, distributed SQL database
LBADD: An experimental, distributed SQL database

LBADD Let's build a distributed database. LBADD is an experimental distributed SQL database, written in Go. The goal of this project is to build a dat

Nov 29, 2022
Mocking your SQL database in Go tests has never been easier.

copyist Mocking your SQL database in Go tests has never been easier. The copyist library automatically records low-level SQL calls made during your te

Dec 19, 2022
Additions to Go's database/sql for super fast performance and convenience. (fork of gocraft/dbr)

dbr (fork of gocraft/dbr) provides additions to Go's database/sql for super fast performance and convenience. Getting Started // create a connection (

Dec 31, 2022
Go library for accessing multi-host SQL database installations

hasql hasql provides simple and reliable way to access high-availability database setups with multiple hosts. Status hasql is production-ready and is

Dec 28, 2022
Bluge, will this document match this query?

sour Will this bluge.Document match this bluge.Query? This library allows you to efficiently answer this question. s := sour.New(bluge.InMemoryOnlyCo

Dec 16, 2022
A Golang library for using SQL.

dotsql A Golang library for using SQL. It is not an ORM, it is not a query builder. Dotsql is a library that helps you keep sql files in one place and

Dec 27, 2022
a golang library for sql builder

Gendry gendry is a Go library that helps you operate database. Based on go-sql-driver/mysql, it provides a series of simple but useful tools to prepar

Dec 26, 2022
SQL builder and query library for golang

__ _ ___ __ _ _ _ / _` |/ _ \ / _` | | | | | (_| | (_) | (_| | |_| | \__, |\___/ \__, |\__,_| |___/ |_| goqu is an expressive SQL bu

Dec 30, 2022
SQL query builder for Go

GoSQL Query builder with some handy utility functions. Documentation For full documentation see the pkg.go.dev or GitBook. Examples // Open database a

Dec 12, 2022
Type safe SQL builder with code generation and automatic query result data mapping
Type safe SQL builder with code generation and automatic query result data mapping

Jet Jet is a complete solution for efficient and high performance database access, consisting of type-safe SQL builder with code generation and automa

Jan 6, 2023
Write your SQL queries in raw files with all benefits of modern IDEs, use them in an easy way inside your application with all the profit of compile time constants

About qry is a general purpose library for storing your raw database queries in .sql files with all benefits of modern IDEs, instead of strings and co

Dec 25, 2022
Type safe SQL query builder and struct mapper for Go

sq (Structured Query) ?? ?? sq is a code-generated, type safe query builder and struct mapper for Go. ?? ?? Documentation • Reference • Examples This

Dec 19, 2022
Fast SQL query builder for Go

sqlf A fast SQL query builder for Go. sqlf statement builder provides a way to: Combine SQL statements from fragments of raw SQL and arguments that ma

Dec 23, 2022
💥 A lightweight DSL & ORM which helps you to write SQL in Go.
💥 A lightweight DSL & ORM which helps you to write SQL in Go.

sqlingo is a SQL DSL (a.k.a. SQL Builder or ORM) library in Go. It generates code from the database and lets you write SQL queries in an elegant way.

Jan 2, 2023
Fluent SQL generation for golang

sqrl - fat-free version of squirrel - fluent SQL generator for Go Non thread safe fork of squirrel. The same handy fluffy helper, but with extra lette

Dec 16, 2022
Fluent SQL generation for golang

Squirrel is "complete". Bug fixes will still be merged (slowly). Bug reports are welcome, but I will not necessarily respond to them. If another fork

Jan 6, 2023