[LIQ] Add nulls first / nulls last to order by

Extend the `order by` clause with optional `nulls first` and `nulls
last` modifiers following standard SQL semantics. Each sort key can
independently specify where `nil` values should appear in the result. By
default, ascending sorts place nulls last and descending sorts place
nulls first. An explicit nulls first or nulls last overrides this
default. The `asc` keyword is also now accepted as an explicit
alternative to the default ascending direction. Documentation and tests
were updated accordingly.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
This commit is contained in:
Matouš Jan Fialka
2026-03-02 12:34:25 +01:00
parent 8f193dc402
commit ff6854f6ee
10 changed files with 314 additions and 40 deletions
+17 -5
View File
@@ -35,7 +35,7 @@ const luaStyleTags = styleTags({
CompareOp: t.operator,
"true false": t.bool,
Comment: t.lineComment,
"return break goto do end while repeat until function local if then else elseif in for nil or and not query from where limit select order by desc group having":
"return break goto do end while repeat until function local if then else elseif in for nil or and not query from where limit select order by desc asc nulls first last group having":
t.keyword,
});
@@ -1258,12 +1258,24 @@ function parseQueryClause(t: ParseTree, ctx: ASTCtx): LuaQueryClause {
const orderBy: LuaOrderBy[] = [];
for (const child of t.children!) {
if (child.type === "OrderBy") {
orderBy.push({
const kids = child.children!;
let direction: "asc" | "desc" = "asc";
let nulls: "first" | "last" | undefined;
for (let i = 1; i < kids.length; i++) {
const typ = kids[i].type;
if (typ === "desc") direction = "desc";
else if (typ === "asc") direction = "asc";
else if (typ === "first") nulls = "first";
else if (typ === "last") nulls = "last";
}
const ob: LuaOrderBy = {
type: "Order",
expression: parseExpression(child.children![0], ctx),
direction: child.children![1]?.type === "desc" ? "desc" : "asc",
expression: parseExpression(kids[0], ctx),
direction,
ctx: context(child, ctx),
});
};
if (nulls) ob.nulls = nulls;
orderBy.push(ob);
}
}
return {