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.
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.