# User Quickstart with immudb and immuclient

TIP

To learn interactively and to get started with immudb from the command line and with programming languages, visit the immudb Playground at https://play.codenotary.com (opens new window)

# Getting immudb running

You may download the immudb binary from the latest releases on Github (opens new window). Once you have downloaded immudb, rename it to immudb, make sure to mark it as executable, then run it. The following example shows how to obtain v1.2.4 for linux amd64:

$ wget https://github.com/vchain-us/immudb/releases/download/v1.2.4/immudb-v1.2.4-linux-amd64
$ mv immudb-v1.2.4-linux-amd64 immudb
$ chmod +x immudb

# run immudb in the foreground to see all output
$ ./immudb

# or run immudb in the background
$ ./immudb -d

Alternatively, you may pull immudb docker image from DockerHub (opens new window) and run it in a ready-to-use container:

$ docker run -d --net host -it --rm --name immudb codenotary/immudb:latest

# Connecting with immuclient

You may download the immuclient binary from the latest releases on Github (opens new window). Once you have downloaded immuclient, rename it to immuclient, make sure to mark it as executable, then run it. The following example shows how to obtain v1.2.4 for Linux amd64:

$ wget https://github.com/vchain-us/immudb/releases/download/v1.2.4/immuclient-v1.2.4-linux-amd64
$ mv immuclient-v1.2.4-linux-amd64 immuclient
$ chmod +x immuclient

Alternatively, you may pull immuclient docker image from DockerHub (opens new window) and run it in a ready-to-use container:

$ docker run -it --rm --net host --name immuclient codenotary/immuclient:latest

# Basic operations with immuclient

Before any operations can be run by immuclient, it is necessary to authenticate against the running immudb server.

When immudb is first run, it is ready to use immediately with the default database and credentials:

  • Database name: defaultdb
  • User: immudb
  • Password: immudb
  • Address: 127.0.0.1
  • Port: 3322

./immudb --help displays and describes all options available.

Running login immudb from within immuclient will use the default database name and port. All you need to supply is the user and password:

$ ./immuclient login immudb
Password: immudb

While immudb supports set and get for key-value storing and retrieving, its immutability means that we can verify the integrity of the underlying Merkle tree. To do this, we use the safeset and safeget commands. Let's try setting a value of 100 for the key balance:

$ ./immuclient safeset balance 100
tx:             2
key:            balance
value:          100
verified:       true

Then, we can immediately overwrite the key balance with a value of 9001 instead:

$ ./immuclient safeset balance 9001
tx:             3
key:            balance
value:          9001
verified:       true

If we try to retrieve the current value of key balance, we should get 9001:

$ ./immuclient safeget balance
tx:             3
key:            balance
value:          9001
verified:       true 

Note that at each step so far, the verified flag is set to true. This ensures that the Merkle tree remains consistent for each transaction.

We can show the history of transactions for key balance using the history command:

$ ./immuclient history balance
tx:             2
key:            balance
value:          100

tx:             3
key:            balance
value:          9001

# Basic operations with immuadmin

Immuadmin is the admin client for immudb. This is used for a variety of tasks such as creating and updating databases and users. Creating backups, restoring from backups etc.

To get started we need to login to immuadmin first. The admin user is the similar to the root user in MySQL etc.

$ ./immuadmin login immudb
Password: immudb

Once logged in we can create a new database using

$ ./immuadmin database create mydatabase
database 'mydatabase' {replica: false} successfully created

Switching to our newly created database. Using immuclient once you are logged in you can select the database you would like to using

$ ./immuclient use mydatabase
Now using mydatabase

# SQL operations with immuclient

In addition to a key-value store, immudb supports the relational model (SQL). For example, to create a table:

$ ./immuclient exec "CREATE TABLE people(id INTEGER, name VARCHAR, salary INTEGER, PRIMARY KEY id);"
Updated rows: 0

To insert data, use UPSERT (insert or update), which will add an entry, or overwrite it if already exists (based on the primary key):

$ ./immuclient exec "UPSERT INTO people(id, name, salary) VALUES (1, 'Joe', 10000);"
Updated rows: 1
$ ./immuclient exec "UPSERT INTO people(id, name, salary) VALUES (2, 'Bob', 30000);"
Updated rows: 1

To query the data you can use the traditional SELECT:

$ ./immuclient query "SELECT id, name, salary FROM people;"
+------------------------+--------------------------+----------------------------+
| (MYDATABASE PEOPLE ID) | (MYDATABASE PEOPLE NAME) | (MYDATABASE PEOPLE SALARY) |
+------------------------+--------------------------+----------------------------+
|                      1 | "Joe"                    |                      10000 |
|                      2 | "Bob"                    |                      30000 |
+------------------------+--------------------------+----------------------------+

If we upsert again on the primary key "1", the value for "Joe" will be overwritten:

$ ./immuclient exec "UPSERT INTO people(id, name, salary) VALUES (1, 'Joe', 20000);"
Updated rows: 1

