Skip to content

SQL Feature Matrix

This document provides a comprehensive matrix of SQL features, comparing support across DecentDB, SQLite, PostgreSQL, and DuckDB.

See also: Comparison Overview for a narrative discussion of design differences, trade-offs, and architecture.

Local-first sync and replication

This section compares built-in database surfaces. SQLite, PostgreSQL, and DuckDB can all participate in application-designed synchronization systems, but DecentDB includes local-first sync as an engine feature with CLI, SQL inspection, and SDK surfaces.

Feature DecentDB SQLite PostgreSQL DuckDB
Durable local change journal ✅ (sidecar sync journal) ⚠️ (session extension or triggers; app-managed) ⚠️ (WAL/logical decoding; server-managed) ❌
Replica identity ✅ ❌ ⚠️ (server/publication identity concepts) ❌
Peer catalog ✅ ❌ ⚠️ (subscription catalogs; not embedded local-first peers) ❌
Manual batch export/import ✅ (sync export / sync import) ⚠️ (changesets via optional extension) ⚠️ (logical replication/dump tooling) ❌
Scoped row replication ✅ (named scopes with validated row filters) ❌ ⚠️ (publication filters; server replication) ❌
Conflict recording and manual resolution ✅ (sync conflicts, sync conflict show/resolve/reopen) ❌ ⚠️ (replication conflicts exist, but no embedded app conflict workflow) ❌
Operational sync inspection ✅ (sys.* inspection views; sys_sync_* compatibility) ❌ ⚠️ (system catalogs and monitoring views, server-oriented) ❌
Sync doctor, retention, peer lag, prune ✅ ❌ ⚠️ (separate server administration surfaces) ❌
Local HTTP sync dev transport ✅ (sync run / sync serve) ❌ ⚠️ (server replication protocols) ❌
Typed .NET sync SDK ✅ (DecentDBSyncClient) ❌ ❌ ❌

DecentDB sync is intentionally conservative by default: conflicts are recorded and inspectable instead of silently hidden behind global last-write-wins behavior.

Examples

decentdb sync status --db=app.ddb --format=table
decentdb sync export --db=app.ddb --since=0 --limit=100 --output=changes.json
decentdb sync import --db=peer.ddb --input=changes.json
decentdb sync conflicts --db=peer.ddb --format=table
SELECT * FROM sys_sync_status;
SELECT * FROM sys.sync_status;
SELECT * FROM sys.wal_metrics;
SELECT * FROM sys.write_queue_metrics;
SELECT * FROM sys.storage_metrics;
SELECT * FROM sys.reactive_metrics;
SELECT * FROM sys.reactive_subscriptions;
SELECT * FROM sys_sync_journal WHERE sequence > 100 ORDER BY sequence;
SELECT * FROM sys_sync_conflicts ORDER BY conflict_id;
SELECT * FROM sys_sync_doctor;

See Local-first sync for the full guide.

Local data security

This section compares local database security surfaces. DecentDB is embedded and does not implement server users or roles; host applications control who can open a database handle and execute SQL.

Feature DecentDB SQLite PostgreSQL DuckDB
Transparent local database encryption ✅ (TDE open config) ⚠️ (SQLCipher or other extension/build) ⚠️ (cluster/file-system encryption; server-oriented) ❌
WAL and sidecar encryption through the same local key ✅ ⚠️ (extension-dependent) ⚠️ (deployment-dependent) ❌
Durable row policies in an embedded engine ✅ (CREATE POLICY) ❌ ✅ (row-level security) ❌
Durable projection masks ✅ (CREATE MASK) ❌ ⚠️ (views/policies/extensions) ❌
Connection-local audit context functions ✅ (current_tenant, current_actor) ❌ ⚠️ (session settings/current user) ❌
Queryable local security audit events ✅ (__decentdb_audit_events) ❌ ⚠️ (audit extensions/logging) ❌
SET AUDIT CONTEXT tenant_id = 'tenant-a';
CREATE POLICY tenant_filter ON invoices USING tenant_id = current_tenant();
CREATE MASK ssn_mask ON employees(ssn) USING '***-**-' || right(ssn, 4);

See Local Data Security for the full DecentDB contract.

Branch, diff, restore, and time travel

This section compares built-in database workflow surfaces. Other engines can approximate many workflows with file copies, backups, transactions, sessions, or external tools; DecentDB exposes these as named database operations.

Feature DecentDB SQLite PostgreSQL DuckDB
Named retained snapshots ✅ (snapshot create/list/delete) ⚠️ (backup/session APIs; app-managed names/retention) ⚠️ (server backups, restore points, PITR; server-managed) ⚠️ (database/file snapshots; app-managed)
Read-only time-travel query by name ✅ (exec --as-of) ❌ ⚠️ (PITR/temporal patterns, not embedded per-DB named snapshots) ❌
Branch-local writes ✅ (exec --branch, repl --branch) ❌ ❌ ❌
Branch diff ✅ (primary-key row diff; JSON/table output) ❌ ❌ ❌
Guarded branch restore ✅ (branch restore --dry-run/--confirm) ❌ ⚠️ (restore/PITR outside a live embedded DB workflow) ❌
Constrained branch merge ✅ (clean primary-key row changes; conflicts stop) ❌ ❌ ❌
C ABI workflow bridge ✅ (ddb_db_branch_execute_json) N/A N/A N/A

DDL (Data Definition Language)

