Language reference
Delimited Identifier (`name`)
Syntax
`` `name` `` -- any name, wrapped in backticks
`` `order` `` -- the relation/column named "order", not the ORDER operator
`` `my table` `` -- a name containing spaces
`` `a``b` `` -- a name containing a literal backtick (doubled to escape)
Description
Relix reserves a fixed set of operator and keyword words — SELECT, SORT, ORDER, GROUP, JOIN, UNION, IS, IN, and so on (the full set is the lexer keyword table; relix help lang operators prints it). Written bare in a relation position, such a word is read as the operator, so a relation or view that happens to be named after one cannot be referenced directly:
π name (order) -- ERROR: 'order' is read as the ORDER/τ operator
A delimited identifier — the name wrapped in backticks — tells the lexer to treat the text as a plain name, never an operator or keyword:
π name (`order`) -- OK: 'order' is the relation named "order"
Backticks also let a name contain characters an ordinary identifier can't (spaces, punctuation): `line total`, `2024 q1`. A literal backtick inside the name is written by doubling it: `ab `` is the name `` ab ``.
This is the same escape hatch SQL provides with double-quotes (ANSI) or backticks (MySQL). Relix uses backticks because single and double quotes are already string literals.
Technical Description
The lexer emits a delimited identifier as an ordinary IDENTIFIER token whose value is the decoded text (backticks stripped, doubled backticks collapsed), so every downstream consumer treats it exactly like a normal name — there is no distinct AST node and no semantic difference from an unreserved bare name. An unterminated identifier (end of input or a newline before the closing backtick) or an empty `` is a parse error, and so is one that holds only whitespace. An empty name:
query { π `` (Orders) };
A name of only spaces:
query { π ` ` (Orders) };
A name that reaches the end of the line before its closing backtick:
query { π `order
id` (Orders) };
The pretty-printer performs the inverse: when it prints a relation or column reference whose name collides with a reserved word (or is not identifier-shaped), it re-adds the backticks, so a printed tree always parses back to the identical tree. Operator-introduced parameter names (the columns after AS, VIA, BY, PER, HOPS, a rename column list, a projection or aggregate alias) are read leniently by the parser and are printed bare.
Examples
A view named after the RA operator word order — defined bare (a name is a plain identifier on the left of :=; only the operator words are reserved, and only inside an expression) and referenced with backticks downstream:
order := { σ status = "open" (RawOrders) };
Open := { π id, total (`order`) };
query { Open };
order := { SELECT status = "open" (RawOrders) };
Open := { PROJECT id, total (`order`) };
query { Open };
Join two relations, one named after a reserved word (select), and filter on a column whose name also collides (order):
<!-- analysis-skip: a relation named with a delimited identifier can only come from a catalog, so this example cannot declare what it references -->
Recent := { `select` ⋈ Users };
query { σ `order` = 1 (Events) };
Recent := { `select` JOIN Users };
query { SELECT `order` = 1 (Events) };
A qualified column reference delimits only the reserved segment:
σ `order`.total > 0 (Sales) -- 'order' the table, 'total' an ordinary column
Limitations
- The backtick delimiter is fixed; there is no alternate quoting character.
- A name may not span a line — the closing backtick must be on the same line.
- A single delimited name that itself contains a dot (
`a.b`) is stored as the bare stringa.b, indistinguishable from a qualified reference, so it re-renders as two segments (`a`.`b`). Such names are vanishingly rare. - Backticks are recognised inside relational-algebra expressions (view bodies,
query { … }). A statement-level name — the left side of:=or asource/defdeclaration — is a bare identifier and does not (yet) accept backticks, so a name that collides with a statement keyword (source,query,import,namespace, …) cannot be used there. The RA operator words (order,select,sort, …) are not statement keywords, so they work bare on the left of:=and only need backticks where they are referenced.
Alternatives
Prefer a non-reserved name when you control the schema — delimiting is for names you don't control (imported schemas, external tables) or that genuinely need unusual characters.
See Also
Notes
Delimiting a name that does not need it is harmless and a no-op on output — `Users` parses to exactly the same relation as Users.