$ ./immuclient query "SELECT id, name, salary FROM people;"
+------------------------+--------------------------+----------------------------+
| (MYDATABASE PEOPLE ID) | (MYDATABASE PEOPLE NAME) | (MYDATABASE PEOPLE SALARY) |
+------------------------+--------------------------+----------------------------+
|                      1 | "Joe"                    |                      20000 |
|                      2 | "Bob"                    |                      30000 |
+------------------------+--------------------------+----------------------------+

# Time travel

immudb is a immutable database. History is always preserved. With immudb you can travel in time!

$ ./immuclient query "SELECT id, name, salary FROM people WHERE name='Joe';"
+------------------------+--------------------------+----------------------------+
| (MYDATABASE PEOPLE ID) | (MYDATABASE PEOPLE NAME) | (MYDATABASE PEOPLE SALARY) |
+------------------------+--------------------------+----------------------------+
|                      1 | "Joe"                    |                      20000 |
+------------------------+--------------------------+----------------------------+

We can see the current transaction id using 'current':

$ ./immuclient current
database:       mydatabase
txID:           5
hash:           2986dfeb2d15e55d8189f08c2508318addabe9e773e0b6e329cf23b654cc22e7

This is the transaction id we will be using for the subsequent queries.

Eg. before the update:

$ ./immuclient query "SELECT id, name, salary FROM people BEFORE TX 5 WHERE name='Joe';"
+------------------------+--------------------------+----------------------------+
| (MYDATABASE PEOPLE ID) | (MYDATABASE PEOPLE NAME) | (MYDATABASE PEOPLE SALARY) |
+------------------------+--------------------------+----------------------------+
|                      1 | "Joe"                    |                      10000 |
+------------------------+--------------------------+----------------------------+

or even before the first time insert (guess what, it is empty!):

$ ./immuclient query "SELECT id, name, salary FROM people BEFORE TX 1 WHERE name='Joe';"
+------------------------+--------------------------+----------------------------+
| (MYDATABASE PEOPLE ID) | (MYDATABASE PEOPLE NAME) | (MYDATABASE PEOPLE SALARY) |
+------------------------+--------------------------+----------------------------+
+------------------------+--------------------------+----------------------------+

You can even TABLE a table with itself in the past. Imagine you want to see how people salary changed between two points in time:

$ ./immuclient query "SELECT peoplenow.id, peoplenow.name, peoplethen.salary, peoplenow.salary FROM people BEFORE TX 5 AS peoplethen INNER JOIN people AS peoplenow ON peoplenow.id=peoplethen.id;"
+---------------------------+-----------------------------+--------------------------------+-------------------------------+
| (MYDATABASE PEOPLENOW ID) | (MYDATABASE PEOPLENOW NAME) | (MYDATABASE PEOPLETHEN SALARY) | (MYDATABASE PEOPLENOW SALARY) |
+---------------------------+-----------------------------+--------------------------------+-------------------------------+
|                         1 | "Joe"                       |                          10000 |                         20000 |
|                         2 | "Bob"                       |                          30000 |                         30000 |
+---------------------------+-----------------------------+--------------------------------+-------------------------------+

# KV Data revisions

Whenever a new value is stored under given key, immudb saves a new revision of that data. Revision numbers start with 1 - the first value ever written to the database will have a revision number 1, the second will have 2 and so on.

When reading a value from immudb, an explicit revision number can be specified. If the provided number is greater than 0, a value for given revision is retrieved. If the provided number is less than 0, the nth previous value is retrieved.

$ ./immuclient set key value1
tx:       2
rev:      1
key:      key
value:    value1

$ ./immuclient set key value2
tx:       3
rev:      2
key:      key
value:    value2

$ ./immuclient set key value3
tx:       4
rev:      3
key:      key
value:    value3

$ ./immuclient get key@1  # Get the key at the first revision
tx:       2
rev:      1
key:      key
value:    value1

$ ./immuclient get key@-1  # Get the key at the previous revision
tx:       3
rev:      2
key:      key
value:    value2

The immuclient tool has also the possibility to restore a previous revision for given key.

$ ./immuclient restore key@-2
tx:       5
rev:      4
key:      key
value:    value1

# Querying for keys containing revision separator

In some cases, the key can already contain the @ character reserved for key separator. In such case there are few options to read such key. The revision separator can be changed to any other string that is not part of the key. Also because immuclient will only scan the last occurrence of the revision separator, an explicit 0th revision can be set to read the current value behind such key.

$ ./immuclient set some@email.address active
tx:       2
rev:      1
key:      some@email.address
value:    active

# Change the revision separator with environment variable
$ IMMUCLIENT_REVISION_SEPARATOR="###" ./immuclient get some@email.address
tx:     2
key:    some@email.address
value:  active
hash:   138033b5a89438758fdb3481ba0dc44816d550749f799223587cb30cd7eadf5a

# Disable / change the revision separator through command-line argument
$ ./immuclient get --revision-separator="" some@email.address
tx:       2
rev:      1
key:      some@email.address
value:    active

# Always use the revision number, use 0 for the current value
$ ./immuclient get some@email.address@0
tx:       2
rev:      1
key:      some@email.address
value:    active