Feature DecentDB SQLite PostgreSQL DuckDB
CREATE TABLE ✅ ✅ ✅ ✅
DROP TABLE ✅ ✅ ✅ ✅
CREATE INDEX ✅ ✅ ✅ ✅
Full-text indexes and BM25 ranking ✅ (USING fulltext, bm25) ✅ (FTS5) ✅ (tsvector/GIN) ⚠️ (extension-dependent)
Spatial indexes ✅ (USING spatial) ⚠️ (RTree extension) ✅ (GiST/SP-GiST via PostGIS) ✅ (spatial extension)
Covering indexes (INCLUDE (...)) ✅ (BTREE key-column indexes) ❌ ✅ ❌
DROP INDEX ✅ ✅ ✅ ✅
ALTER TABLE ADD COLUMN ✅ ✅ ✅ ✅
ALTER TABLE DROP COLUMN ✅ ✅ ✅ ✅
ALTER TABLE RENAME TO ✅ ✅ ✅ ✅
ALTER TABLE RENAME COLUMN ✅ ✅ (via ALTER TABLE RENAME) ✅ ✅
ALTER TABLE ALTER COLUMN TYPE ✅ ✅ ✅ ✅
ALTER TABLE ADD CONSTRAINT (CHECK) ✅ ✅ ✅ ✅
ALTER TABLE DROP CONSTRAINT (CHECK) ✅ ⚠️ (limited support) ✅ ✅
CREATE VIEW ✅ ✅ ✅ ✅
DROP VIEW ✅ ✅ ✅ ✅
CREATE TRIGGER ✅ ✅ ✅ ❌
DROP TRIGGER ✅ ✅ ✅ ❌
CREATE TEMP TABLE ✅ ✅ ✅ ✅
CREATE TEMP VIEW ✅ ✅ ✅ ✅
Generated columns (STORED, VIRTUAL) ✅ ✅ ✅ ✅
Table-level FOREIGN KEY ✅ ✅ ✅ ⚠️ (parsed, not enforced)
CREATE SCHEMA ✅ (catalog registration) ✅ ✅ ✅

DecentDB note: ALTER TABLE ... ADD/DROP CONSTRAINT supports named CHECK, FOREIGN KEY, and UNIQUE constraints, validates existing rows before commit, persists across reopen, and rejects duplicate constraint names.

Examples

-- CREATE TABLE with constraints
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
  amount DECIMAL(10,2) NOT NULL
);

-- Table-level FOREIGN KEY
CREATE TABLE order_items (
  order_id INTEGER,
  product_id INTEGER,
  qty INTEGER NOT NULL,
  FOREIGN KEY (order_id) REFERENCES orders(id),
  FOREIGN KEY (product_id) REFERENCES products(id)
);

-- CREATE INDEX
CREATE INDEX idx_users_name ON users (name);
CREATE INDEX idx_orders_user ON orders (user_id) WHERE user_id IS NOT NULL;
CREATE INDEX idx_places_geog ON places USING spatial(geog);

-- ALTER TABLE
ALTER TABLE users ADD COLUMN email TEXT;
ALTER TABLE users DROP COLUMN email;
ALTER TABLE users RENAME TO customers;
ALTER TABLE users RENAME COLUMN name TO full_name;
ALTER TABLE users ALTER COLUMN name TYPE VARCHAR(255);
ALTER TABLE users ADD CONSTRAINT chk_email CHECK (email LIKE '%@%');
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE (email);
ALTER TABLE orders ADD CONSTRAINT fk_orders_user
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE users DROP CONSTRAINT chk_email;
ALTER TABLE orders DROP CONSTRAINT fk_orders_user;

-- CREATE VIEW
CREATE VIEW user_orders AS
  SELECT u.name, o.id FROM users u LEFT JOIN orders o ON u.id = o.user_id;
CREATE OR REPLACE VIEW active_users AS
  SELECT * FROM users WHERE active = TRUE;

-- TEMP objects (session-scoped, not persisted)
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, val TEXT);
CREATE TEMP VIEW recent_orders AS SELECT * FROM orders WHERE id > 100;

-- Generated columns (STORED or VIRTUAL)
CREATE TABLE products (
  id INTEGER PRIMARY KEY,
  price DECIMAL(10,2),
  tax_rate DECIMAL(4,2),
  total_stored DECIMAL(10,2) GENERATED ALWAYS AS (price * (1 + tax_rate)) STORED,
  total_virtual DECIMAL(10,2) GENERATED ALWAYS AS (price * (1 + tax_rate)) VIRTUAL
);

-- CREATE TRIGGER (AFTER row trigger)
CREATE TRIGGER log_insert AFTER INSERT ON users
FOR EACH ROW BEGIN
  SELECT decentdb_exec_sql('INSERT INTO audit_log (msg) VALUES (''user added'')');
END;

DML (Data Manipulation Language)

Feature DecentDB SQLite PostgreSQL DuckDB
SELECT ✅ ✅ ✅ ✅
INSERT ✅ ✅ ✅ ✅
UPDATE ✅ ✅ ✅ ✅
DELETE ✅ ✅ ✅ ✅
INSERT ... RETURNING ✅ ❌ ✅ ✅
UPDATE ... RETURNING ✅ ❌ ✅ ✅
DELETE ... RETURNING ✅ ❌ ✅ ✅
TRUNCATE TABLE ✅ ❌ ✅ ✅
INSERT ... ON CONFLICT ✅ (DO NOTHING/DO UPDATE) ✅ (ON CONFLICT) ✅ (ON CONFLICT) ✅ (ON CONFLICT)
Bulk INSERT ✅ ✅ ✅ ✅

DecentDB note: TRUNCATE TABLE supports RESTART IDENTITY, CONTINUE IDENTITY, and CASCADE, participates in transaction rollback, and rejects unsupported targets such as temp tables and views.

Examples

-- INSERT with RETURNING
INSERT INTO users (name) VALUES ('Alice') RETURNING id;

-- INSERT ON CONFLICT (upsert)
INSERT INTO users (id, name) VALUES (1, 'Alice')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;

-- ON CONFLICT DO NOTHING
INSERT INTO users (id, name) VALUES (1, 'Alice')
ON CONFLICT (id) DO NOTHING;

-- Bulk INSERT (multiple rows)
INSERT INTO users (name) VALUES ('Alice'), ('Bob'), ('Charlie');

