From 4bc0557e58e3f59830d4ff98e4dc10e0a788ca7e Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Tue, 16 Jul 2024 12:39:04 -0700 Subject: [PATCH] docs(70): SQL spec for 1.0 Closes #70. --- docs/sql/index.md | 407 ------------------------------------- docs/sql/sql.md | 498 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 498 insertions(+), 407 deletions(-) delete mode 100644 docs/sql/index.md create mode 100644 docs/sql/sql.md diff --git a/docs/sql/index.md b/docs/sql/index.md deleted file mode 100644 index 6609720..0000000 --- a/docs/sql/index.md +++ /dev/null @@ -1,407 +0,0 @@ -# SQL Support - -SpacetimeDB supports a subset of SQL as a query language. Developers can evaluate SQL queries against a Spacetime database via the `spacetime sql` command-line tool and the [`/database/sql/:name_or_address POST` HTTP endpoint](/docs/http/database#databasesqlname_or_address-post). Client developers also write SQL queries when subscribing to events in the [WebSocket API](/docs/ws#subscribe) or via an SDK `subscribe` function. - -SpacetimeDB aims to support much of the [SQL 2016 standard](https://www.iso.org/standard/63555.html), and in particular aims to be compatible with [PostgreSQL](https://www.postgresql.org/). - -SpacetimeDB 0.6 implements a relatively small subset of SQL. Future SpacetimeDB versions will implement additional SQL features. - -## Types - -| Type | Description | -| --------------------------------------------- | -------------------------------------- | -| [Nullable types](#nullable-types) | Types which may not hold a value. | -| [Logic types](#logic-types) | Booleans, i.e. `true` and `false`. | -| [Integer types](#integer-types) | Numbers without fractional components. | -| [Floating-point types](#floating-point-types) | Numbers with fractional components. | -| [Text types](#text-types) | UTF-8 encoded text. | - -### Definition statements - -| Statement | Description | -| ----------------------------- | ------------------------------------ | -| [CREATE TABLE](#create-table) | Create a new table. | -| [DROP TABLE](#drop-table) | Remove a table, discarding all rows. | - -### Query statements - -| Statement | Description | -| ----------------- | -------------------------------------------------------------------------------------------- | -| [FROM](#from) | A source of data, like a table or a value. | -| [JOIN](#join) | Combine several data sources. | -| [SELECT](#select) | Select specific rows and columns from a data source, and optionally compute a derived value. | -| [DELETE](#delete) | Delete specific rows from a table. | -| [INSERT](#insert) | Insert rows into a table. | -| [UPDATE](#update) | Update specific rows in a table. | - -## Data types - -SpacetimeDB is built on the Spacetime Algebraic Type System, or SATS. SATS is a richer, more expressive type system than the one included in the SQL language. - -Because SATS is a richer type system than SQL, some SATS types cannot cleanly correspond to SQL types. In particular, the SpacetimeDB SQL interface is unable to construct or compare instances of product and sum types. As such, SpacetimeDB SQL must largely restrict themselves to interacting with columns of builtin types. - -Most SATS builtin types map cleanly to SQL types. - -### Nullable types - -SpacetimeDB types, by default, do not permit `NULL` as a value. Nullable types are encoded in SATS using a sum type which corresponds to [Rust's `Option`](https://doc.rust-lang.org/stable/std/option/enum.Option.html). In SQL, such types can be written by adding the constraint `NULL`, like `INT NULL`. - -### Logic types - -| SQL | SATS | Example | -| --------- | ------ | --------------- | -| `BOOLEAN` | `Bool` | `true`, `false` | - -### Numeric types - -#### Integer types - -An integer is a number without a fractional component. - -Adding the `UNSIGNED` constraint to an integer type allows only positive values. This allows representing a larger positive range without increasing the width of the integer. - -| SQL | SATS | Example | Min | Max | -| ------------------- | ----- | ------- | ------ | ----- | -| `TINYINT` | `I8` | 1 | -(2⁷) | 2⁷-1 | -| `TINYINT UNSIGNED` | `U8` | 1 | 0 | 2⁸-1 | -| `SMALLINT` | `I16` | 1 | -(2¹⁵) | 2¹⁵-1 | -| `SMALLINT UNSIGNED` | `U16` | 1 | 0 | 2¹⁶-1 | -| `INT`, `INTEGER` | `I32` | 1 | -(2³¹) | 2³¹-1 | -| `INT UNSIGNED` | `U32` | 1 | 0 | 2³²-1 | -| `BIGINT` | `I64` | 1 | -(2⁶³) | 2⁶³-1 | -| `BIGINT UNSIGNED` | `U64` | 1 | 0 | 2⁶⁴-1 | - -#### Floating-point types - -SpacetimeDB supports single- and double-precision [binary IEEE-754 floats](https://en.wikipedia.org/wiki/IEEE_754). - -| SQL | SATS | Example | Min | Max | -| ----------------- | ----- | ------- | ------------------------ | ----------------------- | -| `REAL` | `F32` | 1.0 | -3.40282347E+38 | 3.40282347E+38 | -| `DOUBLE`, `FLOAT` | `F64` | 1.0 | -1.7976931348623157E+308 | 1.7976931348623157E+308 | - -### Text types - -SpacetimeDB supports a single string type, `String`. SpacetimeDB strings are UTF-8 encoded. - -| SQL | SATS | Example | Notes | -| ----------------------------------------------- | -------- | ------- | -------------------- | -| `CHAR`, `VARCHAR`, `NVARCHAR`, `TEXT`, `STRING` | `String` | 'hello' | Always UTF-8 encoded | - -> SpacetimeDB SQL currently does not support length contraints like `CHAR(10)`. - -## Syntax - -### Comments - -SQL line comments begin with `--`. - -```sql --- This is a comment -``` - -### Expressions - -We can express different, composable, values that are universally called `expressions`. - -An expression is one of the following: - -#### Literals - -| Example | Description | -| --------- | ----------- | -| `1` | An integer. | -| `1.0` | A float. | -| `'hello'` | A string. | -| `true` | A boolean. | - -#### Binary operators - -| Example | Description | -| ------- | ------------------- | -| `1 > 2` | Integer comparison. | -| `1 + 2` | Integer addition. | - -#### Logical expressions - -Any expression which returns a boolean, i.e. `true` or `false`, is a logical expression. - -| Example | Description | -| ---------------- | ------------------------------------------------------------ | -| `1 > 2` | Integer comparison. | -| `1 + 2 == 3` | Equality comparison between a constant and a computed value. | -| `true AND false` | Boolean and. | -| `true OR false` | Boolean or. | -| `NOT true` | Boolean inverse. | - -#### Function calls - -| Example | Description | -| --------------- | -------------------------------------------------- | -| `lower('JOHN')` | Apply the function `lower` to the string `'JOHN'`. | - -#### Table identifiers - -| Example | Description | -| ------------- | ------------------------- | -| `inventory` | Refers to a table. | -| `"inventory"` | Refers to the same table. | - -#### Column references - -| Example | Description | -| -------------------------- | ------------------------------------------------------- | -| `inventory_id` | Refers to a column. | -| `"inventory_id"` | Refers to the same column. | -| `"inventory.inventory_id"` | Refers to the same column, explicitly naming its table. | - -#### Wildcards - -Special "star" expressions which select all the columns of a table. - -| Example | Description | -| ------------- | ------------------------------------------------------- | -| `*` | Refers to all columns of a table identified by context. | -| `inventory.*` | Refers to all columns of the `inventory` table. | - -#### Parenthesized expressions - -Sub-expressions can be enclosed in parentheses for grouping and to override operator precedence. - -| Example | Description | -| ------------- | ----------------------- | -| `1 + (2 / 3)` | One plus a fraction. | -| `(1 + 2) / 3` | A sum divided by three. | - -### `CREATE TABLE` - -A `CREATE TABLE` statement creates a new, initially empty table in the database. - -The syntax of the `CREATE TABLE` statement is: - -> **CREATE TABLE** _table_name_ (_column_name_ _data_type_, ...); - -![create-table](/images/syntax/create_table.svg) - -#### Examples - -Create a table `inventory` with two columns, an integer `inventory_id` and a string `name`: - -```sql -CREATE TABLE inventory (inventory_id INTEGER, name TEXT); -``` - -Create a table `player` with two integer columns, an `entity_id` and an `inventory_id`: - -```sql -CREATE TABLE player (entity_id INTEGER, inventory_id INTEGER); -``` - -Create a table `location` with three columns, an integer `entity_id` and floats `x` and `z`: - -```sql -CREATE TABLE location (entity_id INTEGER, x REAL, z REAL); -``` - -### `DROP TABLE` - -A `DROP TABLE` statement removes a table from the database, deleting all its associated rows, indexes, constraints and sequences. - -To empty a table of rows without destroying the table, use [`DELETE`](#delete). - -The syntax of the `DROP TABLE` statement is: - -> **DROP TABLE** _table_name_; - -![drop-table](/images/syntax/drop_table.svg) - -Examples: - -```sql -DROP TABLE inventory; -``` - -## Queries - -### `FROM` - -A `FROM` clause derives a data source from a table name. - -The syntax of the `FROM` clause is: - -> **FROM** _table_name_ _join_clause_?; - -![from](/images/syntax/from.svg) - -#### Examples - -Select all rows from the `inventory` table: - -```sql -SELECT * FROM inventory; -``` - -### `JOIN` - -A `JOIN` clause combines two data sources into a new data source. - -Currently, SpacetimeDB SQL supports only inner joins, which return rows from two data sources where the values of two columns match. - -The syntax of the `JOIN` clause is: - -> **JOIN** _table_name_ **ON** _expr_ = _expr_; - -![join](/images/syntax/join.svg) - -### Examples - -Select all players rows who have a corresponding location: - -```sql -SELECT player.* FROM player - JOIN location - ON location.entity_id = player.entity_id; -``` - -Select all inventories which have a corresponding player, and where that player has a corresponding location: - -```sql -SELECT inventory.* FROM inventory - JOIN player - ON inventory.inventory_id = player.inventory_id - JOIN location - ON player.entity_id = location.entity_id; -``` - -### `SELECT` - -A `SELECT` statement returns values of particular columns from a data source, optionally filtering the data source to include only rows which satisfy a `WHERE` predicate. - -The syntax of the `SELECT` command is: - -> **SELECT** _column_expr_ > **FROM** _from_expr_ -> {**WHERE** _expr_}? - -![sql-select](/images/syntax/select.svg) - -#### Examples - -Select all columns of all rows from the `inventory` table: - -```sql -SELECT * FROM inventory; -SELECT inventory.* FROM inventory; -``` - -Select only the `inventory_id` column of all rows from the `inventory` table: - -```sql -SELECT inventory_id FROM inventory; -SELECT inventory.inventory_id FROM inventory; -``` - -An optional `WHERE` clause can be added to filter the data source using a [logical expression](#logical-expressions). The `SELECT` will return only the rows from the data source for which the expression returns `true`. - -#### Examples - -Select all columns of all rows from the `inventory` table, with a filter that is always true: - -```sql -SELECT * FROM inventory WHERE 1 = 1; -``` - -Select all columns of all rows from the `inventory` table with the `inventory_id` 1: - -```sql -SELECT * FROM inventory WHERE inventory_id = 1; -``` - -Select only the `name` column of all rows from the `inventory` table with the `inventory_id` 1: - -```sql -SELECT name FROM inventory WHERE inventory_id = 1; -``` - -Select all columns of all rows from the `inventory` table where the `inventory_id` is 2 or greater: - -```sql -SELECT * FROM inventory WHERE inventory_id > 1; -``` - -### `INSERT` - -An `INSERT INTO` statement inserts new rows into a table. - -One can insert one or more rows specified by value expressions. - -The syntax of the `INSERT INTO` statement is: - -> **INSERT INTO** _table_name_ (_column_name_, ...) **VALUES** (_expr_, ...), ...; - -![sql-insert](/images/syntax/insert.svg) - -#### Examples - -Insert a single row: - -```sql -INSERT INTO inventory (inventory_id, name) VALUES (1, 'health1'); -``` - -Insert two rows: - -```sql -INSERT INTO inventory (inventory_id, name) VALUES (1, 'health1'), (2, 'health2'); -``` - -### UPDATE - -An `UPDATE` statement changes the values of a set of specified columns in all rows of a table, optionally filtering the table to update only rows which satisfy a `WHERE` predicate. - -Columns not explicitly modified with the `SET` clause retain their previous values. - -If the `WHERE` clause is absent, the effect is to update all rows in the table. - -The syntax of the `UPDATE` statement is - -> **UPDATE** _table_name_ **SET** > _column_name_ = _expr_, ... -> {_WHERE expr_}?; - -![sql-update](/images/syntax/update.svg) - -#### Examples - -Set the `name` column of all rows from the `inventory` table with the `inventory_id` 1 to `'new name'`: - -```sql -UPDATE inventory - SET name = 'new name' - WHERE inventory_id = 1; -``` - -### DELETE - -A `DELETE` statement deletes rows that satisfy the `WHERE` clause from the specified table. - -If the `WHERE` clause is absent, the effect is to delete all rows in the table. In that case, the result is a valid empty table. - -The syntax of the `DELETE` statement is - -> **DELETE** _table_name_ -> {**WHERE** _expr_}?; - -![sql-delete](/images/syntax/delete.svg) - -#### Examples - -Delete all the rows from the `inventory` table with the `inventory_id` 1: - -```sql -DELETE FROM inventory WHERE inventory_id = 1; -``` - -Delete all rows from the `inventory` table, leaving it empty: - -```sql -DELETE FROM inventory; -``` diff --git a/docs/sql/sql.md b/docs/sql/sql.md new file mode 100644 index 0000000..a89abed --- /dev/null +++ b/docs/sql/sql.md @@ -0,0 +1,498 @@ +# SQL Support + +SpacetimeDB supports two subsets of SQL. +One for ad hoc queries against the database. +These can be made through the [cli] or the [http] endpoint. +Another for subscriptions made through the [websocket] api or [sdk]. + +## Subscriptions + +```ebnf +SELECT projection FROM relation [ WHERE predicate ] +``` + +The subscription language is strictly a query language. +Its sole purpose is to replicate a subset or view of the database, +and to **automatically** update that view in realtime as the database changes. + +There is no context for manually updating this view. +Hence data manipulation commands are not supported. + +> NOTE: Because subscriptions are evaluated in realtime, +> performance is critical, and as a result, +> additional restrictions are applied over ad hoc queries. +> These restrictions are highlighted below. + +### SELECT + +```ebnf +SELECT ( '*' | table '.' '*' ) +``` + +The `SELECT` clause determines the table that is being subscribed to. +A subscription query may only return rows from a single table, +and it must return the entire row. +Individual column projections are not allowed. + +A wildcard projection `*` is allowed when the table is unambiguous, +but when a query contains multiple table references, +a qualified wildcard projection `.*` is necessary. + +### FROM + +```ebnf +FROM table [ [AS] alias ] [ [INNER] JOIN table [ [AS] alias ] ON predicate ] +``` + +While you can only subscribe to rows from a single table, +you may reference two tables in the `FROM` clause using a `JOIN`. +A `JOIN` selects all combinations of rows from its input tables, +and `ON` determines which combinations are considered. + +> IMPORTANT: If a `JOIN` is used, +> an index **must** be defined on the join column of both tables, +> so that it can be evaluated efficiently. + +### WHERE + +```ebnf +predicate + = expr + | predicate AND predicate + ; + +expr + = literal + | column + | expr op expr + ; + +op + = '=' + | '<' + | '>' + | '<' '=' + | '>' '=' + ; + +literal + = INTEGER + | STRING + | HEX + | TRUE + | FALSE + ; +``` + +While the `SELECT` clause determines the table, +the `WHERE` clause determines the rows in the subscription. + +The subscription language supports conjunctions `AND`. +It does not support disjunctions `OR`. +It does not support `!=` comparisons. + +Note that disjunctions can be expressed by subscribing to multiple queries. + +```sql +-- Which items in my inventory have been purchased and are more than $X? +SELECT item +FROM Inventory item +JOIN Orders order +ON item.item_id = order.item_id +WHERE item.price > {X} + +-- Which items in my inventory have been purchased and are less than $Y? +SELECT item +FROM Inventory item +JOIN Orders order +ON item.item_id = order.item_id +WHERE item.price < {Y} +``` + +> NOTE: Arithmetic expressions will be supported in the future. + +## Query and DML + +### Statements + +- [SELECT](#select) +- [INSERT](#insert) +- [DELETE](#delete) +- [UPDATE](#update) +- [SET](#set) +- [SHOW](#show) + +### SELECT + +```ebnf +SELECT [ DISTINCT ] projection FROM relation [ [ WHERE predicate ] [ ORDER BY order ] [ LIMIT limit ] ] +``` + +#### SELECT Clause + +```ebnf +projection + = '*' + | table '.' '*' + | projExpr { ',' projExpr } + | aggrExpr { ',' aggrExpr } + ; + +projExpr + = column [ [ AS ] alias ] + ; + +aggrExpr + = COUNT '(' STAR ')' [AS] alias + | COUNT '(' DISTINCT column ')' [AS] alias + | SUM '(' column ')' [AS] alias + ; +``` + +The `SELECT` clause determines the columns that are returned. +In particular it supports both column and wildcard projections. +It also supports `sum` and `count` aggregation. + +If `DISTINCT` is specified, only unique rows are returned. +However each projected column must be of a type that allows comparison. + +Examples: +```sql +-- Select the items in my inventory +SELECT * FROM Inventory; + +-- Select the names of the items in my inventory +SELECT item_name FROM Inventory + +-- How many items are in my inventory? +SELECT COUNT(*) as num_items FROM Inventory +``` + +#### FROM Clause + +```ebnf +FROM table [ [AS] alias ] { [INNER] JOIN table [ [AS] alias ] ON predicate } +``` + +> NOTE: The query language supports joins among an arbitrary number of tables. +> It does not impose any index restrictions like the subscription language. + +Examples: +```sql +-- Which orders include currently stocked items? +SELECT order.* +FROM Orders order +JOIN Inventory inv +ON order.item_id = inv.item_id +``` + +#### WHERE Clause + +```ebnf +predicate + = expr + | predicate AND predicate + | predicate OR predicate + ; + +expr + = literal + | column + | expr op expr + ; + +op + = '=' + | '<' + | '>' + | '<' '=' + | '>' '=' + | '!' '=' + | '<' '>' + ; + +literal + = INTEGER + | FLOAT + | STRING + | HEX + | TRUE + | FALSE + ; +``` + +The query language supports both conjunctions and disjunctions. +It also supports `!=` comparisons. + +> NOTE: Arithmetic expressions will be supported in the future. + +Examples: +```sql +SELECT * FROM Inventory WHERE item_id = 1; +``` + +#### ORDER BY Clause + +```ebnf +ORDER BY column [ ASC | DESC ] { ',' column [ ASC | DESC ] } +``` + +`ORDER BY` sorts the result set by one or more of the projected columns. +Ascending is the default behavior if not qualified. +Each column involved must be of a type that allows comparison. +Comparison is not supported for sum, product, or array types. + +Examples: +```sql +-- Sort the items in the inventory by order of increasing price +SELECT * FROM Inventory ORDER BY price; + +-- Sort the items in the inventory by order of decreasing price +SELECT * FROM Inventory ORDER BY price DESC + +#### LIMIT Clause + +```ebnf +LIMIT INTEGER +``` + +`LIMIT` restricts the number of rows returned. + +Examples: +```sql +-- What are the 5 highest priced items in my inventory? +SELECT * FROM Inventory ORDER BY price LIMIT 5; +``` + +### INSERT + +```ebnf +INSERT INTO table [ '(' column { ',' column } ')' ] VALUES '(' literal { ',' literal } ')' +``` + +Examples: +```sql +-- Inserting one row +INSERT INTO Inventory (item_id, item_name) VALUES (1, 'health1'); + +-- Inserting two rows +INSERT INTO Inventory (item_id, item_name) VALUES (1, 'health1'), (2, 'health2'); +``` + +### DELETE + +```ebnf +DELETE FROM table [ WHERE predicate ] +``` + +Deletes all rows from a table. +If `WHERE` is specified, only the matching rows are deleted. + +Examples: +```sql +-- Delete all rows +DELETE FROM Inventory; + +-- Delete all rows with a specific item_id +DELETE FROM Inventory WHERE item_id = 1; +``` + +### UPDATE + +```ebnf +UPDATE table SET [ '(' assignment { ',' assignment } ')' ] [ WHERE predicate ] +``` + +Updates column values of existing rows in a table. +The columns are identified by the `assignment` defined as `column '=' expr`. +The column values are updated for all rows that match the `WHERE` condition. +The rows are updated after the `WHERE` condition is evaluated for all rows. + +Examples: +```sql +-- Update the item_name for all rows with a specific item_id +UPDATE Inventory SET item_name = 'new name' WHERE item_id = 1; +``` + +### SET + +> NOTE: This statement is not part of the stable API. +> Its compatibility with future versions of SpacetimeDB is not guaranteed. + +```ebnf +SET var ( TO | '=' ) literal +``` + +Updates the value of a system variable. + +### SHOW + +> NOTE: This statement is not part of the stable API. +> Its compatibility with future versions of SpacetimeDB is not guaranteed. + +```ebnf +SHOW var +``` + +Returns the value of a system variable. + +## System Variables + +> NOTE: System variables are not part of the stable API. +> Their compatibility with future versions of SpacetimeDB is not guaranteed. + +- `row_limit` + + This system variable defines a cardinality threshold. + Queries are rejected if their estimated cardinality exceeds it. + + Ex. + ```sql + -- Reject queries that read more than 10K rows + SET row_limit = 10000 + ``` + +## Data types + +SpacetimeDB data types are defined by [SATS], +the Spacetime Algebraic Type System, +and tuples are stored or encoded using [BSATN], +the Binary Spacetime Algebraic Type Notation format. + +Spacetime SQL however does not support all of [SATS]. +In particular it has limited support for [products] and [sums]. +The language itself does not provide a way to construct them, +nor does it provide scalar operations on top of them, +the one exception being equality comparison. +However the language does allow for reading them from tables, +as well as returning them from queries. + +## Literals + +```ebnf +literal = INTEGER | FLOAT | STRING | HEX | TRUE | FALSE ; +``` + +For each of the following [SATS] data types, +we describe how to express their literal values in Spacetime SQL. + +### Bool + +In Spacetime SQL, the [boolean] Algebraic Type, +representing the two truth values of boolean logic, +is expressed using the canonical atoms `true` or `false`. + +### Integer + +The construction of [integer] values in Spacetime SQL. +The concrete [SATS] type is inferred from the context. + +```ebnf +INTEGER + = [ '+' | '-' ] NUM + | [ '+' | '-' ] NUM 'E' [ '+' | '-' ] NUM + ; + +NUM + = DIGIT { DIGIT } + ; + +DIGIT + = 0..9 + ; +``` + +### Float + +The construction of [IEEE 754] [float] values in Spacetime SQL. +The concrete [SATS] type is inferred from the context. + +```ebnf +FLOAT + = [ '+' | '-' ] [ NUM ] '.' NUM + | [ '+' | '-' ] [ NUM ] '.' NUM 'E' [ '+' | '-' ] NUM + ; +``` + +### String + +The construction of variable length string values in Spacetime SQL, +where `CHAR` is a `utf-8` encoded unicode character. + +```ebnf +STRING + = "'" { "''" | CHAR } "'" + ; +``` + +### Hex + +Hexidecimal literals represent either [Identity] or [Address] types. +The type is ultimately inferred from the context. + +```ebnf +HEX + = 'X' "'" { HEXIT } "'" + | '0' 'x' { HEXIT } + ; + +HEXIT + = DIGIT | a..f | A..F + ; +``` + +## Identifiers + +```ebnf +identifier + = LATIN { LATIN | DIGIT | '_' } + | '"' { '""' | CHAR } '"' + ; + +LATIN + = a..z | A..Z + ; +``` + +Identifiers are tokens that identify database objects like tables or columns. +Spacetime SQL supports both quoted and unquoted identifiers. +Quoted identifiers are case sensitive, +whereas unquoted identifiers are case insensitive. +Column references may also be qualified with a table name. + +```ebnf +table + = identifier + ; + +alias + = identifier + ; + +var + = identifier + ; + +column + = identifier + | identifier '.' identifier + ; +``` + + +[sdk]: /docs/sdks/rust/index.md#subscribe-to-queries +[cli]: /docs/http/database#databasesqlname_or_address-post +[http]: /docs/http/database#databasesqlname_or_address-post +[websocket]: /docs/ws#subscribe + +[sats]: /docs/satn.md +[bsatn]: /docs/bsatn.md +[boolean]: /docs/satn.md#builtintype +[integer]: /docs/satn.md#builtintype +[floats]: /docs/satn.md#builtintype +[sums]: /docs/satn.md#sumtype +[products]: /docs/satn.md#producttype +[Identity]: /docs/index.md#identities +[Address]: /docs/index.md#addresses + +[IEEE 754]: https://en.wikipedia.org/wiki/IEEE_754