Use raw tables for native SQLite functionality and improved performance.
Raw tables are an experimental feature. We’re actively seeking feedback on:
Join our Discord community to share your experience and get help.
By default, PowerSync uses a JSON-based view system where data is stored schemalessly in JSON format and then presented through SQLite views based on the client-side schema. Raw tables allow you to define native SQLite tables in the client-side schema, bypassing this.
This eliminates overhead associated with extracting values from the JSON data and provides access to advanced SQLite features like foreign key constraints and custom indexes.
Availability
Raw tables were introduced in the following versions of our client SDKs:
0.8.0
, React-Native: 1.23.0
, Web: 1.24.0
)package:powersync
.Also note that raw tables are only supported by the new Rust-based sync client, which is currently opt-in.
Consider raw tables when you need:
FOREIGN KEY
and ON DELETE CASCADE
constraintsGENERATED
columns, etcSELECT SUM(value) FROM transactions
) - raw tables more efficiently get these values directly from the SQLite column, instead of extracting the value from the JSON object on every rowps_data__<table>.data
columnCurrently the sync system involves two general steps:
The bucket operations use JSON to store the individual operation data. The local database uses tables with a simple schemaless ps_data__<table_name>
structure containing only an id
(TEXT) and data
(JSON) column.
PowerSync automatically creates views on that table that extract JSON fields to resemble standard tables reflecting your schema.
When opting in to raw tables, you are responsible for creating the tables before using them - PowerSync will no longer create them automatically.
Because PowerSync takes no control over raw tables, you need to manually:
For the purpose of this example, consider a simple table like this:
To sync into the raw todo_lists
table instead of ps_data__
, PowerSync needs the SQL statements extracting
columns from the untyped JSON protocol used during syncing.
This involves specifying two SQL statements:
put
SQL statement for upserts, responsible for creating a todo_list
row or updating it based on its id
and data columns.delete
SQL statement responsible for deletions.The PowerSync client as part of our SDKs will automatically run these statements in response to sync lines being sent from the PowerSync Service.
To reference the ID or extract values, prepared statements with parameters are used. delete
statements can reference the id of the affected row, while put
statements can also reference individual column values.
Declaring these statements and parameters happens as part of the schema passed to PowerSync databases:
Raw tables are not included in the regular Schema()
object. Instead, add them afterwards using withRawTables
.
For each raw table, specify the put
and delete
statement. The values of parameters are described as a JSON
array either containing:
Id
to reference the id of the affected row.{ Column: name }
to reference the value of the column name
.We will simplify this API after understanding the use-cases for raw tables better.
Raw tables are not included in the regular Schema()
object. Instead, add them afterwards using withRawTables
.
For each raw table, specify the put
and delete
statement. The values of parameters are described as a JSON
array either containing:
Id
to reference the id of the affected row.{ Column: name }
to reference the value of the column name
.We will simplify this API after understanding the use-cases for raw tables better.
Raw tables are not part of the regular tables list and can be defined with the optional rawTables
parameter.
To define a raw table, include it in the list of tables passed to the Schema
:
To define a raw table, include it in the list of tables passed to the Schema
:
Unfortunately, raw tables are not available in the .NET SDK yet.
After adding raw tables to the schema, you’re also responsible for creating them by executing the
corresponding CREATE TABLE
statement before connect()
-ing the database.
PowerSync uses an internal SQLite table to collect local writes. For PowerSync-managed views, a trigger for insertions, updates and deletions automatically forwards local mutations into this table. When using raw tables, defining those triggers is your responsibility.
The PowerSync SQLite extension creates an insert-only virtual table named powersync_crud
with these columns:
The virtual table associates local mutations with the current transaction and ensures writes made during the sync process (applying server-side changes) don’t count as local writes. This means that triggers can be defined on raw tables like so:
In PowerSync’s JSON-based view system the client-side schema is applied to the schemaless data, meaning no migrations are required. Raw tables however are excluded from this, so it is the developers responsibility to manage migrations for these tables.
When you’re adding new tables to your sync rules, clients will start to sync data on those tables - even if the tables aren’t mentioned
in the client’s schema yet.
So at the time you’re introducing a new raw table to your app, it’s possible that PowerSync has already synced some data for that
table, which would be stored in ps_untyped
. When adding regular tables, PowerSync will automatically extract rows from ps_untyped
.
With raw tables, that step is your responsibility. To copy data, run these statements in a transaction after creating the table:
This does not apply if you’ve been using the raw table from the beginning (and never called connect()
without them) - you only
need this for raw tables you already had locally.
Another workaround is to clear PowerSync data when changing raw tables and opt for a full resync.
To migrate from PowerSync-managed tables to raw tables, first:
ps_untyped
.INSERT FROM SELECT
statement to insert ps_untyped
data into your raw tables.When adding new columns to raw tables, there currently isn’t a way to re-sync that table to add those columns from the server - we are investigating possible workarounds and encourage users to try out if they need this.
To ensure the column values are accurate, you’d have to delete all data after a migration and wait for the next complete sync.
APIs that clear an entire PowerSync database, like e.g. disconnectAndClear()
, don’t affect raw tables.
This should be kept in mind when you’re using those methods - data from raw tables needs to be deleted explicitly.