Functions & Aggregates
The most common way to extend DortDB is to add functions, operators, aggregates, and castables, grouped into an extension that you register on the engine.
Functions
A function has a unique name and an implementation. Mark it pure when it has no
side effects and returns the same output for the same input (which lets the
optimizer reason about it):
const myExtension = {
functions: [{ name: 'double', impl: (v: number) => v * 2, pure: true }],
};
const db = new DortDB({
mainLang: SQL(),
optimizer: { rules: defaultRules },
extensions: [myExtension],
});
db.query('SELECT double(x) AS d FROM t');
// with t = [{ x: 5 }, { x: 7 }] → [{ d: 10 }, { d: 14 }]
Aggregates
An aggregate is expressed as three parts: init to create the starting state,
step to fold in each value, and result to extract the final value:
const myExtension = {
aggregates: [
{
name: 'product',
init: () => 1,
step: (state: number, v: number) => state * v,
result: (state: number) => state,
},
],
};
db.query('SELECT product(x) AS p FROM t');
// with t = [{ x: 5 }, { x: 7 }] → [{ p: 35 }]
By default aggregates ignore nulls; set includeNulls: true to fold them in. An
optional stepInverse can speed up window-style use.
Operators and castables
- Operators (
{ name, impl }) back query operators such as+orlike. Only languages that support user-defined operators (e.g. SQL) will pick them up. - Castables (
{ name, convert }) back cast keywords, as in SQL'sCAST(val AS mytype).
Scoping and namespacing
Set scope to restrict an extension to specific languages, and schema to
namespace all of its members under a prefix:
const xqueryOnly = {
scope: ['xquery'],
aggregates: [
/* ... only visible to XQuery ... */
],
};
Worked example: the datetime extension
The provided @dortdb/datetime extension bundles date/time helpers and is a good
reference for how an extension is packaged:
import { datetime } from '@dortdb/datetime';
const db = new DortDB({
mainLang: SQL(),
optimizer: { rules: defaultRules },
extensions: [datetime],
});
db.query(
"SELECT date.extract(date.sub(now(), interval('3 years')), 'year') AS y FROM t",
);