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

Data types

Every type a field may be declared as, two kinds of absence, and the one order they all sort in.

A field with no declaration holds anything. DEFINE FIELD … TYPE narrows it to one of these, and this is the whole set:

writtenholds
anyany value at all — the same as not declaring a type
nonethe field is not present — what a read of a field a record does not have answers, and what a function answers when there is nothing to answer with
nullthe field is present and holds nothing — written deliberately, and a different fact from none; object::has tells the two apart
booltrue or false
numberany of the three numeric forms
inta signed integer
floata binary floating-point number
decimalan exact decimal — money, and anything else that must not drift
stringtext
bytesopaque bytes
durationa span of time, which may be negative
datetimea point in time
uuida universally unique identifier, as its sixteen bytes
tablea reference to a table
recorda reference to one record
arrayan ordered sequence
objecta map from field name to value
rangea span between two values
seta collection with no duplicates and no significant order
geometrya shape on the sphere — see Geometry
regexa pattern, held rather than executed
'draft' | 'published'one of a fixed set of strings, and nothing else
vector<768>an array of exactly that many numbers
DEFINE NAMESPACE prod;
USE NAMESPACE prod;
DEFINE DATABASE orders;
USE DATABASE orders;
DEFINE COLLECTION users;
DEFINE FIELD email ON users TYPE string;
DEFINE FIELD balance ON users TYPE decimal;
DEFINE FIELD seen_at ON users TYPE datetime;
DEFINE FIELD owner ON users TYPE record;
DEFINE FIELD tags ON users TYPE set;
DEFINE FIELD status ON users TYPE 'draft' | 'published';

A type name may be a reserved word, because after TYPE nothing else can appear — the grammar knows what it is reading.

Every one of them can also be written as a column, in the parentheses of a DEFINE TABLE — see Definitions.

The last two rows are the ones that are not words. Every other type is a name the grammar recognises on its own; those two carry a value — a set of permitted strings, and a width — and so they are written out rather than looked up.

A union of strings

The first of them is the one kind that is not a type of the value system but a subset of one:

DEFINE COLLECTION orders;
DEFINE FIELD state ON orders TYPE 'new' | 'paid' | 'shipped';
CREATE orders = { state: 'paid' };

TYPE string is true of a status column and says nothing; an ASSERT … IN [ … ] says it, but says it where a reader of the schema does not look. The members are held sorted and deduplicated, because a declared type is a set, and a set that remembered the order somebody typed it in would be two values for one fact.

vector<n> — an array that declares how wide it is

DEFINE COLLECTION documents;
DEFINE FIELD embedding ON documents TYPE vector<3>;
DEFINE TABLE pages (title_at vector<2>, body_at vector<4>);

Both spellings take it, because it is a type rather than a spelling.

The width is what the declaration is for. TYPE array is true of a 768-wide embedding and says nothing, so without a width a 512-wide row sits legally beside a 768-wide one and nothing refuses either. The mistake then surfaces only where the distance functions meet them — per read, long after the write, at the point furthest from the cause — and not as an error, because a vector of the wrong shape is infinitely far from everything. That is a plausible ordering rather than a complaint, so the wrong answer looks exactly like a right one.

With a width, the refusal happens at the write and names both shapes:

CREATE documents:1 = { embedding: [0.1, 0.2, 0.3] };
CREATE documents:2 = { embedding: [0.1, 0.2] };

Refused at the second statement, and the message names both shapes:

error: table documents declares embedding as vector<3>, but record 2 holds
vector<2> there

It is declared on the field, not on the table, because that is where a vector is: an array of numbers in an ordinary field, with the index built over it. The pages table above holds two of them, each held to its own width — a width declared for the table could only govern one and would leave the other exactly as unchecked as it was.

Three boundaries follow from that:

  • There is no width-less vector. An array whose length nobody declared is the array this language already has, and a word that looked checked and was not would be worse than no word at all.
  • There is no vector<0>. The only value such a field could hold is the empty array, which no distance can measure and no index will keep.
  • The width is a literal, not a parameter. A schema whose shape depended on what was bound when the declaration ran would leave the catalog with nothing single to store.

A declared width does not make the field mandatory — that is REQUIRED, and it is a separate constraint, the same rule every other kind follows.

vector stays a name a caller may use, for a reason a database of embeddings makes obvious: that is exactly where a field called vector turns up.

When the vectors are the data rather than a column of it, the store is declared in one statement instead — see Vectors.

Three numbers, not one

int, float and decimal are separate types and stay separate. They share a sort rank, so they compare semantically — 1 and 1.0 and dec 1.00 order together rather than by which type they happen to be — but a decimal is never quietly turned into a float, because that is how money loses a hundredth.

Write a decimal with dec:

DEFINE COLLECTION ledgers;
CREATE ledgers = { balance: dec 10.05 };

How deep a value nests

A value nests at most 64 containers deep — arrays, sets, objects and ranges inside one another. Deeper is refused by name wherever it arrives, a statement or a frame on the wire, because every level costs a stack frame in the parser and the decoder, and a crafted frame used to be able to end the node before anybody had signed in. A statement also nests at most 64 expressions, so the deepest literal you can write is 63 arrays inside one another:

the statement nests more than 64 expressions deep (at 71..72)

A store written by 0.9 that holds a value deeper than 64 containers cannot be read by 0.10.0-beta or anything later; flatten the value and ingest it again.

Two kinds of nothing

means
NONEthe field is not present
NULLthe field is present and holds nothing

They are different values and the language keeps them apart everywhere — in conditions, in key-value reads, and in what a fold counts. Never written and written empty are different facts, and a store that merged them could not tell you which had happened.

Neither is a small number. They do not appear in ordered comparisons at all, which is what stops "youngest customer" from quietly becoming "the record nobody filled in".

One order, across types

Every value sorts against every other, by a declared order across types rather than by an error. That is why SELECT * FROM people WHERE age > 18 answers even when one record's age is the string 'nineteen' — a string orders after every number, so the comparison has an answer instead of failing the whole read.

regex holds a pattern; it does not run one

There is no regular-expression engine in this store. A regex value round-trips as a pattern — distinguishable from a string that happens to spell one — and matching is a later capability rather than an implied one. Saying so is the difference between a type that is honest about its scope and a type that looks like a feature.

For patterns you can use today, see LIKE in Conditions; for whole words, see Full-text search.

geometry stores a shape

The type stores and returns a shape. Answering intersects, within or a distance over one is an engine, and a separate thing — a stored geometry with no index is already useful, and conflating the two is how the work stops being schedulable. Both exist; see Geometry.

What a declaration adds beyond a type

clauseeffect
REQUIREDthe field must hold a value: present, and not NULL
DEFAULT <value>what a write supplying no value uses instead
ASSERT <constraint>what the value must satisfy beyond its type
ANALYZER <name>the analyzer this field's text becomes terms by

All four are covered in Definitions, including the three shapes an ASSERT may not take.

Data types — TessariDB