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

Parameters

A supplied value never becomes part of a statement, and being remote does not give that back.

A parameter is written $name and stands where a value stands:

DEFINE NAMESPACE prod;
USE NAMESPACE prod;
DEFINE DATABASE orders;
USE DATABASE orders;
DEFINE COLLECTION users;
CREATE users = { name: 'ada', email: 'ada@example.com' };
$ tessaridb ./data --param who="'ada'" -e 'SELECT * FROM users WHERE name = $who;'
1: { email: 'ada@example.com', name: 'ada' }
(1 record(s), via scan)

The value is bound to the name after the statement has been read. Nothing a caller supplies can therefore become syntax: a $who holding 'ada' OR true is a string containing those characters, compared for equality against a field, and it matches the record whose name is literally that.

A $name can also be bound inside the script itself, with LET — that is Bindings, and it is the same mechanism seen from the other side. This page is about the half that arrives from outside, which is the half that carries the risk.

This is the whole of the injection story, and it is worth being explicit about why it is not merely "we escape it properly". Escaping is a transformation applied to text that is about to become syntax. Binding means the text never gets there: by the time a value exists, the statement's shape is already decided and cannot be added to.

Where a parameter may stand

positionexample
a value in a conditionWHERE name = $who
a value in a recordCREATE users = { name: $who }
a bound or a limitLIMIT $howmany, START $from, CLAIM $n FROM jobs
a record's idGET sessions:$token

That last one is the exception worth knowing. A record is table:id, and the two halves are different things: the table is a name, which a caller may never supply, and the id is a value, which they may. So a parameter stands in an identity's id and nowhere else in it.

That matters because GET sessions:$token is the shape a session lookup actually has. Without it, every key-value read of a caller-supplied key would be built by pasting text into a statement — which is exactly the construction parameters exist to remove, and it would be the one place the language forced you back into it.

Named values a database holds

DEFINE PARAM $grace VALUE 10m;
RETURN time::now() - $grace < time::now();
DEFINE PARAM OR REPLACE $grace VALUE 1h;
DROP PARAM $grace;
DROP PARAM IF EXISTS $grace;

DEFINE PARAM gives a database a named value that scripts, event bodies and views read as $name (from 0.33.4-beta). A parameter bound to the statement wins over one of the same name. OR REPLACE changes the value, IF NOT EXISTS keeps it, and a second plain DEFINE PARAM is refused as ParamExists. INFO FOR DATABASE lists them under params; they replicate with the catalog and every backup carries them.

What a parameter is not

It is not a name. A table, a field, an index or a database cannot be parameterised, and that is deliberate rather than unfinished: a statement whose shape depended on a supplied value could not be planned once and reused, and could not be reasoned about by reading it. A caller that needs to choose between two tables writes two statements and chooses between them in their own language, where the choice is visible.

Unbound

A parameter with no value is a refusal and never an empty value:

SELECT * FROM users WHERE name = $nobody;

UnboundParameter. Treating it as NONE would turn a wiring mistake — a name misspelled on one side — into a read that quietly matches the records with no such field, and answers.

From a client

Every client speaks this the same way, because binding happens in the node and not in the client's string handling. See The Rust client for the shape it takes there, and The HTTP surface for how a script and its bindings travel together in one request.

The search box on this site is one:

SELECT page FROM fragment WHERE text MATCHES $q;

The term you typed reached the store as a value. That is what makes it safe to take a search term from a query string — and it is the same mechanism whether the caller is across a socket or in the same process, because there is only one.

Parameters — TessariDB