TessariDBDocs
  • 0.34.2-betalatest
  • 0.34.1-beta
  • 0.34.0-beta
  • 0.33.4-beta
  • 0.33.3-beta
  • 0.33.2-beta
  • 0.33.1-beta
  • 0.33.0-beta
  • 0.32.0-beta

Your first store

From an empty prompt to a record you can read back, and the three names you have to choose before you can write anything.

This assumes tessaridb is on your path. Install is where it comes from, and it is worth reading first rather than after: the repository is private while this is a pre-release, so getting the binary starts with an email.

Start with nothing at all:

$ tessaridb
tessaridb> 

That is a complete store, in memory, ready to be written to. It is lost when you leave, which is exactly what you want while you are finding out what the language does.

Statements end with ; and may span as many lines as they like, so pasting a record with its fields on separate lines works the way you would expect. Leave with .exit, or Ctrl-D.

Three names before the first record

A record does not live in a table alone. It lives in a table, in a database, in a namespace — and you say which before anything else.

namespace prod database orders table users users:1 { name: 'ada' } users:2 { name: 'grace' }
Three levels of tenancy, and the records inside them.
DEFINE NAMESPACE prod;
USE NAMESPACE prod;
DEFINE DATABASE orders;
USE DATABASE orders;
DEFINE TABLE users (name string, email string);

A table says what its columns are, and refuses the ones it does not. That is what the parentheses are for, and a DEFINE TABLE without them is refused — a table with nothing declared has nothing to be strict about, so the statement cannot tell whether you meant a table or a collection, which is the word for records that carry fields nobody declared.

DEFINE creates; USE selects. They are separate because creating a namespace and working in one are different acts, and a statement that did both would make a typo in a name silently create a second empty tenancy that looks like yours until you go looking for your data.

A USE stays in force for the rest of the connection. It is session state, not a per-statement argument, which is why a script's first two lines are almost always the two USEs.

Write something

CREATE users = { name: 'ada', email: 'ada@example.com' };
CREATE users = { name: 'grace', email: 'grace@example.com' };
1
2

You did not have to invent an id. The table keeps a counter, and each write answers with the number it used — so those two records are users:1 and users:2. Name one yourself, as CREATE users:ada = { … }, when the id already means something outside the store.

users:1 is the record's address: the table and the id, written together. The id is not a column inside the record — which is why reading it back needs no condition and no index:

SELECT * FROM users:1;

The prompt answers in TessariQL's own syntax, so what comes out can be pasted back in:

1: { email: 'ada@example.com', name: 'ada' }
(1 record(s), via record)

The via record is the node telling you the access path it took — here the address was known, so nothing was searched. It says via index when a condition met an index and via scan when it read the table, and those are the three a read like this one uses; the store names eight in all. It is a fact you can observe rather than a cost model you have to reason about. See Records.

Keep it

Everything above was in memory. Give the same command a path and it is a store on disk instead:

$ tessaridb ./data

The directory is created if it is not there. There is nothing to initialise, no initdb step, and no configuration file — the arguments are the configuration, so a store that started is a store that is running the settings you can see.

Run a file instead of typing

$ tessaridb ./data -f setup.tessariql
$ tessaridb ./data -e 'SELECT * FROM users;'
$ echo 'SELECT * FROM users;' | tessaridb ./data

A file and a pipe behave the same way, and both differ from the prompt in one respect that matters: a refusal stops a script, where at a prompt it prints its message and the next statement runs. A script is a sequence somebody expected to complete, and carrying on past a failed step is how a half-applied migration happens.

Stopping is not undoing, though, and the difference is worth knowing before you run one: what a halted script leaves in the store, and how to make it all-or-nothing, is A script is not a transaction.

A script may end with a comment, and a ; inside a comment is not the end of a statement:

SELECT * FROM users -- the filter is not optional
WHERE name = 'ada';

Next

  • Records — the rest of create, read, update, delete.
  • Definitions — types, indexes, and what a field may demand.
  • Connecting — when the store should outlive the prompt.