Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Scalar functions

Function names are case-insensitive. A function given a null argument answers null unless its row says otherwise. Aggregates (COUNT, SUM, MEDIAN, …) are listed under Queries, window functions under Window functions, the JSON functions on their own page, and CAST, TRY_CAST, IF, CASE and the operators under Expressions and types.

Strings

length(), substr(), upper()/lower(), the functions in this table and LIKE’s _ count characters, as Postgres and DuckDB do; strlen() counts bytes. A byte that is not UTF-8 counts as one character, so mis-encoded text never fails a load.

functionreturns
length(s)the number of characters
strlen(s)the number of bytes
lower(s), upper(s)the text in one case; Latin, Greek and Cyrillic map one character to one (ß stays)
trim(s)the text without leading and trailing spaces
substr(s, start[, length])the characters from start (from 1) on, or length of them
left(s, n), right(s, n)the first or last n characters
lpad(s, length[, fill]), rpad(s, length[, fill])the text padded on the left or right to length with fill (a space by default); longer text is cut to length
replace(s, from, to)every from replaced by to
translate(s, from, to)each character of from mapped to the one at the same place in to, deleted when to is shorter: translate(cnpj, './-', '') keeps the digits
concat(a, …)the values joined; null when any value is (a || b is the same)
concat_ws(sep, a, …)the values joined with sep, skipping nulls
split_part(s, delimiter, n)the n-th piece (from 1), '' past the last
strpos(s, sub)where sub first starts (from 1), 0 when it does not occur
starts_with(s, prefix), ends_with(s, suffix), contains(s, sub)whether the text starts with, ends with or holds the other
like(s, pattern)s LIKE pattern as a call
repeat(s, n)the text n times over
reverse(s)the characters in reverse order
initcap(s)each run of letters and digits capitalized
unaccent(s), strip_accents(s)the accents of Latin letters dropped: São → Sao, Æ → AE, ß → ss
ascii(s)the first character’s code point
chr(n)the character with code point n

Searching rows

search(cols, text[, unaccent]) is true when a row holds text in the given columns: * for every column, t.* for one table of a join, * EXCEPT (a, b) or t.* EXCEPT (…) to leave some out, (a, b) or a single column to name them. The text is a small query: words must all appear, each in any of the columns; -word keeps the rows without it; col:word looks in that column only (a name that is none of them leaves the whole token as text); "two words" is one phrase. All of it ignores case — accented, Greek and Cyrillic letters too, as upper() and lower() fold them, so crédito finds CRÉDITO (an accent still counts: credito does not, unless the third argument is true, which drops accents on both sides as unaccent() does — credito then finds CRÉDITO and strasse finds Straße; it may be a bool $param) — and a value is matched as basalt prints it — a date as 2026-03-01, a decimal at its scale — so 2026-03 finds March and 10.50 that amount; a NULL holds nothing. The REPL’s \view find (f) is the same search over the rows it shows.

SELECT * FROM 'orders.csv' WHERE search(*, 'north -cancelled');
SELECT o.id, c.name FROM 'orders.csv' o JOIN 'customers.csv' c ON c.id = o.cust
WHERE search(c.*, '"são paulo"');
SELECT COUNT_IF(search((note, memo), 'urgent')) AS urgent FROM 'orders.csv';
SELECT * FROM 'payments.csv' WHERE search(kind, 'credito', true);

It reads whole rows, so it is never pushed to a source: a SQL read sends every column and filters here, and a WHERE with search stays after the selects and joins it follows. In a join’s ON it applies to the joined pair — filtering an inner join, deciding the matches of an outer one. The columns are resolved when the query is planned, so an unknown name fails check; the text may be a literal or a $param. Every searched value is turned into text for each row, so naming the columns (search((a, b), …)) is faster than * on a wide table.

Regular expressions

The pattern syntax is on Regular expressions. A literal pattern is compiled at plan time, so a malformed one fails check.

functionreturns
regexp_matches(s, pattern)whether the pattern matches anywhere; anchor with ^…$ for the whole string
regexp_extract(s, pattern[, group])the match, or the numbered group; null where nothing matches
regexp_replace(s, pattern, replacement[, flags])the text with the first match replaced; \1…\9 in the replacement expand to captured groups and \0 to the whole match. flags is any of g (replace every match) and i (ignore the case of ASCII letters): regexp_replace(s, '\s+', ' ', 'g')

Hashes and encodings

The hashes are over the value’s text. For a row’s change key, write md5(concat_ws('|', a, b, c)): concat would make it null whenever a column is.

functionreturns
md5(s), sha256(s)the digest as lowercase hex
xxhash64(s)the hash as a BIGINT
to_base64(s)the Base64 text
from_base64(s)the decoded BYTES; CAST(… AS STRING) for text
url_encode(s), url_decode(s)percent-encoding as RFC 3986 has it; + is left alone

Numbers

functionreturns
abs(x)the absolute value
floor(x), ceil(x)rounded down or up to a whole number
round(x[, digits])rounded half away from zero, to digits decimal places; deliberately engine-side, never pushed down. On a DECIMAL it is exact, and a literal digits answers DECIMAL(p, digits)
mod(a, b)the remainder, with the dividend’s sign: mod(-7, 3) is -1
power(base, exponent)base raised to exponent
sqrt(x)the square root
sign(x)-1, 0 or 1

Nulls and comparison

functionreturns
coalesce(a, …)the first value that is not null (a ?? b is the same)
nullif(a, b)null when a = b, else a
greatest(a, b, …), least(a, b, …)the largest or smallest value; null arguments are ignored, as in Postgres

Dates and times

The units are year, month, week, day, hour, minute and second. week is the ISO week: it starts on Monday, extract numbers it 1–53 with week 1 holding the year’s first Thursday, and date_diff counts the Mondays crossed. check rejects an unknown unit even over a SQL table whose columns it has not seen.

functionreturns
now()the current timestamp
today()the current date
date_trunc(unit, ts)the time cut to the start of its unit; date_trunc('week', …) is that Monday at 00:00
extract(unit FROM ts)the unit’s number; extract(unit, ts) is accepted too
date_add(unit, n, ts)the time moved by n units; month and year arithmetic clamps the day of the month (2026-01-31 plus a month is 2026-02-28)
date_diff(unit, start, end)the number of units from start to end
make_date(year, month, day)the date
epoch(ts)seconds since 1970-01-01 00:00:00
to_timestamp(seconds)the timestamp that many seconds after 1970-01-01 00:00:00
strftime(ts, format)the time as text
strptime(text, format)the timestamp read out of text: strptime(dt, '%d/%m/%Y')
try_strptime(text, format)the same, but null where the text does not fit

The format codes are %Y %m %d %H %M %S %y %%. strptime takes a number shorter than its width (3/1/2026) and pivots %y at 69. Text that does not fit the format, or a day that does not exist, is an error that stops the statement, as a failed CAST does — one bad date in a file loads nothing; try_strptime makes it null instead and the load goes on.

Bits

The bitwise operators & | ^ << >> ~ are under Expressions and types.

functionreturns
bit_count(n)the number of set bits
to_hex(n)the integer as lowercase hex
from_hex(s)the integer the hex text spells