-- UPDATE with WHERE
UPDATE orders SET amount = amount * 1.1 WHERE user_id = 1;

-- UPDATE ... RETURNING
UPDATE orders SET amount = amount * 1.1 WHERE user_id = 1 RETURNING id, amount;

-- DELETE with WHERE
DELETE FROM orders WHERE amount < 1.00;

-- DELETE ... RETURNING
DELETE FROM orders WHERE amount < 1.00 RETURNING id, amount;

-- TRUNCATE TABLE
TRUNCATE TABLE temp_events;
TRUNCATE TABLE users RESTART IDENTITY;
TRUNCATE TABLE orders CASCADE;

JOINs

Feature DecentDB SQLite PostgreSQL DuckDB
INNER JOIN ✅ ✅ ✅ ✅
LEFT JOIN ✅ ✅ ✅ ✅
RIGHT JOIN ✅ (rewritten as LEFT JOIN) ✅ ✅ ✅
FULL OUTER JOIN ✅ ✅ ✅ ✅
CROSS JOIN ✅ ✅ ✅ ✅
NATURAL JOIN ✅ ✅ ✅ ✅

Examples

-- INNER JOIN
SELECT u.name, o.amount
FROM users u INNER JOIN orders o ON u.id = o.user_id;

-- LEFT JOIN (all users, even those without orders)
SELECT u.name, o.amount
FROM users u LEFT JOIN orders o ON u.id = o.user_id;

-- RIGHT JOIN (all orders, even those without matching users)
SELECT u.name, o.amount
FROM users u RIGHT JOIN orders o ON u.id = o.user_id;

-- FULL OUTER JOIN (all rows from both sides)
SELECT u.name, o.amount
FROM users u FULL OUTER JOIN orders o ON u.id = o.user_id;

-- CROSS JOIN (cartesian product)
SELECT u.name, p.name AS product
FROM users u CROSS JOIN products p;

-- NATURAL JOIN (join on columns with matching names)
SELECT * FROM orders NATURAL JOIN order_details;

Queries and Clauses

Feature DecentDB SQLite PostgreSQL DuckDB
WHERE ✅ ✅ ✅ ✅
ORDER BY ✅ ✅ ✅ ✅
LIMIT/OFFSET ✅ ✅ ✅ ✅
GROUP BY ✅ ✅ ✅ ✅
HAVING ✅ ✅ ✅ ✅
DISTINCT ✅ ✅ ✅ ✅
DISTINCT ON ✅ ❌ ✅ ✅
LIMIT ALL ✅ ✅ ✅ ✅
OFFSET with FETCH ✅ ✅ ✅ ✅
Standalone VALUES (...) queries ✅ ✅ ✅ ✅
FROM (VALUES ...) AS alias(...) ✅ ✅ ✅ ✅
CREATE TABLE ... AS SELECT (CTAS) ✅ ✅ ✅ ✅
CTAS WITH NO DATA ✅ ❌ ✅ ✅ (WITH NO DATA)
LATERAL subqueries/table functions ✅ ⚠️ (implicit only) ✅ ✅

DecentDB note: VALUES works both as a standalone query body and as a table source in FROM, joins, INSERT ... SELECT, and row-value membership tests such as WHERE (a, b) IN (VALUES (...), (...)).

Examples

-- Basic filtering and sorting
SELECT id, name FROM users WHERE id > 10 ORDER BY name LIMIT 5;

-- GROUP BY with HAVING
SELECT user_id, COUNT(*) AS order_count
FROM orders GROUP BY user_id HAVING COUNT(*) > 5;

-- DISTINCT ON (first order per user, by date)
SELECT DISTINCT ON (user_id) user_id, id, created_at
FROM orders ORDER BY user_id, created_at DESC;

-- OFFSET with FETCH (SQL-standard pagination)
SELECT * FROM users ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

-- LIMIT / OFFSET (traditional form)
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20;

-- Standalone VALUES query body
VALUES (1, 'one'), (2, 'two');

-- VALUES as table source
SELECT t.id, t.label
FROM (VALUES (1, 'one'), (2, 'two')) AS t(id, label)
ORDER BY t.id;

-- VALUES feeding an INSERT ... SELECT pipeline
INSERT INTO labels (id, label)
SELECT * FROM (VALUES (1, 'one'), (2, 'two')) AS src(id, label);

-- Row-value membership against VALUES
SELECT *
FROM labels
WHERE (id, label) IN (VALUES (1, 'one'), (3, 'three'));

-- CTAS with inferred schema
CREATE TABLE active_users AS
SELECT id, name FROM users WHERE active = TRUE;

-- CTAS structure only (no row copy)
CREATE TABLE active_users_template AS
SELECT id, name FROM users WITH NO DATA;

-- LATERAL subquery
SELECT u.id, x.twice
FROM users u
JOIN LATERAL (SELECT u.id * 2 AS twice) AS x ON true;

Aggregate Functions

Feature DecentDB SQLite PostgreSQL DuckDB
COUNT(*) ✅ ✅ ✅ ✅
COUNT(expr) ✅ ✅ ✅ ✅
SUM ✅ ✅ ✅ ✅
AVG ✅ ✅ ✅ ✅
MIN ✅ ✅ ✅ ✅
MAX ✅ ✅ ✅ ✅
GROUP_CONCAT ✅ ✅ ✅ ❌ (use STRING_AGG)
STRING_AGG ✅ ❌ ✅ ✅
TOTAL ✅ ✅ ❌ ❌
COUNT(DISTINCT) ✅ ✅ ✅ ✅
SUM(DISTINCT) ✅ ✅ ✅ ✅
AVG(DISTINCT) ✅ ✅ ✅ ✅
STDDEV()/STDDEV_SAMP()/STDDEV_POP() ✅ ❌ ✅ ✅
VARIANCE()/VAR_SAMP()/VAR_POP() ✅ ❌ ✅ ✅
BOOL_AND()/BOOL_OR() ✅ ❌ ✅ ✅
ARRAY_AGG() ✅ (JSON text array) ❌ (use json_group_array) ✅ ✅
MEDIAN() ✅ ❌ ❌ (use percentile) ✅
PERCENTILE_CONT()/PERCENTILE_DISC() WITHIN GROUP ✅ ❌ ✅ ✅

