Settled, Known Limitation, Unspecified¶
- Rulings: semantics undertsood and supported
- Known Limitations: semantics understood but there is too much current cost to implement
- Unspecified: semantics not understood
This reference is an attempt to enumerate all semantics and the syntax that manifests those semantics. These we may call rulings.
The enumeration of these rulings may be incomplete. A language that is designed of composable units may have compositions that create unsettled semantics.
To better specify delightql, the enumeration of known limitations and/or unspecified behavior creates both an acknowledgment of boundaries and an inventory for future work.
This section attempts the enumeration of the negative space where delightql has knowingly refused to support something or has thrown up its hands and said “this is too hard for the moment.”
Unspecified behaviour¶
Categorizing functionality or code as “unspecified behavior” leaves a clear warning to the programmer.
The contents of this category are places where delightql or SQL or both have no clear idea as to what the proper semantics should be.
This does not preclude a latter enlightment or clarification.
Language Limitations¶
Delightql has limitations on its expressive power and its expressive guarantees.
The source of these limitations can be any of the following:
- a missing feature in SQL itself
- a feature that is inconsistently implemented across SQL targets
- a feature from logic that has no expressive abstraction at all in SQL
- a feature that is possible in SQL but requires an admixture of DDL and DML and querying
- any feature that does not yet have a satisfactory semantics
The last point expresses the fact that delightql may choose to not support a feature at the current time while leaving the door open for its support upon future semantic clarity. This is a statement that can apply to all SQL targets or to selective SQL targets.
The current section aims to enumerate all known limitations.
Higher-Order Moding Limitations¶
Because there is no clear standard in SQL with regards to higher-order tables, delightql has two paths whenever a higher-order abstraction is used or invoked:
- it can lower the abstraction directly to the SQL target via a default transpilation rules
- it can rewrite with a detail intended semantics into a composition of normal relational values
The former states that if delightql sees:
it will lower to:
which is useful for well-known table-value functions like json_each.
The latter is exclusively the concern of the transpiler when the higher-order rule is programmer authored.
Programmer authored¶
Non-deterministic functions¶
Delightql makes NO guarantee about impure function-level referential transparency or delimited referential transparency. The rest of this section details the challenges that will need to be overcome to permit any future guarantees.
True referential transparency is a strong guarantee: that a function with the exact same arguments may be substituted with the one value it produces in all situations.
Delimited referential transparency is a looser guarantee: that within one calling context, a function form with the exact same arguments may be substituted with the one value it produces.
Non-deterministic functions like certain vendors’ random() may not have clearly defined semantics around delimited referential transparency.
To motivate, in SQLite SQL, a random value across many rows:
| r |
|---|
| 7906891248351165381 |
| -8883546532970986158 |
| -8728216203524003274 |
feels correct, as does when calling random twice in the same expression:
| r1 | r2 |
|---|---|
| -3037396489422509295 | 4469562710814311768 |
| -4848443231643320494 | -713724063462585786 |
| 4226627867170606774 | 2825360043875777743 |
When we desire our expected flavor of delimited referential transparency, the random function “materialized” once seems to work like we think it should:
with
_random as (
select random() as r
union all
select random() as r)
select
r,
r+1 as r_plus_one
from _random;
| r | r_plus_one |
|---|---|
| -7433712953842994257 | -7433712953842994256 |
| -8813118922048968177 | -8813118922048968176 |
but when SQLite’s rules about flattening mark the expression differently, we may not get what we think we are going to get:
WITH input(n) AS (
VALUES (1), (2), (3)
)
SELECT r AS first_read, r AS second_read
FROM (
SELECT random() AS r
FROM input
);
| first_read | second_read |
|---|---|
| 1747592606472746190 | -5537291275306372640 |
| -8262209585999104522 | 6666755709977980449 |
| 6154489567650109788 | -8039489511224155566 |
SQLite does, however, adhere to a certain form of referential transparency for those non-deterministic functions that are part of the SQL standard: CURRENT_TIME,CURRENT_DATE,CURRENT_TIMESTAMP.
The difference in this behaviour can be understood by the taxonomic Postgres terminology:
- IMMUTABLE: a function is guaranteed to be referentially transparent in all situations
- STABLE: a function is guaranteed to be referentially transparent within one context
- VOLATILE: a function is not guaranteed to be referentially transparent
NOTE: the term “context” may mean different things between different SQL targets: for some, at the unit of a query, for others at the unit of a transaction.
Delightql’s challenge¶
Delightql has several challenges in this corner of the SQL world:
- is there a semantics that can be guaranteed equivalent across SQL targets?
- can the standard semantic of “one delightql query expression = one SQL query expression” be maintained?
- is the IMMUTABLE/STABLE/VOLATILE taxonomy a suitable standard for coloring SQL functions?
- can rewrite equivalences match any expected semantics?
Cross-target¶
The SQLite example above shows that its implementations of the functions random() and current_timestamp() are VOLATILE and STABLE respectively. That the function current_timestamp is part of the SQL standard implies that SQL vendors should maintain STABLE semantics for these.
A matrix of implementation strategies across vendors shows that even the STABLE semantic comes with some vendor-specific caveats:
| SQL-standard family | PostgreSQL | DuckDB | MySQL | SQL Server |
|---|---|---|---|---|
CURRENT_DATE |
Yes; transaction start | Yes; transaction start | Yes; query start | SQL Server 2025+/Azure |
CURRENT_TIME |
Yes; transaction start | Yes; transaction start | Yes; query start | No ordinary T-SQL spelling |
CURRENT_TIMESTAMP |
Yes; transaction start | Yes; transaction start | Yes; query start | Yes; classified nondeterministic |
LOCALTIME |
Yes; transaction start | Yes; transaction start | Accepted as a NOW() synonym, not the standard TIME distinction |
No |
LOCALTIMESTAMP |
Yes; transaction start | Yes; transaction start | Accepted as a NOW() synonym |
No |
NEXT VALUE FOR |
No; uses nextval() |
No; uses nextval() |
No sequences | Yes |
PREVIOUS VALUE FOR |
No; uses currval() |
No; uses currval() |
No | No |
| Routine volatility declaration | IMMUTABLE / STABLE / VOLATILE |
UDF side_effects metadata |
DETERMINISTIC / NOT DETERMINISTIC, author-asserted |
Engine-derived determinism and schema binding |
The challenge therefore may be answered with vendor-specific transpilation using correct transactional boundaries.
Query-only Semantics and Materialization¶
The primary semantic that delightql guarantees is that all pure queries are equivalent to the SQL string that they produce. That delightql does not have a direct mapping between query string to query string when directives are utilized is not a contradiction: directives are impure and are marked as such (!).
Therefore if delightql wishes to support support referential transparency for functions syntactically marked as pure:
it cannot result in an admixture of DDL and SQL:
BEGIN;
CREATE TEMPORARY TABLE __dql_stage AS
WITH input(a) AS (
VALUES (1), (2), (3), (3)
)
SELECT
input.a,
random() AS r
FROM input;
SELECT
a,
r,
r + 1
FROM __dql_stage;
DROP TABLE __dql_stage;
COMMIT;
If delightql eventually does allow for these impure functions, it will require marking them as impure:
which will
- give a syntactic indication to the programmer that both the primary semantic is void and that a new (to-be-determined) referential semantic is now in force
- instruct the delightql compiler to create an effect plan of DDL and transactions
This remains a challenge under the current epistemic regime whereby unknown functions are default transpiled into the target SQL:
The IMMUTABLE/STABLE/VOLATILE¶
Relational Equational Rewriting¶
Recursion Limitations¶
Autogenerated Names¶
JSON-awareness and cataloging¶
partner_sale(*)
|> (sale_id, payload:{.buyer} as buyer)
|> %(sale_id ~> max:(buyer) as buyer)
|> (sale_id, {"buyer": buyer} as packet)
Today, without the max step, the buyer re-enters the packet as an object (sale 1):
{"buyer":{"email":"[email protected]","country":"BR"}}
With the max step it re-enters as a string:
{"buyer":"{\"email\":\"[email protected]\",\"country\":\"BR\"}"}
This is a known limitation.
Lateral Join Limitations¶
An inner-relation can only reference an outer relation prior to a scope barrier.