Skip to main content
Version: 0.3.36

SQL Interface Overview

RaisinDB exposes its content model through SQL. The dialect is PostgreSQL-flavoured (the parser is the PostgreSQL dialect of sqlparser), extended with statements and functions for hierarchical paths, branches, graph relations, JSON properties, full-text and vector search, and schema definitions.

You can run SQL over HTTP (POST /api/sql/{repo} or POST /api/sql/{repo}/{branch}), from psql over the PostgreSQL wire protocol, from the JavaScript client, and from server-side functions via raisin.sql.

curl -s -X POST localhost:8090/api/sql/docs-sql \
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"sql":"SELECT id, path, properties->>'"'"'title'"'"' AS title FROM '"'"'blog'"'"' WHERE path = $1", "params":["/hello"]}'
{"columns":["id","path","title"],"rows":[{"id":"eebfcd9f-9a5b-4ec9-89aa-421294f3278b","path":"/hello","title":"Hello"}],"row_count":1,"execution_time_ms":1}

Every response has the same shape: columns, rows (one JSON object per row), row_count and execution_time_ms. Statements that change data return one row with affected_rows; DDL returns result and success; EXPLAIN returns a QUERY PLAN column. Bound parameters are positional ($1, $2, ...) and passed in the params array.

The workspace is the table

There is no global nodes table. Each workspace is a table, and the table name in FROM, INSERT INTO, UPDATE and DELETE FROM is the workspace name. Quote it as a string literal so names with a colon or a hyphen work:

SELECT path, name FROM 'blog' ORDER BY path;
SELECT name FROM 'raisin:access_control' WHERE node_type = 'raisin:Role';

Four reserved names read the schema registry instead of content: NodeTypes, Archetypes, ElementTypes and Workspaces. See Schema Tables.

Node columns

Every workspace table has the same columns. SELECT * returns them in this order:

ColumnTypeDescription
idTEXTNode id (a UUID). You may supply one on INSERT; otherwise the server generates it.
pathPATHHierarchical path, e.g. /news/first. Unique within a workspace.
nameTEXTLast path segment. Defaults to the last segment of path on INSERT.
node_typeTEXTNodeType name, e.g. raisin:Page. Fixed after creation.
archetypeTEXTArchetype name, or NULL.
propertiesJSONBAll user data. Read fields with -> and ->>.
parent_nameTEXTName of the parent node (/ for root-level nodes).
versionINTVersion counter.
created_at, updated_atTIMESTAMPTZSet by the server.
published_at, published_byTIMESTAMPTZ, TEXTSet by publishing.
created_by, updated_byTEXTActor ids.
translationsJSONBPer-locale property overrides, or NULL.
owner_idTEXTOwning user, or NULL.
relationsJSONBGraph relations stored on the node, or NULL.
parent_pathPATHFilled by hierarchy traversals; NULL on plain scans.
depthINTNumber of path segments (/news/first is 2).
localeTEXTLocale the row was rendered in.
__revision, __branch, __workspaceTEXTRevision id, branch name and workspace name of the row. __branch is also usable in WHERE to read another branch.
__order, __tree_orderTEXTEditorial (drag-and-drop) sort keys. See SELECT.

All user-defined data lives in properties. A Page's title is properties->>'title', not a title column.

Statements

FamilyStatementsReference
QuerySELECT (joins, GROUP BY, HAVING, DISTINCT, UNION/INTERSECT/EXCEPT, ORDER BY, LIMIT/OFFSET, WITH, subqueries, window functions, EXPLAIN)SELECT
DataINSERT (including INSERT ... SELECT), UPSERT, UPDATE, DELETE, each with an optional RETURNINGINSERT, UPDATE, DELETE
SchemaCREATE / ALTER / DROP for NODETYPE, MIXIN, ARCHETYPE, ELEMENTTYPEDDL
BranchesCREATE / ALTER / DROP / MERGE BRANCH, USE BRANCH, SHOW BRANCHES, SHOW CURRENT BRANCH, SHOW CONFLICTS, SHOW DIVERGENCE, BEGIN / COMMITBranch statements
GraphRELATE, UNRELATE, MOVE, COPY, ORDER, RESTORE, TRANSLATEGraph DML
Access controlCREATE / ALTER / DROP ROLE, GROUP, USER; ALTER SECURITY CONFIG; SHOW ROLES, SHOW USERS, SHOW GROUPS, SHOW SECURITY CONFIG
AI and vectorsSHOW AI CONFIG, ALTER AI CONFIG, SHOW EMBEDDING CONFIG, ALTER EMBEDDING CONFIG, SHOW / VERIFY / REBUILD VECTOR INDEXVector functions
SpatialALTER SPATIAL INDEX, REBUILD SPATIAL INDEX, SHOW SPATIAL INDEXGeospatial functions

Several statements separated by ; can be sent in one request; they run in order.

Working with properties

-- text value
SELECT properties->>'title' AS title FROM 'blog';

-- JSON value (object, array, number, boolean)
SELECT properties->'tags' AS tags FROM 'blog';

-- nested field
SELECT properties->'author'->>'name' AS author FROM 'blog';

-- numbers: ->> yields text, so cast before comparing
SELECT name FROM 'blog' WHERE (properties->>'views')::INT > 20;

-- containment
SELECT name FROM 'blog' WHERE properties @> '{"published": true}';

-- bound parameter
SELECT name FROM 'blog' WHERE properties->>'title' = $1;

JSON literals in INSERT and UPDATE must be cast: '{"title":"Hello"}'::jsonb. The full operator list is in Operators; the function list is in the pages under Functions.

Hierarchy

Paths are first-class. CHILD_OF('/news') and DESCENDANT_OF('/news') select by position in the tree, DEPTH(path), PARENT(path) and ANCESTOR(path, n) compute with paths, and REFERENCES('blog:/news/second') finds nodes whose reference properties point at a node. See Path functions.

Type coercion and casting

Integer and float literals combine freely; arithmetic produces DOUBLE. Casts use ::type or CAST(x AS type) with these names: INT, BIGINT, DOUBLE, TEXT, BOOLEAN, TIMESTAMP, TIMESTAMPTZ, INTERVAL, JSONB, PATH, UUID, GEOMETRY, TSVECTOR, TSQUERY. NUMERIC and DECIMAL are not accepted; use DOUBLE. See Data Types.

Case sensitivity

Keywords and function names are case-insensitive. Column names, workspace names, paths, node type names and string comparisons are case-sensitive. LIKE is case-sensitive; ILIKE is not.

Functions

Each page under Functions lists the functions the server implements, with executed examples: string, numeric, JSON, path, date and time, aggregate, window, system, plus full-text, vector, geospatial and graph.

Subqueries and set operations

A SELECT can be filtered by HAVING, combined with another query using UNION, UNION ALL, INTERSECT or EXCEPT, and can carry subqueries in FROM, in IN (SELECT ...), in EXISTS, behind ANY / ALL, and as a scalar value. Subqueries are independent: one cannot reference a column of the query that contains it. See SELECT.

INSERT accepts a query as its source, and INSERT, UPDATE and DELETE each take a RETURNING list that reports the rows they wrote.