Examples

-- Basic aggregates
SELECT COUNT(*), SUM(amount), AVG(amount), MIN(amount), MAX(amount) FROM orders;

-- DISTINCT aggregates (unique values only)
SELECT COUNT(DISTINCT user_id) AS unique_customers,
       SUM(DISTINCT amount) AS distinct_totals,
       AVG(DISTINCT amount) AS avg_distinct
FROM orders;

-- GROUP_CONCAT (concatenate values into a string)
SELECT department, GROUP_CONCAT(name, ', ') FROM employees GROUP BY department;

-- STRING_AGG (Postgres-style equivalent)
SELECT department, STRING_AGG(name, ', ') FROM employees GROUP BY department;

-- TOTAL (returns 0.0 for empty sets, unlike SUM which returns NULL)
SELECT TOTAL(amount) FROM orders WHERE 1 = 0;  -- returns 0.0
SELECT SUM(amount) FROM orders WHERE 1 = 0;    -- returns NULL

-- Statistical aggregates
SELECT STDDEV(amount), VARIANCE(amount), BOOL_AND(amount > 0), BOOL_OR(amount > 100) FROM orders;

-- Ordered and collection aggregates
SELECT ARRAY_AGG(amount ORDER BY created_at) FROM orders;
SELECT MEDIAN(amount) FROM orders;
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) FROM orders;
SELECT PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY amount) FROM orders;

Window Functions

Feature DecentDB SQLite PostgreSQL DuckDB
ROW_NUMBER() ✅ ✅ ✅ ✅
RANK() ✅ ✅ ✅ ✅
DENSE_RANK() ✅ ✅ ✅ ✅
LAG() ✅ ✅ ✅ ✅
LEAD() ✅ ✅ ✅ ✅
FIRST_VALUE() ✅ ✅ ✅ ✅
LAST_VALUE() ✅ ✅ ✅ ✅
NTH_VALUE() ✅ ✅ ✅ ✅
NTILE() ✅ ✅ ✅ ✅
PERCENT_RANK() ✅ ✅ ✅ ✅
CUME_DIST() ✅ ✅ ✅ ✅
Aggregate windows (SUM/AVG/COUNT/MIN/MAX ... OVER) ✅ ✅ ✅ ✅
ROWS frame clauses ✅ ✅ ✅ ✅
RANGE frame clauses ⚠️ (CURRENT ROW / UNBOUNDED bounds) ✅ ✅ ✅

Examples

-- ROW_NUMBER, RANK, DENSE_RANK
SELECT name, department, salary,
       ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
       RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rnk,
       DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rnk
FROM employees;

-- LAG / LEAD (access previous/next row values)
SELECT name, salary,
       LAG(salary, 1)  OVER (ORDER BY salary) AS prev_salary,
       LEAD(salary, 1) OVER (ORDER BY salary) AS next_salary
FROM employees;

-- FIRST_VALUE / LAST_VALUE / NTH_VALUE
SELECT name, department, salary,
       FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary DESC) AS top_earner,
       LAST_VALUE(name)  OVER (PARTITION BY department ORDER BY salary DESC) AS low_earner,
       NTH_VALUE(name, 2) OVER (PARTITION BY department ORDER BY salary DESC) AS second_earner
FROM employees;

-- Additional ranking/distribution functions
SELECT name, salary,
       NTILE(4) OVER (ORDER BY salary DESC) AS quartile,
       PERCENT_RANK() OVER (ORDER BY salary) AS pct_rank,
       CUME_DIST() OVER (ORDER BY salary) AS cume_dist
FROM employees;

-- Aggregate window with ROWS frame
SELECT created_at, amount,
       SUM(amount) OVER (
         ORDER BY created_at
         ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
       ) AS rolling_sum
FROM orders;

Scalar Functions

Math Functions

Function DecentDB SQLite PostgreSQL DuckDB
ABS() ✅ ✅ ✅ ✅
CEIL()/CEILING() ✅ ✅ ✅ ✅
FLOOR() ✅ ✅ ✅ ✅
ROUND() ✅ ✅ ✅ ✅
SQRT() ✅ ✅ ✅ ✅
POWER()/POW() ✅ ✅ ✅ ✅
MOD() ✅ ✅ ✅ ✅
SIGN() ✅ ✅ ✅ ✅
LN() ✅ ❌ ✅ ✅
LOG() ✅ ❌ ✅ ✅
EXP() ✅ ✅ ✅ ✅
SIN()/COS()/TAN() ✅ ⚠️ (extension-dependent) ✅ ✅
ASIN()/ACOS()/ATAN()/ATAN2() ✅ ⚠️ (extension-dependent) ✅ ✅
PI()/DEGREES()/RADIANS()/COT() ✅ ⚠️ (extension-dependent) ✅ ✅
RANDOM() ✅ (returns FLOAT64) ✅ (returns INT64) ✅ (returns FLOAT64) ✅ (returns DOUBLE)

Conditional Functions

Function DecentDB SQLite PostgreSQL DuckDB
COALESCE() ✅ ✅ ✅ ✅
NULLIF() ✅ ✅ ✅ ✅
GREATEST() ✅ ❌ ✅ ✅
LEAST() ✅ ❌ ✅ ✅
IIF() ✅ ✅ ❌ (use CASE) ✅

String Functions

