chai

package module
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 25, 2023 License: MIT Imports: 18 Imported by: 6

README

ChaiSQL

ChaiSQL is a modern embedded SQL database, focusing on flexibility and ease of use for developers. It provides a fresh alternative to traditional embedded databases by offering advanced features tailored for modern applications.

Build Status go.dev reference Status

Key Features

  • Modern SQL Experience: ChaiSQL introduces a modern twist to traditional SQL embedded databases
  • Optimized for Go: Native Go implementation with no CGO dependency.
  • Solid foundations: ChaiSQL is backed by Pebble for native Go toolchains, and RocksDB for non-Go or CGO builds (coming soon).
  • Schema Flexibility: Support for strict, partial, and schemaless table designs, catering to various data modeling needs.

Roadmap

ChaiSQL is work in progress and is not ready yet for production.

Here is a high level list of features that we want to implement in the near future, in no particular order:

  • Stable storage format (90% completed)
  • Implement most of the SQL-92 standard (detailed roadmap coming soon)
  • Provide clients for other languages (JS/TS, Python, etc) and add support for RocksDB as the backend

Installation

Install the ChaiSQL database

go install github.com/chaisql/chai

Quickstart

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/chaisql/chai"
)

func main() {
    // Create a database instance, here we'll store everything on-disk
    db, err := chai.Open("mydb")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Create a table.
    // Notice that it is possible to define constraints on nested columns.
    err = db.Exec(`
        CREATE TABLE user (
            id              INT         PRIMARY KEY,
            name            TEXT        NOT NULL UNIQUE,
            created_at      TIMESTAMP   NOT NULL,
            address (
                city        TEXT    DEFAULT "unknown",
                zipcode     TEXT
            ),
            friends         ARRAY,

            CHECK len(friends) > 0
        )
    `)

    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 `chai:"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 data
    rows, err := db.Query("SELECT id, name, age, address FROM user WHERE age >= ?", 18)
    defer rows.Close()

    err = rows.Iterate(func(r *chai.Row) error {
        err = r.Scan(...)
        err = r.StructScan(...)
        err = r.MapScan(...)
        return nil
    })
}

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

In-memory database

For in-memory operations, simply use :memory::

db, err := chai.Open(":memory:")
Using database/sql
// import chai as a blank import
import _ "github.com/chaisql/chai/driver"

// Create a sql/database DB instance
db, err := sql.Open("chai", "mydb")
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(...)

// use the driver.Scanner to scan into a struct
var u User
err = res.Scan(driver.Scanner(&u))

chai shell

The chai command line provides an SQL shell for database management:

go install github.com/chaisql/chai/cmd/chai@latest

Usage example:

# For in-memory database:
chai

# For disk-based database:
chai dirName

Contributing

Contributions are welcome!

A big thanks to our contributors!

Made with contrib.rocks.

For any questions or discussions, open an issue.

Documentation

Overview

package chai implements an embedded SQL database.

Example
package main

import (
	"fmt"

	"github.com/chaisql/chai"
)

type User struct {
	ID      int64
	Name    string
	Age     uint32
	Address struct {
		City    string
		ZipCode string
	}
}

func main() {
	// Create a database instance, here we'll store everything in memory
	db, err := chai.Open(":memory:")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	// Create a table. Chai tables are schemaless by default, you don't need to specify a schema.
	err = db.Exec("CREATE TABLE user (name text, ...)")
	if err != nil {
		panic(err)
	}

	// Create an index.
	err = db.Exec("CREATE INDEX idx_user_name ON user (name)")
	if err != nil {
		panic(err)
	}

	// Insert some data
	err = db.Exec("INSERT INTO user (id, name, age) VALUES (?, ?, ?)", 10, "foo", 15)
	if err != nil {
		panic(err)
	}

	// Insert some data using object notation
	err = db.Exec(`INSERT INTO user VALUES {id: 12, "name": "bar", age: ?, address: {city: "Lyon", zipcode: "69001"}}`, 16)
	if err != nil {
		panic(err)
	}

	// Structs can be used to describe a object
	err = db.Exec("INSERT INTO user VALUES ?, ?", &User{ID: 1, Name: "baz", Age: 100}, &User{ID: 2, Name: "bat"})
	if err != nil {
		panic(err)
	}

	// Query some objects
	stream, err := db.Query("SELECT * FROM user WHERE id > ?", 1)
	if err != nil {
		panic(err)
	}
	// always close the result when you're done with it
	defer stream.Close()

	// Iterate over the results
	err = stream.Iterate(func(r *chai.Row) error {
		var u User

		err = r.StructScan(&u)
		if err != nil {
			return err
		}

		fmt.Println(u)
		return nil
	})
	if err != nil {
		panic(err)
	}

}
Output:

{10 foo 15 { }}
{12 bar 16 {Lyon 69001}}
{2 bat 0 { }}

Index

Examples

Constants

This section is empty.

Variables

View Source
var IsNotFoundError = errs.IsNotFoundError

IsNotFoundError determines if the given error is a NotFoundError. NotFoundError is returned when the requested table, index, object or sequence doesn't exist.

Functions

func IsAlreadyExistsError

func IsAlreadyExistsError(err error) bool

IsAlreadyExistsError determines if the error is returned as a result of a conflict when attempting to create a table, an index, an row or a sequence with a name that is already used by another resource.

Types

type DB

type DB struct {
	DB *database.Database
	// contains filtered or unexported fields
}

DB represents a collection of tables.

func Open

func Open(path string) (*DB, error)

Open creates a Chai database at the given path. If path is equal to ":memory:" it will open an in-memory database, otherwise it will create an on-disk database.

func (*DB) Begin

func (db *DB) Begin(writable bool) (*Tx, error)

Begin starts a new transaction. The returned transaction must be closed either by calling Rollback or Commit.

func (*DB) Close

func (db *DB) Close() error

Close the database.

func (*DB) Exec

func (db *DB) Exec(q string, args ...any) error

Exec a query against the database without returning the result.

func (*DB) Prepare

func (db *DB) Prepare(q string) (*Statement, error)

Prepare parses the query and returns a prepared statement.

func (*DB) Query

func (db *DB) Query(q string, args ...any) (*Result, error)

Query the database and return the result. The returned result must always be closed after usage.

func (*DB) QueryRow

func (db *DB) QueryRow(q string, args ...any) (*Row, error)

QueryRow runs the query and returns the first row.

func (*DB) Update

func (db *DB) Update(fn func(tx *Tx) error) error

Update starts a read-write transaction, runs fn and automatically commits it.

func (*DB) View

func (db *DB) View(fn func(tx *Tx) error) error

View starts a read only transaction, runs fn and automatically rolls it back.

func (DB) WithContext

func (db DB) WithContext(ctx context.Context) *DB

WithContext creates a new database handle using the given context for every operation.

type Result

type Result struct {
	// contains filtered or unexported fields
}

Result of a query.

func (*Result) Close

func (r *Result) Close() (err error)

Close the result stream.

func (*Result) Columns

func (r *Result) Columns() []string

func (*Result) GetFirst

func (r *Result) GetFirst() (*Row, error)

func (*Result) Iterate

func (r *Result) Iterate(fn func(r *Row) error) error

func (*Result) MarshalJSON

func (r *Result) MarshalJSON() ([]byte, error)

func (*Result) MarshalJSONTo

func (r *Result) MarshalJSONTo(w io.Writer) error

type Row

type Row struct {
	// contains filtered or unexported fields
}

func (*Row) Clone

func (r *Row) Clone() *Row

func (*Row) Columns

func (r *Row) Columns() ([]string, error)

func (*Row) GetColumnType

func (r *Row) GetColumnType(column string) (string, error)

func (*Row) Iterate

func (r *Row) Iterate(fn func(column string, value types.Value) error) error

func (*Row) MapScan

func (r *Row) MapScan(dest map[string]any) error

func (*Row) MarshalJSON

func (r *Row) MarshalJSON() ([]byte, error)

func (*Row) Object

func (r *Row) Object() types.Object

func (*Row) Scan

func (r *Row) Scan(dest ...any) error

func (*Row) ScanColumn

func (r *Row) ScanColumn(column string, dest any) error

func (*Row) StructScan

func (r *Row) StructScan(dest any) error

type Statement

type Statement struct {
	// contains filtered or unexported fields
}

Statement is a prepared statement. If Statement has been created on a Tx, it will only be valid until Tx closes. If it has been created on a DB, it is valid until the DB closes. It's safe for concurrent use by multiple goroutines.

func (*Statement) Exec

func (s *Statement) Exec(args ...any) (err error)

Exec a query against the database without returning the result.

func (*Statement) Query

func (s *Statement) Query(args ...any) (*Result, error)

Query the database and return the result. The returned result must always be closed after usage.

func (*Statement) QueryRow

func (s *Statement) QueryRow(args ...any) (r *Row, err error)

QueryRow runs the query and returns the first row.

type Tx

type Tx struct {
	// contains filtered or unexported fields
}

Tx represents a database transaction. It provides methods for managing the collection of tables and the transaction itself. Tx is either read-only or read/write. Read-only can be used to read tables and read/write can be used to read, create, delete and modify tables.

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

tx, err := db.Begin(true)
if err != nil {
	panic(err)
}
defer tx.Rollback()

err = tx.Exec("CREATE TABLE IF NOT EXISTS user")
if err != nil {
	log.Fatal(err)
}

err = tx.Exec("INSERT INTO user (id, name, age) VALUES (?, ?, ?)", 10, "foo", 15)
if err != nil {
	log.Fatal(err)
}

r, err := tx.QueryRow("SELECT id, name, age FROM user WHERE name = ?", "foo")
if err != nil {
	panic(err)
}

var u User
err = r.StructScan(&u)
if err != nil {
	panic(err)
}

fmt.Println(u)

var id uint64
var name string
var age uint8

err = r.Scan(&id, &name, &age)
if err != nil {
	panic(err)
}

fmt.Println(id, name, age)

err = tx.Commit()
if err != nil {
	panic(err)
}
Output:

{10 foo 15 { }}
10 foo 15

func (*Tx) Commit

func (tx *Tx) Commit() error

Commit the transaction. Calling this method on read-only transactions will return an error.

func (*Tx) Exec

func (tx *Tx) Exec(q string, args ...any) (err error)

Exec a query against the database within tx and without returning the result.

func (*Tx) Prepare

func (tx *Tx) Prepare(q string) (*Statement, error)

Prepare parses the query and returns a prepared statement.

func (*Tx) Query

func (tx *Tx) Query(q string, args ...any) (*Result, error)

Query the database withing the transaction and returns the result. Closing the returned result after usage is not mandatory.

func (*Tx) QueryRow

func (tx *Tx) QueryRow(q string, args ...any) (*Row, error)

QueryRow runs the query and returns the first row.

func (*Tx) Rollback

func (tx *Tx) Rollback() error

Rollback the transaction. Can be used safely after commit.

Directories

Path Synopsis
cmd
chai Module
internal
database
Package database provides database primitives such as tables, transactions and indexes.
Package database provides database primitives such as tables, transactions and indexes.
expr/glob
Package glob implements wildcard pattern matching algorithms for strings.
Package glob implements wildcard pattern matching algorithms for strings.
kv
object
Package object defines types to manipulate and compare objects and values.
Package object defines types to manipulate and compare objects and values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL