COMPAT.md
Turso is a re-implementation of SQLite in Rust. This document describes the current state of compatibility between the two. Any deviation from SQLite behavior that is not explicitly documented as an opt-in extension is considered a bug.
Compatibility is validated through differential testing against SQLite and ongoing work to pass the full SQLite TCL test suite.
| Statement | Status | Comment |
|---|---|---|
| ALTER TABLE | ✅ Yes | |
| ANALYZE | ✅ Yes | |
| ATTACH DATABASE | ✅ Yes | |
| BEGIN TRANSACTION | ✅ Yes | |
| COMMIT TRANSACTION | ✅ Yes | |
| CHECK | ✅ Yes | |
| CREATE INDEX | ✅ Yes | |
| CREATE TABLE | ✅ Yes | |
| CREATE TABLE ... STRICT | ✅ Yes | |
| CREATE TRIGGER | ✅ Yes | |
| CREATE VIEW | ✅ Yes | |
| CREATE VIRTUAL TABLE | ✅ Yes | |
| DELETE | ✅ Yes | |
| DETACH DATABASE | ✅ Yes | |
| DROP INDEX | ✅ Yes | |
| DROP TABLE | ✅ Yes | |
| DROP TRIGGER | ✅ Yes | |
| DROP VIEW | ✅ Yes | |
| END TRANSACTION | ✅ Yes | |
| EXPLAIN | ✅ Yes | |
| INDEXED BY | ✅ Yes | |
| INSERT | ✅ Yes | |
| INSERT ... ON CONFLICT (UPSERT) | ✅ Yes | |
| ON CONFLICT clause | ✅ Yes | |
| REINDEX | ✅ Yes | |
| RELEASE SAVEPOINT | ✅ Yes | |
| REPLACE | ✅ Yes | |
| RETURNING clause | ✅ Yes | |
| ROLLBACK TRANSACTION | ✅ Yes | |
| SAVEPOINT | ✅ Yes | |
| SELECT | ✅ Yes | |
| SELECT ... WHERE | ✅ Yes | |
| SELECT ... WHERE ... LIKE | ✅ Yes | |
| SELECT ... LIMIT | ✅ Yes | |
| SELECT ... ORDER BY | ✅ Yes | |
| SELECT ... GROUP BY | ✅ Yes | |
| SELECT ... HAVING | ✅ Yes | |
| SELECT ... JOIN | ✅ Yes | |
| SELECT ... CROSS JOIN | ✅ Yes | |
| SELECT ... INNER JOIN | ✅ Yes | |
| SELECT ... OUTER JOIN | ✅ Yes | |
| SELECT ... JOIN USING | ✅ Yes | |
| SELECT ... NATURAL JOIN | ✅ Yes | |
| UPDATE | ✅ Yes | |
| VACUUM | 🚧 Partial | VACUUM INTO supported; plain in-place VACUUM is experimental |
| WITH clause | 🚧 Partial | WITH RECURSIVE not yet supported. |
| WINDOW functions | 🚧 Partial | Only row_number() and aggregate … OVER (…) work. Missing: rank, dense_rank, percent_rank, cume_dist, ntile, lag, lead, first_value, last_value, nth_value. Custom frame specs (ROWS/RANGE/GROUPS BETWEEN, EXCLUDE) not yet supported. agg(…) FILTER (…) OVER (…) panics. |
| GENERATED | 🚧 Partial | virtual columns only (no ALTER, partial affinity support). Requires --experimental-generated-columns. |
| WITHOUT ROWID | 🚧 Partial | Requires --experimental-without-rowid. Effectively insert-only: CREATE / INSERT / SELECT work (incl. composite PK), but UPDATE, DELETE, UPSERT, INSERT OR REPLACE, secondary UNIQUE constraints, secondary CREATE INDEX, FOREIGN KEY, CDC, and materialized views are all rejected. AUTOINCREMENT and missing PK rejection are parity with SQLite. |
| CREATE TRIGGER ... INSTEAD OF | ❌ No | Triggers on views are not supported. Currently errors with misleading "no such table" message. |
| CREATE VIEW IF NOT EXISTS | 🚧 Partial | Not idempotent — second create on an existing view errors instead of no-op. |
SQLite allows more than one active write statement on the same connection. For
example, an application can step one INSERT ... RETURNING, leave it open, and
then start another write statement on the same connection.
Turso currently returns SQLITE_BUSY for the second write statement. Reads may
still run while a write statement is active.
This is a deliberate compatibility gap. SQLite's built-in write opcodes do not
return control to the application halfway through the mutation. Turso can suspend
there for async I/O. If a second writer were allowed to start, dropping or
resetting the first half-finished writer could not always clean up only that
writer without risking the second writer's state. Returning SQLITE_BUSY keeps
the connection state simple: finish or reset the active writer first, then start
the next write statement.
SAVEPOINT, RELEASE, and ROLLBACK TO also return SQLITE_BUSY while a
write statement on the connection is active. SQLite rejects SAVEPOINT and
RELEASE the same way ("SQL statements in progress"); for ROLLBACK TO it
instead aborts the in-progress statements, which Turso does not support, so
Turso rejects that too rather than let a suspended writer resume over pages the
rollback just restored.
These same-connection SQLITE_BUSY rejections are errors ("... - SQL
statements in progress") that abort the rejected statement: it must be reset
or re-executed, not merely stepped again, and the busy handler is never
invoked for them. No amount of waiting can release the conflict, because only
the application finishing or resetting its own statement can. This matches
SQLite, which reports its statements-in-progress rejections as error-class
SQLITE_BUSY and reserves the busy handler for lock contention.
If a write statement inside BEGIN is reset or dropped before it finishes and
Turso did not open a statement savepoint for it, the transaction becomes
rollback-only. A later COMMIT rolls back the whole transaction and returns an
error. ROLLBACK also clears that state. This prevents a half-finished statement
from being committed after control returned to the application at an async I/O
point. Two caveats until then: statements running later in the same transaction
can observe the abandoned statement's partial changes (they are undone only when
the transaction ends), and ROLLBACK TO a savepoint does not clear the
rollback-only marker even if it restored every page the abandoned statement
touched — only ROLLBACK recovers the connection.
In experimental MVCC mode there is an additional known gap: all statements on a connection share one MVCC transaction, so a write statement that finishes while a sibling statement is still active defers its commit until the last sibling finishes. SQLite instead commits at the writer's own completion and lets the remaining statements continue on a read-only transaction. Until Turso does the same, a write that reported success is not durable while sibling statements remain active, and it is silently rolled back if the transaction then ends abnormally — for example if the last sibling reader is reset or dropped mid-scan, or a later write statement on the same connection fails after changing rows. Finish or reset sibling statements promptly after writing to avoid this window.
| Statement | Status | Comment |
|---|---|---|
| PRAGMA analysis_limit | ❌ No | |
| PRAGMA application_id | ✅ Yes | |
| PRAGMA auto_vacuum | 🚧 Partial | Read works; write requires --experimental-autovacuum |
| PRAGMA automatic_index | ❌ No | |
| PRAGMA busy_timeout | ✅ Yes | |
| PRAGMA cache_size | ✅ Yes | |
| PRAGMA cache_spill | 🚧 Partial | Enabled/Disabled only |
| PRAGMA case_sensitive_like | Not Needed | deprecated in SQLite |
| PRAGMA cell_size_check | ❌ No | |
| PRAGMA checkpoint_fullfsync | ❌ No | |
| PRAGMA collation_list | ❌ No | |
| PRAGMA compile_options | ❌ No | |
| PRAGMA count_changes | Not Needed | deprecated in SQLite |
| PRAGMA data_store_directory | Not Needed | deprecated in SQLite |
| PRAGMA data_version | ❌ No | |
| PRAGMA database_list | ✅ Yes | |
| PRAGMA default_cache_size | Not Needed | deprecated in SQLite |
| PRAGMA defer_foreign_keys | ❌ No | |
| PRAGMA empty_result_callbacks | Not Needed | deprecated in SQLite |
| PRAGMA encoding | ✅ Yes | |
| PRAGMA foreign_key_check | ❌ No | |
| PRAGMA foreign_key_list | ✅ Yes | |
| PRAGMA foreign_keys | ✅ Yes | |
| PRAGMA freelist_count | ✅ Yes | |
| PRAGMA full_column_names | Not Needed | deprecated in SQLite |
| PRAGMA fullfsync | ✅ Yes | |
| PRAGMA function_list | ✅ Yes | |
| PRAGMA hard_heap_limit | ❌ No | |
| PRAGMA ignore_check_constraints | ✅ Yes | |
| PRAGMA incremental_vacuum | ❌ No | |
| PRAGMA index_info | ✅ Yes | |
| PRAGMA index_list | ✅ Yes | |
| PRAGMA index_xinfo | ✅ Yes | |
| PRAGMA integrity_check | ✅ Yes | |
| PRAGMA journal_mode | ✅ Yes | |
| PRAGMA journal_size_limit | ❌ No | |
| PRAGMA legacy_alter_table | ❌ No | |
| PRAGMA legacy_file_format | ✅ Yes | |
| PRAGMA locking_mode | 🚧 Partial | EXCLUSIVE only |
| PRAGMA max_page_count | ✅ Yes | |
| PRAGMA mmap_size | ❌ No | |
| PRAGMA module_list | 🚧 Partial | Works, but only completion and generate_series are registered modules |
| PRAGMA optimize | ❌ No | |
| PRAGMA page_count | ✅ Yes | |
| PRAGMA page_size | ✅ Yes | |
| PRAGMA parser_trace | ❌ No | |
| PRAGMA pragma_list | ✅ Yes | |
| PRAGMA query_only | ✅ Yes | |
| PRAGMA quick_check | ✅ Yes | |
| PRAGMA read_uncommitted | ❌ No | |
| PRAGMA recursive_triggers | ❌ No | |
| PRAGMA reverse_unordered_selects | ❌ No | |
| PRAGMA schema_version | ✅ Yes | For writes, emulate defensive mode (always noop) |
| PRAGMA secure_delete | ❌ No | |
| PRAGMA short_column_names | Not Needed | deprecated in SQLite |
| PRAGMA shrink_memory | ❌ No | |
| PRAGMA soft_heap_limit | ❌ No | |
| PRAGMA stats | ❌ No | Used for testing in SQLite |
| PRAGMA synchronous | 🚧 Partial | OFF and FULL supported |
| PRAGMA table_info | ✅ Yes | |
| PRAGMA table_list | ✅ Yes | |
| PRAGMA table_xinfo | ✅ Yes | |
| PRAGMA temp_store | ✅ Yes | |
| PRAGMA temp_store_directory | Not Needed | deprecated in SQLite |
| PRAGMA threads | ❌ No | |
| PRAGMA trusted_schema | ❌ No | |
| PRAGMA user_version | ✅ Yes | |
| PRAGMA vdbe_addoptrace | ❌ No | |
| PRAGMA vdbe_debug | ❌ No | |
| PRAGMA vdbe_listing | ❌ No | |
| PRAGMA vdbe_trace | ✅ Yes | |
| PRAGMA wal_autocheckpoint | ❌ No | |
| PRAGMA wal_checkpoint | 🚧 Partial | Not Needed calling with param (pragma-value) |
| PRAGMA writable_schema | ❌ No |
PRAGMAs that exist only in Turso and have no SQLite equivalent. Visible via PRAGMA pragma_list.
| PRAGMA | Comment |
|---|---|
| PRAGMA capture_data_changes_conn | Configure per-connection Change Data Capture. Returns (mode, table, version); default off. |
| PRAGMA unstable_capture_data_changes_conn | Unstable alias of capture_data_changes_conn; same shape and behavior, name may change. |
| PRAGMA cipher | Encryption-at-rest cipher selection (paired with hexkey). Read-only without a session key. |
| PRAGMA hexkey | Encryption-at-rest key for the current session. Returns "encryption key is not set for this session" when unset. |
| PRAGMA data_sync_retry | Retry policy for disk sync failures (boolean). |
| PRAGMA list_types | Introspect Turso's type system. Returns (type, parent, encode, decode, default, operators). |
| PRAGMA mvcc_checkpoint_threshold | MVCC checkpoint tuning. |
| PRAGMA require_where | Safety: when enabled, refuses UPDATE/DELETE without a WHERE clause. |
| PRAGMA i_am_a_dummy | Alias of require_where (homage to MySQL). |
Feature support of sqlite expr syntax.
| Syntax | Status | Comment |
|---|---|---|
| literals | ✅ Yes | |
| schema.table.column | 🚧 Partial | Schemas aren't supported |
| unary operator | ✅ Yes | |
| binary operator | 🚧 Partial | Only %, !<, and !> are unsupported |
| agg() FILTER (WHERE ...) | ❌ No | |
| ... OVER (...) | 🚧 Partial | Supported for aggregate functions and ROW_NUMBER() |
| (expr) | ✅ Yes | |
| CAST (expr AS type) | ✅ Yes | |
| COLLATE | 🚧 Partial | Custom collations not supported. Bug: unknown collation names are silently treated as the default instead of erroring (SQLite errors with "no such collation sequence"). |
| (NOT) LIKE | ✅ Yes | |
| (NOT) GLOB | ✅ Yes | |
| (NOT) REGEXP | ✅ Yes | |
| (NOT) MATCH | ❌ No | |
| IS (NOT) | ✅ Yes | |
| IS (NOT) DISTINCT FROM | ✅ Yes | |
| (NOT) BETWEEN ... AND ... | ✅ Yes | Expression is rewritten in the optimizer |
| (NOT) IN (SELECT...) | ✅ Yes | |
| (NOT) EXISTS (SELECT...) | ✅ Yes | |
| x <operator> (SELECT...)) | 🚧 Partial | Only scalar subqueries supported, i.e. not (x,y) = (SELECT...) |
| CASE WHEN THEN ELSE END | ✅ Yes | |
| RAISE | ✅ Yes | RAISE('msg') and RAISE(ABORT, 'msg') also work outside triggers. |
| Function | Status | Comment |
|---|---|---|
| abs(X) | ✅ Yes | |
| changes() | 🚧 Partial | Still need to support update statements and triggers |
| char(X1,X2,...,XN) | ✅ Yes | |
| coalesce(X,Y,...) | ✅ Yes | |
| concat(X,...) | ✅ Yes | |
| concat_ws(SEP,X,...) | ✅ Yes | |
| format(FORMAT,...) | ✅ Yes | |
| glob(X,Y) | ✅ Yes | |
| hex(X) | ✅ Yes | |
| ifnull(X,Y) | ✅ Yes | |
| if(X,Y,Z) | ✅ Yes | Alias of iif |
| iif(X,Y,Z) | ✅ Yes | |
| instr(X,Y) | ✅ Yes | |
| last_insert_rowid() | ✅ Yes | |
| length(X) | ✅ Yes | |
| like(X,Y) | ✅ Yes | |
| like(X,Y,Z) | ✅ Yes | |
| likelihood(X,Y) | ✅ Yes | |
| likely(X) | ✅ Yes | |
| load_extension(X) | 🚧 Partial | Only Turso-native extensions, not SQLite .so/.dll |
| load_extension(X,Y) | ❌ No | |
| lower(X) | ✅ Yes | |
| ltrim(X) | ✅ Yes | |
| ltrim(X,Y) | ✅ Yes | |
| max(X,Y,...) | ✅ Yes | |
| min(X,Y,...) | ✅ Yes | |
| nullif(X,Y) | ✅ Yes | |
| octet_length(X) | ✅ Yes | |
| printf(FORMAT,...) | ✅ Yes | |
| quote(X) | ✅ Yes | |
| random() | ✅ Yes | |
| randomblob(N) | ✅ Yes | |
| replace(X,Y,Z) | ✅ Yes | |
| round(X) | ✅ Yes | |
| round(X,Y) | ✅ Yes | |
| rtrim(X) | ✅ Yes | |
| rtrim(X,Y) | ✅ Yes | |
| sign(X) | ✅ Yes | |
| soundex(X) | ✅ Yes | |
| sqlite_compileoption_get(N) | ❌ No | |
| sqlite_compileoption_used(X) | ❌ No | |
| sqlite_offset(X) | ❌ No | |
| sqlite_source_id() | ✅ Yes | |
| sqlite_version() | ✅ Yes | |
| substr(X,Y,Z) | ✅ Yes | |
| substr(X,Y) | ✅ Yes | |
| substring(X,Y,Z) | ✅ Yes | |
| substring(X,Y) | ✅ Yes | |
| total_changes() | 🚧 Partial | Still need to support update statements and triggers |
| trim(X) | ✅ Yes | |
| trim(X,Y) | ✅ Yes | |
| typeof(X) | ✅ Yes | |
| unhex(X) | ✅ Yes | |
| unhex(X,Y) | ✅ Yes | |
| unicode(X) | ✅ Yes | |
| unlikely(X) | ✅ Yes | |
| upper(X) | ✅ Yes | |
| unistr(X) | ✅ Yes | |
| unistr_quote(X) | ✅ Yes | |
| zeroblob(N) | ✅ Yes |
| Function | Status | Comment |
|---|---|---|
| acos(X) | ✅ Yes | |
| acosh(X) | ✅ Yes | |
| asin(X) | ✅ Yes | |
| asinh(X) | ✅ Yes | |
| atan(X) | ✅ Yes | |
| atan2(Y,X) | ✅ Yes | |
| atanh(X) | ✅ Yes | |
| ceil(X) | ✅ Yes | |
| ceiling(X) | ✅ Yes | |
| cos(X) | ✅ Yes | |
| cosh(X) | ✅ Yes | |
| degrees(X) | ✅ Yes | |
| exp(X) | ✅ Yes | |
| floor(X) | ✅ Yes | |
| ln(X) | ✅ Yes | |
| log(B,X) | ✅ Yes | |
| log(X) | ✅ Yes | |
| log10(X) | ✅ Yes | |
| log2(X) | ✅ Yes | |
| mod(X,Y) | ✅ Yes | |
| pi() | ✅ Yes | |
| pow(X,Y) | ✅ Yes | |
| power(X,Y) | ✅ Yes | |
| radians(X) | ✅ Yes | |
| sin(X) | ✅ Yes | |
| sinh(X) | ✅ Yes | |
| sqrt(X) | ✅ Yes | |
| tan(X) | ✅ Yes | |
| tanh(X) | ✅ Yes | |
| trunc(X) | ✅ Yes |
| Function | Status | Comment |
|---|---|---|
| avg(X) | ✅ Yes | |
| count(X) | ✅ Yes | |
| count(*) | ✅ Yes | |
| group_concat(X) | ✅ Yes | |
| group_concat(X,Y) | ✅ Yes | |
| string_agg(X,Y) | ✅ Yes | |
| max(X) | ✅ Yes | |
| min(X) | ✅ Yes | |
| sum(X) | ✅ Yes | |
| total(X) | ✅ Yes | |
| median(X) | ✅ Yes | Requires percentile extension |
| percentile(Y,P) | ✅ Yes | Requires percentile extension |
| percentile_cont(Y,P) | ✅ Yes | Requires percentile extension |
| percentile_disc(Y,P) | ✅ Yes | Requires percentile extension |
| stddev(X) | ✅ Yes | Turso extension |
| Function | Status | Comment |
|---|---|---|
| date() | ✅ Yes | |
| time() | ✅ Yes | |
| datetime() | ✅ Yes | |
| julianday() | ✅ Yes | |
| unixepoch() | ✅ Yes | |
| strftime() | ✅ Yes | |
| timediff() | ✅ Yes |
Modifiers:
| Modifier | Status | Comment |
|---|---|---|
| Days | ✅ Yes | |
| Hours | ✅ Yes | |
| Minutes | ✅ Yes | |
| Seconds | ✅ Yes | |
| Months | ✅ Yes | |
| Years | ✅ Yes | |
| TimeOffset | ✅ Yes | |
| DateOffset | ✅ Yes | |
| DateTimeOffset | ✅ Yes | |
| Ceiling | ✅ Yes | |
| Floor | ✅ Yes | |
| StartOfMonth | ✅ Yes | |
| StartOfYear | ✅ Yes | |
| StartOfDay | ✅ Yes | |
| Weekday(N) | ✅ Yes | |
| Auto | ✅ Yes | |
| UnixEpoch | ✅ Yes | |
| JulianDay | ✅ Yes | |
| Localtime | ✅ Yes | |
| Utc | ✅ Yes | |
| Subsec | ✅ Yes |
| Function | Status | Comment |
|---|---|---|
| json(json) | ✅ Yes | |
| jsonb(json) | ✅ Yes | |
| json_array(value1,value2,...) | ✅ Yes | |
| jsonb_array(value1,value2,...) | ✅ Yes | |
| json_array_length(json) | ✅ Yes | |
| json_array_length(json,path) | ✅ Yes | |
| json_error_position(json) | ✅ Yes | |
| json_extract(json,path,...) | ✅ Yes | |
| jsonb_extract(json,path,...) | ✅ Yes | |
| json -> path | ✅ Yes | |
| json ->> path | ✅ Yes | |
| json_insert(json,path,value,...) | ✅ Yes | |
| jsonb_insert(json,path,value,...) | ✅ Yes | |
| json_object(label1,value1,...) | ✅ Yes | |
| jsonb_object(label1,value1,...) | ✅ Yes | |
| json_patch(json1,json2) | ✅ Yes | |
| jsonb_patch(json1,json2) | ✅ Yes | |
| json_pretty(json) | ✅ Yes | |
| json_remove(json,path,...) | ✅ Yes | |
| jsonb_remove(json,path,...) | ✅ Yes | |
| json_replace(json,path,value,...) | ✅ Yes | |
| jsonb_replace(json,path,value,...) | ✅ Yes | |
| json_set(json,path,value,...) | ✅ Yes | |
| jsonb_set(json,path,value,...) | ✅ Yes | |
| json_type(json) | ✅ Yes | |
| json_type(json,path) | ✅ Yes | |
| json_valid(json) | ✅ Yes | |
| json_valid(json,flags) | ✅ Yes | |
| json_quote(value) | ✅ Yes | |
| json_group_array(value) | ✅ Yes | |
| jsonb_group_array(value) | ✅ Yes | |
| json_group_object(label,value) | ✅ Yes | |
| jsonb_group_object(name,value) | ✅ Yes | |
| json_each(json) | ✅ Yes | |
| json_each(json,path) | ✅ Yes | |
| json_tree(json) | 🚧 Partial | see commented-out tests in json.test |
| json_tree(json,path) | 🚧 Partial | see commented-out tests in json.test |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_open | ✅ Yes | |
| sqlite3_open_v2 | 🚧 Partial | URI filenames parsed; VFS parameter ignored |
| sqlite3_open16 | ❌ No | |
| sqlite3_close | ✅ Yes | |
| sqlite3_close_v2 | ✅ Yes | Same as sqlite3_close |
| sqlite3_db_filename | ✅ Yes | |
| sqlite3_db_config | ❌ No | Stub |
| sqlite3_db_handle | ✅ Yes | |
| sqlite3_db_readonly | ❌ No | |
| sqlite3_db_status | ❌ No | |
| sqlite3_db_cacheflush | ❌ No | |
| sqlite3_db_release_memory | ❌ No | |
| sqlite3_db_name | ❌ No | |
| sqlite3_db_mutex | ❌ No | |
| sqlite3_get_autocommit | ✅ Yes | |
| sqlite3_limit | ❌ No | Stub |
| sqlite3_initialize | ✅ Yes | |
| sqlite3_shutdown | ✅ Yes | |
| sqlite3_config | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_prepare | ❌ No | |
| sqlite3_prepare_v2 | ✅ Yes | |
| sqlite3_prepare_v3 | ✅ Yes | Delegates to prepare_v2, prepFlags ignored |
| sqlite3_prepare16 | ❌ No | |
| sqlite3_prepare16_v2 | ❌ No | |
| sqlite3_finalize | ✅ Yes | |
| sqlite3_step | ✅ Yes | |
| sqlite3_reset | ✅ Yes | |
| sqlite3_exec | ✅ Yes | |
| sqlite3_stmt_readonly | ✅ Yes | |
| sqlite3_stmt_busy | ❌ No | Stub |
| sqlite3_stmt_status | 🚧 Partial | Supports FULLSCAN_STEP, SORT, VM_STEP, REPREPARE, LIBSQL_STMTSTATUS_ROWS_READ, and LIBSQL_STMTSTATUS_ROWS_WRITTEN. Returns 0 for AUTOINDEX, RUN, FILTER_MISS, FILTER_HIT, and MEMUSED. |
| sqlite3_sql | ❌ No | |
| sqlite3_expanded_sql | ❌ No | Stub |
| sqlite3_normalized_sql | ❌ No | |
| sqlite3_next_stmt | ✅ Yes |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_bind_parameter_count | ✅ Yes | |
| sqlite3_bind_parameter_name | ✅ Yes | |
| sqlite3_bind_parameter_index | ✅ Yes | |
| sqlite3_bind_null | ✅ Yes | |
| sqlite3_bind_int | ✅ Yes | |
| sqlite3_bind_int64 | ✅ Yes | |
| sqlite3_bind_double | ✅ Yes | |
| sqlite3_bind_text | ✅ Yes | |
| sqlite3_bind_text16 | ❌ No | |
| sqlite3_bind_text64 | ❌ No | |
| sqlite3_bind_blob | ✅ Yes | |
| sqlite3_bind_blob64 | ❌ No | |
| sqlite3_bind_value | ❌ No | |
| sqlite3_bind_pointer | ❌ No | |
| sqlite3_bind_zeroblob | ❌ No | |
| sqlite3_bind_zeroblob64 | ❌ No | |
| sqlite3_clear_bindings | ✅ Yes |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_column_count | ✅ Yes | |
| sqlite3_column_name | ✅ Yes | |
| sqlite3_column_name16 | ❌ No | |
| sqlite3_column_decltype | ✅ Yes | |
| sqlite3_column_decltype16 | ❌ No | |
| sqlite3_column_type | ✅ Yes | |
| sqlite3_column_int | ✅ Yes | |
| sqlite3_column_int64 | ✅ Yes | |
| sqlite3_column_double | ✅ Yes | |
| sqlite3_column_text | ✅ Yes | |
| sqlite3_column_text16 | ❌ No | |
| sqlite3_column_blob | ✅ Yes | |
| sqlite3_column_bytes | ✅ Yes | |
| sqlite3_column_bytes16 | ❌ No | |
| sqlite3_column_value | ✅ Yes | |
| sqlite3_column_table_name | ✅ Yes | |
| sqlite3_column_database_name | ❌ No | |
| sqlite3_column_origin_name | ❌ No | |
| sqlite3_data_count | ✅ Yes |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_value_type | ✅ Yes | |
| sqlite3_value_int | ✅ Yes | |
| sqlite3_value_int64 | ✅ Yes | |
| sqlite3_value_double | ✅ Yes | |
| sqlite3_value_text | ✅ Yes | |
| sqlite3_value_text16 | ❌ No | |
| sqlite3_value_blob | ✅ Yes | |
| sqlite3_value_bytes | ✅ Yes | |
| sqlite3_value_bytes16 | ❌ No | |
| sqlite3_value_dup | ✅ Yes | |
| sqlite3_value_free | ✅ Yes | |
| sqlite3_value_nochange | ❌ No | |
| sqlite3_value_frombind | ❌ No | |
| sqlite3_value_subtype | ❌ No | |
| sqlite3_value_pointer | ❌ No | |
| sqlite3_value_encoding | ❌ No | |
| sqlite3_value_numeric_type | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_errcode | ✅ Yes | |
| sqlite3_errmsg | ✅ Yes | |
| sqlite3_errmsg16 | ❌ No | |
| sqlite3_errstr | ✅ Yes | |
| sqlite3_extended_errcode | ✅ Yes | |
| sqlite3_extended_result_codes | ❌ No | |
| sqlite3_error_offset | ❌ No | |
| sqlite3_system_errno | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_changes | ✅ Yes | |
| sqlite3_changes64 | ✅ Yes | |
| sqlite3_total_changes | ✅ Yes | |
| sqlite3_total_changes64 | ❌ No | |
| sqlite3_last_insert_rowid | ✅ Yes | |
| sqlite3_set_last_insert_rowid | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_malloc | ✅ Yes | |
| sqlite3_malloc64 | ✅ Yes | |
| sqlite3_free | ✅ Yes | |
| sqlite3_realloc | ❌ No | |
| sqlite3_realloc64 | ❌ No | |
| sqlite3_msize | ❌ No | |
| sqlite3_memory_used | ❌ No | |
| sqlite3_memory_highwater | ❌ No | |
| sqlite3_soft_heap_limit64 | ❌ No | |
| sqlite3_hard_heap_limit64 | ❌ No | |
| sqlite3_release_memory | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_busy_handler | ✅ Yes | |
| sqlite3_busy_timeout | ✅ Yes | |
| sqlite3_trace_v2 | ❌ No | Stub |
| sqlite3_progress_handler | ✅ Yes | Step-time callbacks only |
| sqlite3_set_authorizer | ❌ No | Stub |
| sqlite3_commit_hook | ❌ No | |
| sqlite3_rollback_hook | ❌ No | |
| sqlite3_update_hook | ❌ No | |
| sqlite3_preupdate_hook | ❌ No | |
| sqlite3_unlock_notify | ❌ No | |
| sqlite3_wal_hook | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_create_function | ❌ No | |
| sqlite3_create_function_v2 | ❌ No | Stub |
| sqlite3_create_function16 | ❌ No | |
| sqlite3_create_window_function | ❌ No | Stub |
| sqlite3_aggregate_context | ❌ No | Stub |
| sqlite3_user_data | ❌ No | Stub |
| sqlite3_context_db_handle | ✅ Yes | |
| sqlite3_get_auxdata | ❌ No | |
| sqlite3_set_auxdata | ❌ No | |
| sqlite3_result_null | ❌ No | Stub |
| sqlite3_result_int | ❌ No | |
| sqlite3_result_int64 | ❌ No | Stub |
| sqlite3_result_double | ❌ No | Stub |
| sqlite3_result_text | ❌ No | Stub |
| sqlite3_result_text16 | ❌ No | |
| sqlite3_result_text64 | ❌ No | |
| sqlite3_result_blob | ❌ No | Stub |
| sqlite3_result_blob64 | ❌ No | |
| sqlite3_result_value | ❌ No | |
| sqlite3_result_pointer | ❌ No | |
| sqlite3_result_zeroblob | ❌ No | |
| sqlite3_result_zeroblob64 | ❌ No | |
| sqlite3_result_error | ❌ No | Stub |
| sqlite3_result_error16 | ❌ No | |
| sqlite3_result_error_code | ❌ No | |
| sqlite3_result_error_nomem | ❌ No | Stub |
| sqlite3_result_error_toobig | ❌ No | Stub |
| sqlite3_result_subtype | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_create_collation | ❌ No | |
| sqlite3_create_collation_v2 | ❌ No | Stub |
| sqlite3_create_collation16 | ❌ No | |
| sqlite3_collation_needed | ❌ No | |
| sqlite3_collation_needed16 | ❌ No | |
| sqlite3_stricmp | ❌ No | Stub |
| sqlite3_strnicmp | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_backup_init | ❌ No | Stub |
| sqlite3_backup_step | ❌ No | Stub |
| sqlite3_backup_finish | ❌ No | Stub |
| sqlite3_backup_remaining | ❌ No | Stub |
| sqlite3_backup_pagecount | ❌ No | Stub |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_blob_open | ❌ No | Stub |
| sqlite3_blob_close | ❌ No | Stub |
| sqlite3_blob_bytes | ❌ No | Stub |
| sqlite3_blob_read | ❌ No | Stub |
| sqlite3_blob_write | ❌ No | Stub |
| sqlite3_blob_reopen | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_wal_checkpoint | ✅ Yes | |
| sqlite3_wal_checkpoint_v2 | ✅ Yes | |
| sqlite3_wal_autocheckpoint | ❌ No | |
| sqlite3_wal_hook | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_libversion | ✅ Yes | Returns "3.42.0" |
| sqlite3_libversion_number | ✅ Yes | Returns 3042000 |
| sqlite3_sourceid | ❌ No | |
| sqlite3_threadsafe | ✅ Yes | Returns 1 |
| sqlite3_complete | ❌ No | Stub |
| sqlite3_interrupt | ✅ Yes | |
| sqlite3_sleep | ❌ No | Stub |
| sqlite3_randomness | ❌ No | |
| sqlite3_get_table | ✅ Yes | |
| sqlite3_free_table | ✅ Yes | |
| sqlite3_mprintf | ❌ No | |
| sqlite3_vmprintf | ❌ No | |
| sqlite3_snprintf | ❌ No | |
| sqlite3_vsnprintf | ❌ No | |
| sqlite3_strglob | ❌ No | |
| sqlite3_strlike | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_table_column_metadata | ✅ Yes |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_create_module | ❌ No | |
| sqlite3_create_module_v2 | ❌ No | |
| sqlite3_drop_modules | ❌ No | |
| sqlite3_declare_vtab | ❌ No | |
| sqlite3_overload_function | ❌ No | |
| sqlite3_vtab_config | ❌ No | |
| sqlite3_vtab_on_conflict | ❌ No | |
| sqlite3_vtab_nochange | ❌ No | |
| sqlite3_vtab_collation | ❌ No | |
| sqlite3_vtab_distinct | ❌ No | |
| sqlite3_vtab_in | ❌ No | |
| sqlite3_vtab_in_first | ❌ No | |
| sqlite3_vtab_in_next | ❌ No | |
| sqlite3_vtab_rhs_value | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_load_extension | ❌ No | |
| sqlite3_enable_load_extension | ❌ No | |
| sqlite3_auto_extension | ❌ No | |
| sqlite3_cancel_auto_extension | ❌ No | |
| sqlite3_reset_auto_extension | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_serialize | ❌ No | Stub |
| sqlite3_deserialize | ❌ No | Stub |
| Interface | Status | Comment |
|---|---|---|
| sqlite3_keyword_count | ❌ No | |
| sqlite3_keyword_name | ❌ No | |
| sqlite3_keyword_check | ❌ No | |
| sqlite3_txn_state | ❌ No | |
| sqlite3_file_control | ❌ No | |
| sqlite3_status | ❌ No | |
| sqlite3_status64 | ❌ No | |
| sqlite3_test_control | ❌ No | Testing only |
| sqlite3_log | ❌ No |
| Interface | Status | Comment |
|---|---|---|
| libsql_wal_frame_count | ✅ Yes | Get WAL frame count |
| libsql_wal_get_frame | ✅ Yes | Extract frame from WAL |
| libsql_wal_insert_frame | ✅ Yes | Insert frame into WAL |
| libsql_wal_disable_checkpoint | ✅ Yes | Disable checkpointing |
| Opcode | Status | Comment |
|---|---|---|
| Add | ✅ Yes | |
| AddImm | ✅ Yes | |
| Affinity | ✅ Yes | |
| AggFinal | ✅ Yes | |
| AggStep | ✅ Yes | |
| AggValue | ✅ Yes | |
| And | ✅ Yes | |
| AutoCommit | ✅ Yes | |
| BitAnd | ✅ Yes | |
| BitNot | ✅ Yes | |
| BitOr | ✅ Yes | |
| Blob | ✅ Yes | |
| BeginSubrtn | ✅ Yes | |
| Cast | ✅ Yes | |
| Checkpoint | ✅ Yes | |
| Clear | ❌ No | |
| Close | ✅ Yes | |
| CollSeq | ✅ Yes | |
| Column | ✅ Yes | |
| Compare | ✅ Yes | |
| Concat | ✅ Yes | |
| Copy | ✅ Yes | |
| Count | ✅ Yes | |
| CreateBTree | 🚧 Partial | no temp databases |
| DecrJumpZero | ✅ Yes | |
| Delete | ✅ Yes | |
| Destroy | ✅ Yes | |
| Divide | ✅ Yes | |
| DropIndex | ✅ Yes | |
| DropTable | ✅ Yes | |
| DropTrigger | ✅ Yes | |
| EndCoroutine | ✅ Yes | |
| Eq | ✅ Yes | |
| Expire | ❌ No | |
| Explain | ❌ No | |
| FkCheck | ✅ Yes | |
| FkCounter | ✅ Yes | |
| FkIfZero | ✅ Yes | |
| Found | ✅ Yes | |
| Filter | ✅ Yes | |
| FilterAdd | ✅ Yes | |
| Function | ✅ Yes | |
| Ge | ✅ Yes | |
| Gosub | ✅ Yes | |
| Goto | ✅ Yes | |
| Gt | ✅ Yes | |
| Halt | ✅ Yes | |
| HaltIfNull | ✅ Yes | |
| IdxDelete | ✅ Yes | |
| IdxGE | ✅ Yes | |
| IdxInsert | ✅ Yes | |
| IdxLE | ✅ Yes | |
| IdxLT | ✅ Yes | |
| IdxRowid | ✅ Yes | |
| If | ✅ Yes | |
| IfNeg | ✅ Yes | |
| IfNot | ✅ Yes | |
| IfPos | ✅ Yes | |
| IfZero | ❌ No | |
| IncrVacuum | ❌ No | |
| Init | ✅ Yes | |
| InitCoroutine | ✅ Yes | |
| Insert | ✅ Yes | |
| Int64 | ✅ Yes | |
| Integer | ✅ Yes | |
| IntegrityCk | ✅ Yes | |
| IsNull | ✅ Yes | |
| IsUnique | ❌ No | |
| JournalMode | ✅ Yes | |
| Jump | ✅ Yes | |
| Last | ✅ Yes | |
| Le | ✅ Yes | |
| LoadAnalysis | ❌ No | |
| Lt | ✅ Yes | |
| MakeRecord | ✅ Yes | |
| MaxPgcnt | ✅ Yes | |
| MemMax | ✅ Yes | |
| Move | ✅ Yes | |
| Multiply | ✅ Yes | |
| MustBeInt | ✅ Yes | |
| Ne | ✅ Yes | |
| NewRowid | ✅ Yes | |
| Next | ✅ Yes | |
| Noop | ✅ Yes | |
| Not | ✅ Yes | |
| NotExists | ✅ Yes | |
| NotFound | ✅ Yes | |
| NotNull | ✅ Yes | |
| Null | ✅ Yes | |
| NullRow | ✅ Yes | |
| Once | ✅ Yes | |
| OpenAutoindex | ✅ Yes | |
| OpenDup | ✅ Yes | |
| OpenEphemeral | ✅ Yes | |
| OpenPseudo | ✅ Yes | |
| OpenRead | ✅ Yes | |
| OpenWrite | ✅ Yes | |
| Or | ✅ Yes | |
| Pagecount | 🚧 Partial | no temp databases |
| Param | ❌ No | |
| ParseSchema | ✅ Yes | |
| Permutation | ❌ No | |
| Prev | ✅ Yes | |
| Program | ✅ Yes | |
| ReadCookie | 🚧 Partial | no temp databases, only user_version supported |
| Real | ✅ Yes | |
| RealAffinity | ✅ Yes | |
| Remainder | ✅ Yes | |
| ResetCount | ❌ No | |
| ResetSorter | 🚧 Partial | sorter cursors are not supported yet; only ephemeral tables are |
| ResultRow | ✅ Yes | |
| Return | ✅ Yes | |
| Rewind | ✅ Yes | |
| RowData | ✅ Yes | |
| RowId | ✅ Yes | |
| RowKey | ❌ No | |
| RowSetAdd | ✅ Yes | |
| RowSetRead | ✅ Yes | |
| RowSetTest | ✅ Yes | |
| Rowid | ✅ Yes | |
| SCopy | ❌ No | |
| Savepoint | ✅ Yes | |
| Seek | ❌ No | |
| SeekGe | ✅ Yes | |
| SeekGt | ✅ Yes | |
| SeekLe | ✅ Yes | |
| SeekLt | ✅ Yes | |
| SeekRowid | ✅ Yes | |
| SeekEnd | ✅ Yes | |
| Sequence | ✅ Yes | |
| SequenceTest | ✅ Yes | |
| SetCookie | ✅ Yes | |
| ShiftLeft | ✅ Yes | |
| ShiftRight | ✅ Yes | |
| SoftNull | ✅ Yes | |
| Sort | ❌ No | |
| SorterCompare | ✅ Yes | |
| SorterData | ✅ Yes | |
| SorterInsert | ✅ Yes | |
| SorterNext | ✅ Yes | |
| SorterOpen | ✅ Yes | |
| SorterSort | ✅ Yes | |
| String | NotNeeded | SQLite uses String for sized strings and String8 for null-terminated. All our strings are sized |
| String8 | ✅ Yes | |
| Subtract | ✅ Yes | |
| TableLock | ❌ No | |
| Trace | ❌ No | |
| Transaction | ✅ Yes | |
| VBegin | ✅ Yes | |
| VColumn | ✅ Yes | |
| VCreate | ✅ Yes | |
| VDestroy | ✅ Yes | |
| VFilter | ✅ Yes | |
| VNext | ✅ Yes | |
| VOpen | ✅ Yes | |
| VRename | ✅ Yes | |
| VUpdate | ✅ Yes | |
| Vacuum | 🚧 Partial | |
| Variable | ✅ Yes | |
| Yield | ✅ Yes | |
| ZeroOrNull | ✅ Yes |
We currently don't have plan to support the rollback journal mode as it locks the database file during writes.
Therefore, all rollback-type modes (delete, truncate, persist, memory) are marked are Not Needed below.
| Journal mode | Status | Comment |
|---|---|---|
| wal | ✅ Yes | |
| wal2 | ❌ No | experimental feature in sqlite |
| delete | Not Needed | |
| truncate | Not Needed | |
| persist | Not Needed | |
| memory | Not Needed |
Turso has in-tree extensions.
UUID's in Turso are blobs by default.
| Function | Status | Comment |
|---|---|---|
| uuid4() | ✅ Yes | UUID version 4 |
| uuid4_str() | ✅ Yes | UUID v4 string alias gen_random_uuid() for PG compatibility |
| uuid7(X?) | ✅ Yes | UUID version 7 (optional parameter for seconds since epoch) |
| uuid7_timestamp_ms(X) | ✅ Yes | Convert a UUID v7 to milliseconds since epoch |
| uuid_str(X) | ✅ Yes | Convert a valid UUID to string |
| uuid_blob(X) | ✅ Yes | Convert a valid UUID to blob |
The regexp extension is compatible with sqlean-regexp.
| Function | Status | Comment |
|---|---|---|
| regexp(pattern, source) | ✅ Yes | |
| regexp_like(source, pattern) | ✅ Yes | |
| regexp_substr(source, pattern) | ✅ Yes | |
| regexp_capture(source, pattern[, n]) | ✅ Yes | |
| regexp_replace(source, pattern, replacement) | ✅ Yes |
The vector extension is compatible with libSQL native vector search.
| Function | Status | Comment |
|---|---|---|
| vector(x) | ✅ Yes | |
| vector32(x) | ✅ Yes | |
| vector64(x) | ✅ Yes | |
| vector_extract(x) | ✅ Yes | |
| vector_distance_cos(x, y) | ✅ Yes | |
| vector_distance_l2(x, y) | ✅ Yes | Euclidean distance |
| vector_concat(x, y) | ✅ Yes | |
| vector_slice(x, start_index, end_index) | ✅ Yes |
The time extension is compatible with sqlean-time.
| Function | Status | Comment |
|---|---|---|
| time_now() | ✅ Yes | |
| time_date(year, month, day[, hour, min, sec[, nsec[, offset_sec]]]) | ✅ Yes | |
| time_get_year(t) | ✅ Yes | |
| time_get_month(t) | ✅ Yes | |
| time_get_day(t) | ✅ Yes | |
| time_get_hour(t) | ✅ Yes | |
| time_get_minute(t) | ✅ Yes | |
| time_get_second(t) | ✅ Yes | |
| time_get_nano(t) | ✅ Yes | |
| time_get_weekday(t) | ✅ Yes | |
| time_get_yearday(t) | ✅ Yes | |
| time_get_isoyear(t) | ✅ Yes | |
| time_get_isoweek(t) | ✅ Yes | |
| time_get(t, field) | ✅ Yes | |
| time_unix(sec[, nsec]) | ✅ Yes | |
| time_milli(msec) | ✅ Yes | |
| time_micro(usec) | ✅ Yes | |
| time_nano(nsec) | ✅ Yes | |
| time_to_unix(t) | ✅ Yes | |
| time_to_milli(t) | ✅ Yes | |
| time_to_micro(t) | ✅ Yes | |
| time_to_nano(t) | ✅ Yes | |
| time_after(t, u) | ✅ Yes | |
| time_before(t, u) | ✅ Yes | |
| time_compare(t, u) | ✅ Yes | |
| time_equal(t, u) | ✅ Yes | |
| time_add(t, d) | ✅ Yes | |
| time_add_date(t, years[, months[, days]]) | ✅ Yes | |
| time_sub(t, u) | ✅ Yes | |
| time_since(t) | ✅ Yes | |
| time_until(t) | ✅ Yes | |
| time_trunc(t, field) | ✅ Yes | |
| time_trunc(t, d) | ✅ Yes | |
| time_round(t, d) | ✅ Yes | |
| time_fmt_iso(t[, offset_sec]) | ✅ Yes | |
| time_fmt_datetime(t[, offset_sec]) | ✅ Yes | |
| time_fmt_date(t[, offset_sec]) | ✅ Yes | |
| time_fmt_time(t[, offset_sec]) | ✅ Yes | |
| time_parse(s) | ✅ Yes | |
| dur_ns() | ✅ Yes | |
| dur_us() | ✅ Yes | |
| dur_ms() | ✅ Yes | |
| dur_s() | ✅ Yes | |
| dur_m() | ✅ Yes | |
| dur_h() | ✅ Yes |
Turso implements FTS using Tantivy instead of SQLite's FTS3/FTS4/FTS5.
| Feature | Status | Comment |
|---|---|---|
| CREATE INDEX ... USING fts | ✅ Yes | Turso-specific syntax |
| fts_match() | ✅ Yes | |
| fts_score() | ✅ Yes | BM25 relevance scoring |
| fts_highlight() | ✅ Yes | |
| MATCH operator | ✅ Yes | |
| SQLite FTS3/FTS4/FTS5 | ❌ No | Use Turso FTS instead |
| snippet() | ❌ No |
The CSV extension provides RFC 4180 compliant CSV file reading.
| Feature | Status | Comment |
|---|---|---|
| CSV virtual table | ✅ Yes | CREATE VIRTUAL TABLE ... USING csv(...) |
Statistical aggregate functions.
| Function | Status | Comment |
|---|---|---|
| median(X) | ✅ Yes | |
| percentile(Y,P) | ✅ Yes | |
| percentile_cont(Y,P) | ✅ Yes | |
| percentile_disc(Y,P) | ✅ Yes |
| Function | Status | Comment |
|---|---|---|
| generate_series(start, stop[, step]) | ✅ Yes | All parameters supported |
| carray() | ❌ No | C-API specific |
| Virtual Table | Status | Comment |
|---|---|---|
| sqlite_dbpage | 🚧 Partial | readonly, no attach support |