Function DecentDB SQLite PostgreSQL DuckDB
LENGTH() ✅ ✅ ✅ ✅
LOWER() ✅ ✅ ✅ ✅
UPPER() ✅ ✅ ✅ ✅
TRIM() ✅ ✅ ✅ ✅
LTRIM() ✅ ✅ ✅ ✅
RTRIM() ✅ ✅ ✅ ✅
SUBSTR()/SUBSTRING() ✅ ✅ ✅ ✅
REPLACE() ✅ ✅ ✅ ✅
INSTR() ✅ ✅ ✅ ✅ (via strpos)
LEFT() ✅ ❌ ✅ ✅
RIGHT() ✅ ❌ ✅ ✅
LPAD() ✅ ❌ ✅ ✅
RPAD() ✅ ❌ ✅ ✅
REPEAT() ✅ ✅ ✅ ✅
REVERSE() ✅ ✅ ✅ ✅
CHR() ✅ ❌ (uses CHAR) ✅ ✅
HEX() ✅ ✅ ✅ ✅
CONCAT() ✅ ❌ ✅ ✅
CONCAT_WS() ✅ ❌ ✅ ✅
POSITION() ✅ ✅ ✅ ✅
INITCAP() ✅ ❌ ✅ ✅
ASCII() ✅ ✅ ✅ ✅
REGEXP_REPLACE() ✅ ❌ ✅ ✅
SPLIT_PART() ✅ ❌ ✅ ✅
STRING_TO_ARRAY() ✅ ❌ ✅ ✅
QUOTE_IDENT() ✅ ❌ ✅ ✅
QUOTE_LITERAL() ✅ ❌ ✅ ✅
MD5() ✅ ❌ ✅ ✅
SHA256() ✅ ❌ ✅ ✅

Date/Time Functions

Function DecentDB SQLite PostgreSQL DuckDB
NOW() ✅ ❌ ✅ ✅
CURRENT_TIMESTAMP ✅ ✅ ✅ ✅
CURRENT_DATE ✅ ✅ ✅ ✅
CURRENT_TIME ✅ ✅ ✅ ✅
date() ✅ ✅ ✅ (different) ❌ (use CAST)
datetime() ✅ ✅ ✅ (different) ❌ (use CAST)
strftime() ✅ ✅ ❌ ✅
EXTRACT() ✅ ❌ ✅ ✅
DATE_TRUNC() ✅ ❌ ✅ ✅
DATE_PART() ✅ ❌ ✅ ✅
DATE_DIFF() ✅ ❌ ❌ ✅
LAST_DAY() ✅ ❌ ❌ ✅
NEXT_DAY() ✅ ❌ ❌ ✅
MAKE_DATE() ✅ ❌ ✅ ✅
MAKE_TIMESTAMP() ✅ ❌ ✅ ✅
TO_TIMESTAMP() ✅ ❌ ✅ ✅
AGE() ✅ ❌ ✅ ✅
TIMESTAMP ± INTERVAL ✅ ❌ ✅ ✅

JSON Functions

Function DecentDB SQLite PostgreSQL DuckDB
JSON_EXTRACT() ✅ ✅ ✅ (->) ✅
JSON_ARRAY_LENGTH() ✅ ✅ ✅ ✅
json_type() ✅ ✅ ✅ ✅
json_valid() ✅ ✅ ✅ ✅
json_object() ✅ ✅ ✅ ✅
json_array() ✅ ✅ ✅ ✅
-> ✅ ✅ ✅ ✅
->> ✅ ✅ ✅ ✅
json_each() ✅ ✅ ❌ ❌ (use unnest)
json_tree() ✅ ✅ ❌ ❌

Table-Valued And Compatibility Functions

Function / Surface DecentDB SQLite PostgreSQL DuckDB
generate_series() ✅ ❌ ✅ ✅
pragma_table_info() ✅ ✅ ❌ ❌
pragma_table_xinfo() ✅ ✅ ❌ ❌
pragma_table_list() ✅ ✅ ❌ ❌
pragma_index_list() ✅ ✅ ❌ ❌
pragma_index_info() ✅ ✅ ❌ ❌
pragma_index_xinfo() ✅ ✅ ❌ ❌
pragma_foreign_key_list() ✅ ✅ ❌ ❌
pragma_database_list() ✅ ✅ ❌ ❌
current_database() / current_schema() ✅ ❌ ✅ ❌
database() / schema() ✅ ❌ ❌ ✅
version() ✅ ✅ ✅ ✅

Spatial Functions

Function DecentDB SQLite PostgreSQL DuckDB
GEOMETRY / GEOGRAPHY constructors ✅ ⚠️ (extension) ✅ (PostGIS) ✅ (spatial extension)
WKT/WKB/GeoJSON import/export ✅ ⚠️ (extension) ✅ (PostGIS) ✅ (spatial extension)
ST_Distance() / ST_DWithin() ✅ ⚠️ (extension) ✅ (PostGIS) ✅ (spatial extension)
ST_Intersects()/ST_Contains()/ST_Within()/ST_Equals() ✅ ⚠️ (extension) ✅ (PostGIS) ✅ (spatial extension)
ST_Length()/ST_Area() ✅ ⚠️ (extension) ✅ (PostGIS) ✅ (spatial extension)
Distance operator <-> ✅ ❌ ✅ (PostGIS) ❌

Math Examples

SELECT ABS(-42), CEIL(3.2), FLOOR(3.8), ROUND(3.14159, 2);
SELECT SQRT(144), POWER(2, 10), MOD(17, 5), SIGN(-99);
SELECT LN(2.71828), LOG(1000), EXP(1);
SELECT SIN(PI() / 2), COS(0), TAN(PI() / 4), DEGREES(PI()), RADIANS(180);
SELECT RANDOM();  -- returns a random FLOAT64 in [0.0, 1.0)

String Examples

SELECT LENGTH('hello'), LOWER('HELLO'), UPPER('hello');
SELECT TRIM('  hello  '), LTRIM('  hello'), RTRIM('hello  ');
SELECT SUBSTR('hello world', 1, 5), REPLACE('hello', 'l', 'r');
SELECT INSTR('hello world', 'world');  -- returns 7
SELECT LEFT('hello', 3), RIGHT('hello', 3);  -- 'hel', 'llo'
SELECT LPAD('42', 5, '0'), RPAD('hi', 5, '!');  -- '00042', 'hi!!!'
SELECT REPEAT('ab', 3), REVERSE('hello');  -- 'ababab', 'olleh'
SELECT CHR(65), HEX('ABC');  -- 'A', '414243'
SELECT CONCAT('hello', ' ', 'world'), CONCAT_WS('-', '2024', '03', '25');
SELECT POSITION('world' IN 'hello world'), INITCAP('hello world');
SELECT REGEXP_REPLACE('abc123def', '\d', '', 'g');
SELECT SPLIT_PART('a,b,c', ',', 2), STRING_TO_ARRAY('a,b,c', ',');
SELECT QUOTE_IDENT('table name'), QUOTE_LITERAL('O''Brien');
SELECT MD5('hello'), SHA256('hello');
SELECT GREATEST(10, 7, 12), LEAST(10, 7, 12), IIF(2 > 1, 'yes', 'no');

Date/Time Examples

-- Current date/time values
SELECT NOW(), CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME;

-- date() and datetime() (SQLite-compatible)
SELECT date('now'), date('2024-03-15', '+1 month');
SELECT datetime('now'), datetime('2024-03-15 10:30:00', '+2 hours');

-- strftime() formatting
SELECT strftime('%Y-%m-%d', 'now');
SELECT strftime('%H:%M:%S', '2024-03-15 14:30:00');
SELECT strftime('%Y', '2024-03-15');  -- '2024'

-- EXTRACT() (Postgres-compatible)
SELECT EXTRACT(YEAR FROM CURRENT_TIMESTAMP);
SELECT EXTRACT(MONTH FROM '2024-03-15');
SELECT EXTRACT(DOW FROM '2024-03-15');  -- day of week

-- Extended date/time helpers
SELECT DATE_TRUNC('month', '2024-03-15 14:30:45');
SELECT DATE_PART('doy', '2024-03-15');
SELECT DATE_DIFF('day', '2024-03-10', '2024-03-15');
SELECT LAST_DAY('2024-02-11'), NEXT_DAY('2024-03-15', 'Monday');
SELECT MAKE_DATE(2024, 3, 15), MAKE_TIMESTAMP(2024, 3, 15, 14, 30, 0);
SELECT TO_TIMESTAMP(1710505800), AGE('2024-03-15', '2024-03-14');
SELECT '2024-03-15 14:30:00'::timestamp + INTERVAL '1 day';

JSON Examples

-- JSON extraction
SELECT json_extract('{"name":"Alice","age":30}', '$.name');  -- 'Alice'
SELECT '{"name":"Alice"}'->>'name';  -- 'Alice' (text)
SELECT '{"name":"Alice"}'->'name';   -- '"Alice"' (JSON)

-- JSON construction
SELECT json_object('name', 'Alice', 'age', 30);  -- '{"name":"Alice","age":30}'
SELECT json_array(1, 2, 'three');  -- '[1,2,"three"]'

-- JSON inspection
SELECT json_type('{"a":1}');  -- 'object'
SELECT json_valid('{"a":1}');  -- 1 (true)
SELECT json_valid('not json');  -- 0 (false)
SELECT json_array_length('[1,2,3]');  -- 3

-- Table-valued: json_each (iterate array/object)
SELECT key, value FROM json_each('[10, 20, 30]');
-- Returns: (0, 10), (1, 20), (2, 30)

SELECT key, value FROM json_each('{"a":1,"b":2}');
-- Returns: ('a', 1), ('b', 2)

-- Table-valued: json_tree (recursive traversal)
SELECT key, value, type FROM json_tree('{"a":{"b":1},"c":[2,3]}');

Spatial Examples

CREATE TABLE places (id INT PRIMARY KEY, geog GEOGRAPHY(POINT,4326));
CREATE INDEX idx_places_geog ON places USING spatial(geog);
INSERT INTO places VALUES (1, ST_GeogPoint(-97.7431, 30.2672));

SELECT id
FROM places
WHERE ST_DWithin(geog, ST_GeogPoint(-97.7431, 30.2672), 5000);

SELECT h.id, z.id
FROM houses h
JOIN zones z
  ON ST_Contains(z.boundary, h.location);

SELECT ST_Area(ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))'));

Operators

Operator DecentDB SQLite PostgreSQL DuckDB
+ - * / ✅ ✅ ✅ ✅
% (modulo) ✅ ✅ ✅ ✅
|| (concat) ✅ ✅ ✅ ✅
LIKE/ILIKE ✅ ✅ ✅ ✅
COLLATE BINARY / NOCASE / RTRIM in queries ✅ ✅ ⚠️ (different names/semantics) ⚠️
BETWEEN ✅ ✅ ✅ ✅
IN ✅ ✅ ✅ ✅
EXISTS / NOT EXISTS ✅ ✅ ✅ ✅
op ANY (subquery) / op SOME (subquery) ✅ ✅ ✅ ✅
op ALL (subquery) ✅ ✅ ✅ ✅
~, ~*, !~, !~* (regex) ✅ ❌ ✅ ✅
IS NULL ✅ ✅ ✅ ✅
CASE ✅ ✅ ✅ ✅
COALESCE ✅ ✅ ✅ ✅
NULLIF ✅ ✅ ✅ ✅

Examples

-- Arithmetic and modulo
SELECT 10 + 3, 10 - 3, 10 * 3, 10 / 3, 10 % 3;

-- String concatenation
SELECT 'Hello' || ' ' || 'World';

-- Pattern matching
SELECT * FROM users WHERE name LIKE 'A%';
SELECT * FROM users WHERE name ILIKE '%alice%';  -- case-insensitive

