Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions sync/streams/queries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
```

<Warning>
**Authorization:** This example filters only by `subscription.parameter('list_id')`. Any client can pass any `list_id`, so a user could access another user's todos. For production, add an authorization check so the user can only see lists they own or have access to — for example, add `AND list_id IN (SELECT id FROM lists WHERE owner_id = auth.user_id() OR id IN (SELECT list_id FROM list_shares WHERE shared_with = auth.user_id()))`. See [Combining Parameters with Subqueries](#combining-parameters-with-subqueries) below.

Check warning on line 55 in sync/streams/queries.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/streams/queries.mdx#L55

Did you really mean 'list_id'?

Check warning on line 55 in sync/streams/queries.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/streams/queries.mdx#L55

Did you really mean 'list_id'?

Check warning on line 55 in sync/streams/queries.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/streams/queries.mdx#L55

Did you really mean 'owner_id'?

Check warning on line 55 in sync/streams/queries.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/streams/queries.mdx#L55

Did you really mean 'list_shares'?

Check warning on line 55 in sync/streams/queries.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/streams/queries.mdx#L55

Did you really mean 'shared_with'?
</Warning>

```js
Expand Down Expand Up @@ -212,6 +212,33 @@
WHERE gm2.user_id = auth.user_id()
```

### Multi-column join conditions

You can join two tables on multiple columns in the same `WHERE` clause. This is useful when a relationship is defined by a composite key — for example, matching on both a region and an ID:

```yaml
streams:
region_items:
query: |
SELECT a.* FROM a, b
WHERE a.region = b.region
AND a.item_id = b.item_id
AND b.user_id = auth.user_id()
```

This also works when chaining joins across several tables:

```yaml
streams:
linked_records:
query: |
SELECT a.* FROM a
JOIN b ON a.col1 = b.col1
JOIN c ON b.col2 = c.col2
JOIN d ON c.col3 = d.col3
WHERE d.col4 = a.col4
```

### Join Limitations

When writing stream queries with JOINs, keep in mind: use only `JOIN` or `INNER JOIN`; select columns from a single table (e.g. `comments.*`); and use simple equality conditions (`table1.column = table2.column`). For the full list of supported JOIN syntax and invalid examples, see [Supported SQL — JOIN syntax](/sync/supported-sql#join-syntax).
Expand Down Expand Up @@ -269,7 +296,7 @@
- SELECT * FROM files WHERE project_id = subscription.parameter('project_id')
```

### Combining with CTEs

Check warning on line 299 in sync/streams/queries.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/streams/queries.mdx#L299

Did you really mean 'CTEs'?

Multiple queries work well with [Common Table Expressions (CTEs)](/sync/streams/ctes) to share the filtering logic and keep all results in one stream, requiring clients to manage one subscription instead of many:

Expand Down
3 changes: 3 additions & 0 deletions sync/supported-sql.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
- `WHERE` filtering on parameters (see [Filtering: WHERE Clause](#filtering-where-clause))
- A limited set of [operators](#operators) and [functions](#functions)

**Not supported**: subqueries, JOINs, CTEs, aggregation, sorting, or set operations (`GROUP BY`, `ORDER BY`, `LIMIT`, `UNION`, etc.).

Check warning on line 43 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L43

Did you really mean 'CTEs'?
</Tab>
</Tabs>

Expand Down Expand Up @@ -91,7 +91,7 @@
AND (owner_id = auth.user_id() OR shared_with = auth.user_id())
```

**`NOT`** — Supported for simple conditions on row values. `NOT IN` with a literal set of values is supported: use a JSON array string (e.g. `'["draft", "hidden"]'`), or the `ARRAY['draft', 'hidden']` and `ROW('draft', 'hidden')` forms. You cannot negate a subquery or a parameter array expansion.

Check warning on line 94 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L94

Did you really mean 'subquery'?

```sql
-- Simple row-value conditions
Expand Down Expand Up @@ -195,7 +195,7 @@
<Info>Supported in Sync Streams only. Not available in Sync Rules.</Info>
</Accordion>
<Accordion title="&& (Array overlap)">
- `<left> && <right>` — True if the JSON array in `left` and the set `right` share at least one value. Use when the row stores an array (e.g. a `tagged_users` column). `left` must be a row column (JSON array); `right` must be a subquery or parameter array.

Check warning on line 198 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L198

Did you really mean 'subquery'?

Example: `WHERE tagged_users && (SELECT id FROM org_members WHERE org_id = auth.parameter('org_id'))`

Expand All @@ -215,8 +215,8 @@
<Accordion title="String and binary">
- **[upper(text)](https://www.sqlite.org/lang_corefunc.html#upper)** — Convert text to upper case.
- **[lower(text)](https://www.sqlite.org/lang_corefunc.html#lower)** — Convert text to lower case.
- **[substring(text, start, length)](https://www.sqlite.org/lang_corefunc.html#substr)** — Extracts a portion of a string based on specified start index and length. Start index is 1-based. Example: `substring(created_at, 1, 10)` returns the date portion of the timestamp.

Check warning on line 218 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L218

Did you really mean 'substring'?
- **[instr(string, substring)](https://www.sqlite.org/lang_corefunc.html#instr)** — Finds the first occurrence of the substring within the string and returns the number of prior characters plus 1, or 0 if the substring is not found. Useful for locating a delimiter in compound strings. For example, `substring(value, 1, instr(value, '|') - 1)` extracts the portion before a `|` character.

Check warning on line 219 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L219

Did you really mean 'substring'?
- **[hex(data)](https://www.sqlite.org/lang_corefunc.html#hex)** — Convert blob or text data to hexadecimal text.
- **base64(data)** — Convert blob or text data to base64 text.
- **[length(data)](https://www.sqlite.org/lang_corefunc.html#length)** — For text, return the number of characters. For blob, return the number of bytes. For null, return null. For integer and real, convert to text and return the number of characters.
Expand All @@ -227,8 +227,8 @@
</Accordion>
<Accordion title="JSON">
- **[json_each(data)](https://www.sqlite.org/json1.html#jeach)** — Expands a JSON array into rows.
- **Sync Streams:** Works with auth and connection parameters (e.g. `JOIN json_each(auth.parameter('ids')) AS t` or `WHERE id IN (SELECT value FROM json_each(auth.parameter('ids')))`). Can also be used with columns from joined tables in some cases (e.g. `SELECT * FROM lists WHERE id IN (SELECT lists.value FROM access_control a, json_each(a.allowed_lists) as lists WHERE a.user = auth.user_id())`). See [Expanding JSON arrays](/sync/streams/parameters#expanding-json-arrays).

Check warning on line 230 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L230

Did you really mean 'access_control'?
- **Sync Rules:** Expands a JSON array or object from a request or token parameter into a set of parameter rows. Example: `SELECT value AS project_id FROM json_each(request.jwt() -> 'project_ids')`.

Check warning on line 231 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L231

Did you really mean 'project_id'?
- **[json_extract(data, path)](https://www.sqlite.org/json1.html#jex)** — Same as `->>` operator, but the path must start with `$.`
- **[json_array_length(data)](https://www.sqlite.org/json1.html#jarraylen)** — Given a JSON array (as text), returns the length of the array. If data is null, returns null. If the value is not a JSON array, returns 0.
- **[json_valid(data)](https://www.sqlite.org/json1.html#jvalid)** — Returns 1 if the data can be parsed as JSON, 0 otherwise.
Expand Down Expand Up @@ -270,6 +270,9 @@
-- Valid: columns from one table
SELECT comments.* FROM comments INNER JOIN issues ON comments.issue_id = issues.id

-- Valid: multiple equality conditions between tables (composite keys)
SELECT a.* FROM a, b WHERE a.region = b.region AND a.item_id = b.item_id AND b.user_id = auth.user_id()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can find a better example for composite keys though, that might be a good thing to include here.


-- Invalid: columns from multiple tables
SELECT comments.*, issues.title FROM comments JOIN issues ON comments.issue_id = issues.id

Expand All @@ -283,12 +286,12 @@

<Info>Supported in Sync Streams only. Not available in Sync Rules.</Info>

Common Table Expressions (CTEs) can be defined in a `with:` block **inside a stream** (stream-level, scoped to that stream) or at the **top level** of the sync config (global, shared across all streams). Each CTE is a name and a single `SELECT` query. The following rules apply:

Check warning on line 289 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L289

Did you really mean 'CTEs'?

- **Stream-level CTEs take precedence over global CTEs.** If a stream defines a CTE with the same name as a global CTE, the stream-level definition is used within that stream.

Check warning on line 291 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L291

Did you really mean 'CTEs'?

Check warning on line 291 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L291

Did you really mean 'CTEs'?
- **Global CTE names must not shadow source table names.** If a global CTE has the same name as a database table or collection, PowerSync reports a validation error. Stream-level CTE names are not subject to this restriction.
- **Global CTEs require `config: edition: 3`.**

Check warning on line 293 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L293

Did you really mean 'CTEs'?
- **CTEs cannot reference other CTEs.** Each CTE must be self-contained. To chain logic (e.g. orgs → projects), use nested subqueries in your stream query and reference only the CTE at the leaf level.

Check warning on line 294 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L294

Did you really mean 'CTEs'?

Check warning on line 294 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L294

Did you really mean 'CTEs'?

Check warning on line 294 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L294

Did you really mean 'orgs'?
- **Stream-level CTE names take precedence over table names** within that stream. Use distinct names to avoid confusion.
- **Short-hand `IN cte_name`** works only when the CTE has exactly one column.

Expand Down Expand Up @@ -323,7 +326,7 @@
# project_ids: SELECT id FROM projects WHERE org_id IN user_orgs # Error
```

For how to use CTEs, see [Common Table Expressions (CTEs)](/sync/streams/ctes).

Check warning on line 329 in sync/supported-sql.mdx

View check run for this annotation

Mintlify / Mintlify Validation (powersync) - vale-spellcheck

sync/supported-sql.mdx#L329

Did you really mean 'CTEs'?

## CASE Expressions

Expand Down