Embedding the SQL engine in your application

Using the Go client SDK means you are connecting to a immudb database server. There are cases where you don’t want a separate server but embed immudb directly in the same application process, as a library.

immudb provides you a immutable embedded SQL engine which keeps all history, is tamper-proof and can travel in time.

immudb already provides an embeddable key-value store in the embedded Embedding the SQL engine in your application - 图1 (opens new window) package.

The SQL engine is mounted on top of the embedded key value store, and requires two stores to be initialized, one for the data, and one for the catalog (schema).

Make sure to import:

  1. "github.com/codenotary/immudb/embedded/store"
  2. "github.com/codenotary/immudb/pkg/sql"

Create stores for the data and catalog:

  1. catalogStore, err := store.Open("catalog", store.DefaultOptions())
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. dataStore, err := store.Open("sqldata", store.DefaultOptions())
  6. if err != nil {
  7. log.Fatal(err)
  8. }

And now you can create the SQL engine, passing both stores and a key prefix:

  1. engine, err := sql.NewEngine(catalogStore, dataStore, []byte("sql"))
  2. if err != nil {
  3. log.Fatal(err)
  4. }

Create and use a database:

  1. _, err = engine.ExecStmt("CREATE DATABASE db1")
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. _, err = engine.ExecStmt("USE DATABASE db1")
  6. if err != nil {
  7. log.Fatal(err)
  8. }

The engine has an API to execute statements and queries. To execute an statement:

  1. _, err = engine.ExecStmt("CREATE TABLE journal (id INTEGER, date VARCHAR, creditaccount INTEGER, debitaccount INTEGER amount INTEGER, description VARCHAR, PRIMARY KEY id)")
  2. if err != nil {
  3. log.Fatal(err)
  4. }

Queries can be executed using QueryStmt and you can pass a map of parameters to substitute, and whether the engine should wait for indexing:

  1. r, err = engine.QueryStmt("SELECT id, date, creditaccount, debitaccount, amount, description FROM journal WHERE amount > @value", map[string]interface{}{"value": 100}, true)

To iterate over a result set r, just fetch rows until there are no more entries. Every row has a Values member you can index to access the column:

  1. for {
  2. row, err := r.Read()
  3. if err == sql.ErrNoMoreRows {
  4. break
  5. }
  6. if err != nil {
  7. log.Fatal(err)
  8. }
  9. // do something with row.Values
  10. }

And that is all you need. If you need to change options like where things get stored by default, you can do that in the underlying store objects that the SQL engine is using.