-- Range and membership
SELECT * FROM orders WHERE amount BETWEEN 10.00 AND 100.00;
SELECT * FROM users WHERE id IN (1, 2, 3);
SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
SELECT * FROM employees WHERE salary > ANY (SELECT salary FROM peers);
SELECT * FROM employees WHERE salary >= ALL (SELECT salary FROM peers);
SELECT * FROM users WHERE name ~ '^A';
SELECT * FROM users WHERE name !~* '^admin';

-- NULL handling
SELECT * FROM users WHERE email IS NULL;
SELECT COALESCE(email, 'no-email@example.com') FROM users;
SELECT NULLIF(score, 0) FROM results;  -- returns NULL if score = 0

-- CASE expressions
SELECT name,
  CASE WHEN salary > 100000 THEN 'high'
       WHEN salary > 50000  THEN 'mid'
       ELSE 'low' END AS band
FROM employees;

Transaction Control

Feature DecentDB SQLite PostgreSQL DuckDB
BEGIN ✅ ✅ ✅ ✅
BEGIN IMMEDIATE ✅ (treated as BEGIN) ✅ ❌ ❌
COMMIT ✅ ✅ ✅ ✅
ROLLBACK ✅ ✅ ✅ ✅
SAVEPOINT ✅ ✅ ✅ ❌
RELEASE SAVEPOINT ✅ ✅ ✅ ❌
ROLLBACK TO SAVEPOINT ✅ ✅ ✅ ❌

Examples

-- Basic transaction
BEGIN;
INSERT INTO users (name) VALUES ('Alice');
INSERT INTO users (name) VALUES ('Bob');
COMMIT;

-- Rollback on error
BEGIN;
INSERT INTO users (name) VALUES ('Charlie');
ROLLBACK;  -- nothing committed

-- Savepoints (partial rollback within a transaction)
BEGIN;
INSERT INTO users (name) VALUES ('Alice');
SAVEPOINT sp1;
INSERT INTO users (name) VALUES ('Bob');
ROLLBACK TO SAVEPOINT sp1;  -- undoes Bob only
INSERT INTO users (name) VALUES ('Charlie');
RELEASE SAVEPOINT sp1;
COMMIT;  -- Alice and Charlie are committed

-- Nested savepoints
BEGIN;
SAVEPOINT outer;
INSERT INTO users (name) VALUES ('X');
SAVEPOINT inner;
INSERT INTO users (name) VALUES ('Y');
ROLLBACK TO SAVEPOINT inner;  -- undoes Y
RELEASE SAVEPOINT outer;
COMMIT;  -- only X is committed

Utility and Introspection Commands

Feature DecentDB SQLite PostgreSQL DuckDB
EXPLAIN <query> ✅ ✅ ✅ ✅
EXPLAIN ANALYZE <query> ✅ ✅ ✅ ✅
PRAGMA page_size ✅ ✅ ❌ ❌
PRAGMA cache_size ✅ ✅ ❌ ❌
PRAGMA integrity_check ✅ ✅ ❌ ❌
PRAGMA quick_check ✅ ✅ ❌ ❌
PRAGMA database_list ✅ ✅ ❌ ❌
PRAGMA table_info(table) ✅ ✅ ❌ ❌
PRAGMA table_xinfo(table) ✅ ✅ ❌ ❌
PRAGMA table_list ✅ ✅ ❌ ❌
PRAGMA index_list(table) ✅ ✅ ❌ ❌
PRAGMA index_info(index) ✅ ✅ ❌ ❌
PRAGMA index_xinfo(index) ✅ ✅ ❌ ❌
PRAGMA foreign_key_list(table) ✅ ✅ ❌ ❌
PRAGMA foreign_keys ✅ (always 1) ✅ ❌ ❌
PRAGMA journal_mode ✅ (WAL-only) ✅ ❌ ❌
PRAGMA synchronous ✅ (safe no-op assignment only) ✅ ❌ ❌
PRAGMA wal_checkpoint ✅ ✅ ❌ ❌
PRAGMA schema_version ✅ (read-only) ✅ ❌ ❌
PRAGMA user_version / application_id ✅ ✅ ❌ ❌
PRAGMA encoding ✅ (UTF-8 only) ✅ ❌ ❌
PRAGMA busy_timeout ✅ (queued writes) ✅ ❌ ❌
sqlite_schema / sqlite_master ✅ ✅ ❌ ❌
sqlite_temp_schema / temp.sqlite_schema ✅ ✅ ❌ ❌
Minimal information_schema views ✅ ❌ ✅ ✅
main. / temp. qualified local objects ✅ ✅ ✅ (different schema model) ✅
Broad SQLite PRAGMA surface ⚠️ (safe subset only) ✅ ❌ ❌

Examples

-- Query planning / execution diagnostics
EXPLAIN SELECT * FROM users WHERE id = 42;
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 1;

-- SQLite-compatible PRAGMA subset
PRAGMA page_size;
PRAGMA cache_size;
PRAGMA integrity_check;
PRAGMA quick_check;
PRAGMA database_list;
PRAGMA table_info(users);
PRAGMA table_xinfo(users);
PRAGMA table_list;
PRAGMA index_list(users);
PRAGMA index_info(users_name_idx);
PRAGMA foreign_key_list(orders);

-- Assignment form has constrained behavior in DecentDB:
-- no-op only when value matches current open configuration or a safe fixed mode.
PRAGMA page_size = 4096;
PRAGMA cache_size = 1024;
PRAGMA foreign_keys = ON;
PRAGMA journal_mode = WAL;
PRAGMA user_version = 7;

-- Compatibility catalog and helper surfaces
SELECT type, name, tbl_name FROM sqlite_schema;
SELECT table_schema, table_name FROM information_schema.tables;
SELECT value FROM generate_series(1, 5);
SELECT name FROM users ORDER BY name COLLATE NOCASE;

Data Types

Type DecentDB SQLite PostgreSQL DuckDB
NULL ✅ ✅ ✅ ✅
INTEGER/INT ✅ ✅ ✅ ✅
BIGINT ✅ ✅ ✅ ✅
FLOAT/REAL ✅ ✅ ✅ ✅
DOUBLE PRECISION ✅ ✅ ✅ ✅
TEXT ✅ ✅ ✅ ✅ (VARCHAR)
BLOB ✅ ✅ ✅ ✅
BOOLEAN ✅ ✅ ✅ ✅
UUID ✅ ❌ ✅ ✅
DECIMAL/NUMERIC ✅ ✅ ✅ ✅
DATE ✅ (native int64 µs UTC) ✅ ✅ ✅ (native)
TIMESTAMP ✅ (native int64 µs UTC) ✅ ✅ ✅ (native)
GEOMETRY ✅ ⚠️ (extension) ✅ (PostGIS) ✅ (spatial extension)
GEOGRAPHY ✅ (SRID 4326) ⚠️ (extension) ✅ (PostGIS) ⚠️ (via GEOMETRY)

Examples

-- Integer types
CREATE TABLE t1 (a INTEGER, b BIGINT, c INT);

-- Floating point
CREATE TABLE t2 (a FLOAT, b REAL, c DOUBLE PRECISION);

-- Text and binary
CREATE TABLE t3 (name TEXT, data BLOB);

-- Boolean
CREATE TABLE t4 (active BOOLEAN DEFAULT TRUE);

-- Decimal (exact numeric)
CREATE TABLE t5 (price DECIMAL(10,2), tax NUMERIC(5,4));

-- UUID
CREATE TABLE t6 (id UUID PRIMARY KEY DEFAULT GEN_RANDOM_UUID());

-- Spatial
CREATE TABLE places (id INTEGER PRIMARY KEY, geog GEOGRAPHY(POINT,4326));
CREATE TABLE parcels (id INTEGER PRIMARY KEY, boundary GEOMETRY(POLYGON,3857));

-- Date and Timestamp (accepted as ISO-format TEXT literals; stored internally as native int64 microseconds in DecentDB)
CREATE TABLE events (
  id INTEGER PRIMARY KEY,
  event_date DATE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO events (event_date, created_at) VALUES ('2024-03-15', '2024-03-15 14:30:00');

Constraints

Constraint DecentDB SQLite PostgreSQL DuckDB
PRIMARY KEY ✅ ✅ ✅ ✅
FOREIGN KEY ✅ ✅ ✅ ⚠️ (parsed, not enforced)
NOT NULL ✅ ✅ ✅ ✅
UNIQUE ✅ ✅ ✅ ✅
CHECK ✅ ✅ ✅ ✅
DEFAULT ✅ ✅ ✅ ✅

Examples

-- PRIMARY KEY (auto-assignment with a single INT64 PRIMARY KEY; INT/INTEGER/INT64 are aliases)
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);

-- FOREIGN KEY with actions
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER REFERENCES users(id) ON DELETE CASCADE
);

-- NOT NULL and UNIQUE
CREATE TABLE accounts (
  email TEXT NOT NULL UNIQUE,
  username TEXT NOT NULL
);

-- CHECK constraint
CREATE TABLE products (
  price DECIMAL(10,2) CHECK (price >= 0),
  qty INTEGER CHECK (qty >= 0)
);

-- DEFAULT values
CREATE TABLE posts (
  id INTEGER PRIMARY KEY,
  status TEXT DEFAULT 'draft',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Set Operations

Operation DecentDB SQLite PostgreSQL DuckDB
UNION ✅ ✅ ✅ ✅
UNION ALL ✅ ✅ ✅ ✅
INTERSECT ✅ ✅ ✅ ✅
INTERSECT ALL ✅ ✅ ✅ ✅
EXCEPT ✅ ✅ ✅ ✅
EXCEPT ALL ✅ ✅ ✅ ✅

Examples

-- UNION (deduplicated) vs UNION ALL (keeps duplicates)
SELECT name FROM employees UNION SELECT name FROM contractors;
SELECT name FROM employees UNION ALL SELECT name FROM contractors;

-- INTERSECT (rows in both sets)
SELECT user_id FROM orders INTERSECT SELECT user_id FROM returns;

-- INTERSECT ALL (preserves duplicate counts)
SELECT user_id FROM orders INTERSECT ALL SELECT user_id FROM returns;

-- EXCEPT (rows in first set but not second)
SELECT user_id FROM all_users EXCEPT SELECT user_id FROM banned_users;

-- EXCEPT ALL (multiset difference)
SELECT item_id FROM inventory EXCEPT ALL SELECT item_id FROM sold;

CTEs (Common Table Expressions)

Feature DecentDB SQLite PostgreSQL DuckDB
WITH ... AS ✅ ✅ ✅ ✅
WITH RECURSIVE ✅ ✅ ✅ ✅
Multiple CTEs ✅ ✅ ✅ ✅

Examples

-- Basic CTE
WITH active_users AS (
  SELECT * FROM users WHERE active = TRUE
)
SELECT name FROM active_users ORDER BY name;

-- Multiple CTEs
WITH
  dept_totals AS (
    SELECT department, SUM(salary) AS total FROM employees GROUP BY department
  ),
  high_spend AS (
    SELECT * FROM dept_totals WHERE total > 500000
  )
SELECT * FROM high_spend;

-- WITH RECURSIVE (generate a sequence 1..10)
WITH RECURSIVE cnt(x) AS (
  SELECT 1
  UNION ALL
  SELECT x + 1 FROM cnt WHERE x < 10
)
SELECT x FROM cnt;

-- WITH RECURSIVE (tree traversal — find all descendants of node 1)
WITH RECURSIVE descendants AS (
  SELECT id, name, parent_id FROM categories WHERE id = 1
  UNION ALL
  SELECT c.id, c.name, c.parent_id
  FROM categories c INNER JOIN descendants d ON c.parent_id = d.id
)
SELECT * FROM descendants;