Skip to content
3 changes: 3 additions & 0 deletions documentation/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ This page tracks significant updates to the QuestDB documentation.
capped set. Likewise, move a window function into the query you run against
the view.

- [SHOW CREATE DATABASE](/docs/query/sql/show/#show-create-database) - Documented QuestDB Enterprise 4.2.0 ACL output, principal/schema/grant ordering, the requirement for both `LIST USERS` and `USER DETAILS`, active storage policies, and replay limits. Documented the removal of `OWNED BY` from all `SHOW CREATE` output in 4.2.0, and updated the schema-copy recipe, RBAC, and governance guidance
- [Views](/docs/concepts/views/#administrative-operations-in-view-metadata) - Documented QuestDB Enterprise 4.2.0 restrictions on administrative operations in `PIVOT IN` subqueries, schema-only database dumps in materialized and live views, and [upgrade guidance](/docs/operations/upgrade/#upgrading-to-420) for existing scripts and views

## October 2026

### Releases
Expand Down
21 changes: 12 additions & 9 deletions documentation/concepts/live-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,12 @@ replaying it:
`START FROM BEGINNING` to include all history still present in the base table.
- If the view was created with a trailing `;`, the saved query keeps it inside
`AS ( ... )` and replaying fails with `')' expected`. Remove that `;`.
- On QuestDB Enterprise, the saved `OWNED BY` names the user running
`SHOW CREATE`, and dropping the view removes its grants. Set `OWNED BY` to
the user or group that should receive the creator's permissions, and grant
the other permissions on the new view again.
- On QuestDB Enterprise, the saved DDL contains no `OWNED BY` or grants, and
dropping the view removes its grants. Save them first with
[`SHOW CREATE DATABASE INCLUDE (PERMISSIONS)`](/docs/query/sql/show/#enterprise-access-control)
or inspect them with `active_grants()`. On recreation, add `OWNED BY` if
another principal should receive the creator's permissions, and restore the
other grants on the new view.

Dropping the invalid view removes its materialized rows, including rows whose
source history is no longer retained by the base table.
Expand Down Expand Up @@ -540,11 +542,12 @@ in [Base table lifecycle](#base-table-lifecycle).
objects, such as `tables()`, follow the
[materialized view rules](/docs/query/sql/create-mat-view/#catalogue-functions):
in QuestDB Enterprise, only a creator with `SYSTEM ADMIN` can use them.
- **No administrative functions.** Enterprise functions that check the
caller's permissions, such as `permissions()` and `backup_instance_name()`,
are rejected, since a live view refreshes in the background. See
[query constraints](/docs/query/sql/create-live-view/#query-constraints) for
the full list.
- **No administrative functions or ACL dumps.** Enterprise functions that
check the caller's permissions, such as `permissions()` and
`backup_instance_name()`, and `SHOW CREATE DATABASE` with any ACL category
are rejected, since a live view refreshes in the background. Use
`INCLUDE (SCHEMA)` for schema metadata under the catalogue rules above. See
[query constraints](/docs/query/sql/create-live-view/#query-constraints).
- **No TTL on the view.** Live-view disk growth is unbounded in this version.
Base-table retention does not shrink the view, because `DROP PARTITION`,
`TRUNCATE`, and TTL eviction on the base table leave the view's rows in place.
Expand Down
4 changes: 4 additions & 0 deletions documentation/concepts/materialized-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,10 @@ Materialized view queries:
refresh: `active_grants()`, `active_permissions()`, `backup_instance_name()`,
`backup_status()`, `backups()`, `permissions()`, `reload_tick_calendars()`,
and `reload_tls()`
- Must not use `SHOW CREATE DATABASE` with any ACL category when Enterprise
access control is enabled. The default includes ACL output; use
`INCLUDE (SCHEMA)` under the
[catalogue rules](/docs/query/sql/create-mat-view/#catalogue-functions)
- Must use join conditions compatible with incremental refresh
- When the base table uses [deduplication](/docs/concepts/deduplication/), non-aggregate
columns must be a subset of the `DEDUP` keys
Expand Down
44 changes: 41 additions & 3 deletions documentation/concepts/views.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,10 @@ they can read through `views()` and `SHOW CREATE VIEW`, still names the table.
The view's definer permissions are preserved even if the creator's account is
later disabled or deleted.

Access control functions such as `active_grants()` and `active_permissions()`
are an exception: in a view, they check the permissions of the user who queries
the view, not those of its definer. See
Access control functions such as `active_grants()` and `active_permissions()`,
and [`SHOW CREATE DATABASE`](/docs/query/sql/show/#use-in-views) with any ACL
category, are exceptions: in a view's query body, they check the permissions of
the user who queries the view, not those of its definer. See
[access control functions](/docs/query/functions/access-control/#limitations).

### Metadata functions in views (Enterprise) {#metadata-functions-in-views}
Expand Down Expand Up @@ -464,6 +465,43 @@ table-name functions name, so it resolves them with the reader's own
permissions until you run [`ALTER VIEW`](/docs/query/sql/alter-view/) or
`CREATE OR REPLACE VIEW` on it.

### Administrative operations in view metadata

A regular view can use administrative functions and administrative `SHOW`
statements in its query body, including ordinary subqueries. These operations
check the permissions of the user who queries the view, not those of its definer.

A [`PIVOT IN` subquery](/docs/query/sql/pivot/#dynamic-in-list-from-subquery) is a
special case: its result values determine the output column names. QuestDB must
execute it at compile time to derive the view's columns, not just when a user
queries the view. This also happens during background recompilation and startup,
when there is no calling session. The subquery therefore cannot use administrative
operations that need a calling session or have side effects.

This restriction extends to nested subqueries and other views read by the
`PIVOT IN` subquery. It does not apply to ordinary subqueries elsewhere in the
view's query body.

With Enterprise access control enabled, `SHOW CREATE DATABASE` with any ACL
category counts as an administrative statement. Use
`SHOW CREATE DATABASE INCLUDE (SCHEMA)` if the subquery only needs schema
metadata. Catalogue queries that do not require a calling session remain
allowed.

:::warning Existing views

Starting with QuestDB Enterprise 4.2.0, creating, altering, or replacing a view
with a prohibited operation fails. Previously accepted views become invalid at
their next compilation after upgrading, including at startup. Inspect
`view_status` and `invalidation_reason` in
[`views()`](/docs/query/functions/meta/#views). Replace ACL dumps with
`INCLUDE (SCHEMA)` where appropriate, or remove the administrative operation
from the subquery, then update the definition with
[`ALTER VIEW`](/docs/query/sql/alter-view/) and run
[`COMPILE VIEW`](/docs/query/sql/compile-view/).

:::

### No column-level permissions on views

Unlike tables, views do **not** support column-level permissions. You can only
Expand Down
58 changes: 35 additions & 23 deletions documentation/cookbook/operations/copy-schema-between-instances.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ description:
---

Recreate the structure of one QuestDB instance on another: tables, views,
materialized views, and, on QuestDB Enterprise, the access control layer.
materialized views, and live views, with the option to copy access control on
QuestDB Enterprise.

## Problem

You want to copy the schema from one QuestDB instance to another.

## Solution

[`SHOW CREATE DATABASE`](/docs/query/sql/show/#show-create-database) returns a
data-free dump of the whole database, with one round-trippable DDL statement per
row:
[`SHOW CREATE DATABASE INCLUDE (SCHEMA)`](/docs/query/sql/show/#show-create-database)
returns a data-free schema dump, with one replayable DDL statement per row. The
explicit category keeps this recipe schema-only on both editions:

```questdb-sql title="Dump the database schema" demo
SHOW CREATE DATABASE;
SHOW CREATE DATABASE INCLUDE (SCHEMA);
```

Statements come back in dependency order, so replaying them top to bottom on an
empty instance recreates the database.
Statements come back in dependency order. Replay them top to bottom on an empty
instance to recreate the schema.

On QuestDB Enterprise, the dump contains only the objects the caller
[can see](/docs/security/rbac/#object-visibility). To copy the whole schema,
Expand All @@ -38,7 +39,7 @@ result set as JSON, with the statements under `.dataset[][0]`:

```shell
curl -s -G "https://demo.questdb.io/api/v1/sql/execute" \
--data-urlencode "query=SHOW CREATE DATABASE"
--data-urlencode "query=SHOW CREATE DATABASE INCLUDE (SCHEMA)"
```

From there you can either save the statements to a file or apply them straight
Expand All @@ -48,7 +49,7 @@ to the target instance.

```shell
curl -s -G "https://demo.questdb.io/api/v1/sql/execute" \
--data-urlencode "query=SHOW CREATE DATABASE" \
--data-urlencode "query=SHOW CREATE DATABASE INCLUDE (SCHEMA)" \
| jq -r '.dataset[][0] | sub(";?$";";") + "\n"' > schema.sql
```

Expand All @@ -63,7 +64,7 @@ Send each statement to the target's `/execute` endpoint as you read it:

```shell
curl -s -G "https://demo.questdb.io/api/v1/sql/execute" \
--data-urlencode "query=SHOW CREATE DATABASE" \
--data-urlencode "query=SHOW CREATE DATABASE INCLUDE (SCHEMA)" \
| jq --raw-output0 '.dataset[][0]' \
| xargs -0 -n1 sh -c \
'curl -sG "http://localhost:9000/api/v1/sql/execute" \
Expand Down Expand Up @@ -104,14 +105,15 @@ export QDB_TOKEN="your-rest-api-token"

curl -s -G "https://source:9000/api/v1/sql/execute" \
-H "Authorization: Bearer $QDB_TOKEN" \
--data-urlencode "query=SHOW CREATE DATABASE"
--data-urlencode "query=SHOW CREATE DATABASE INCLUDE (SCHEMA)"
```

## Enterprise: schema without permissions
## Enterprise: schema and access control {#enterprise-schema-without-permissions}

In [QuestDB Enterprise](/enterprise/), the default dump also carries the access
control layer: users, groups, service accounts, memberships, and grants. That is
usually not what you want when seeding a development instance from production.
In [QuestDB Enterprise](/enterprise/) 4.2.0 and later, the default dump also
carries the access control layer: users, groups, service accounts, memberships,
and grants. That is usually not what you want when seeding a development
instance from production.

Select what to copy with `INCLUDE`:

Expand All @@ -121,19 +123,29 @@ curl -s -G "https://source:9000/api/v1/sql/execute" \
-H "Authorization: Bearer $QDB_TOKEN" \
--data-urlencode "query=SHOW CREATE DATABASE INCLUDE (SCHEMA)"

# Permissions only, no tables or views
# Identities, memberships, and grants; no schema
curl -s -G "https://source:9000/api/v1/sql/execute" \
-H "Authorization: Bearer $QDB_TOKEN" \
--data-urlencode "query=SHOW CREATE DATABASE INCLUDE (ACL)"
```

The ACL categories each require the matching `LIST` or `USER DETAILS`
permission, while the schema categories need none, so a user with only `SELECT`
can still dump the structure of the objects they can see.

Passwords and tokens are never dumped, so `CREATE USER` and
`CREATE SERVICE ACCOUNT` statements replay without credentials. Set those on the
target after the replay.
Every selection that includes an ACL category, including plain
`SHOW CREATE DATABASE`, requires both `LIST USERS` and `USER DETAILS`. Missing
either permission aborts the dump. `INCLUDE (SCHEMA)` requires neither, so a
user with only `SELECT` can still dump the structure of the objects they can see.
Active table storage policies are included in the schema.

To copy access control too, use plain `SHOW CREATE DATABASE` in the commands
above. It creates principals first, schema objects next, then memberships,
service account assignments, and grants. Replay on an empty target as its
built-in admin to avoid adding another principal's creator grants. Schema-only
and per-object dumps contain neither grants nor `OWNED BY`.

Before opening the target to users, set credentials and reapply memory limits,
resource groups, and resource group mappings: none are dumped. Only the external
alias shown by `SHOW GROUPS` is restored for each group. Avoid concurrent schema
and ACL changes during the export; the dump is best-effort, not a consistent
snapshot. See [Replay limits](/docs/query/sql/show/#replay-limits).

## Manual alternative: copy from the Web Console

Expand Down
23 changes: 23 additions & 0 deletions documentation/operations/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,29 @@ check how these changes affect your users and clients:
versions started with them, then rejected every OIDC login. See
[Startup validation](/docs/security/oidc/#startup-validation).

### Upgrading to 4.2.0

QuestDB Enterprise 4.2.0 changes `SHOW CREATE` output and restricts
administrative operations in views. Check schema-export scripts and stored
views for the following before upgrading:

- With access control enabled, plain `SHOW CREATE DATABASE` now requires both
`LIST USERS` and `USER DETAILS`. Schema-only scripts should use
`INCLUDE (SCHEMA)` instead of acquiring more permissions. See
[database dumps](/docs/query/sql/show/#enterprise-access-control).
- Per-object `SHOW CREATE` and database dumps no longer append `OWNED BY`.
Schema-only output does not restore grants. Use the ACL dump for permissions,
and review
[creator permissions on replay](/docs/query/sql/show/#enterprise-variant).
- With access control enabled, materialized and live views reject
`SHOW CREATE DATABASE` with any ACL category, including the default. Change
schema-metadata queries to `INCLUDE (SCHEMA)` under the
[catalogue rules](/docs/query/sql/create-mat-view/#catalogue-functions).
- A regular view's `PIVOT IN` subquery rejects administrative functions and
administrative `SHOW` statements. Existing definitions become invalid at
their next compilation, including startup. See
[view migration guidance](/docs/concepts/views/#administrative-operations-in-view-metadata).

### QuestDB Enterprise BYOC

For QuestDB Enterprise BYOC customers, QuestDB manages the upgrade process. The
Expand Down
11 changes: 8 additions & 3 deletions documentation/query/functions/access-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ These apply to both `active_grants()` and `active_permissions()`:
the permissions a user gains by assuming a service account. Use
[`SHOW GROUPS userName`](/docs/query/sql/show/#show-groups) and
[`SHOW SERVICE ACCOUNTS userName`](/docs/query/sql/show/#show-service-accounts)
to see them.
to see them. To export memberships, assignments, and direct grants as
replayable SQL, use
[`SHOW CREATE DATABASE INCLUDE (PERMISSIONS)`](/docs/query/sql/show/#enterprise-access-control).
- Access through a view is not shown on the tables the view reads. A view
reads them with the permissions of its
[definer](/docs/concepts/views/#definer-security-model-enterprise), so a user
Expand All @@ -105,8 +107,11 @@ These apply to both `active_grants()` and `active_permissions()`:
refresh in the background without a session whose permissions could be
checked. Creating such a view fails with an error such as
`administrative function cannot be used in materialized view: active_grants`.
A regular view can call them. Unlike the tables it reads, the functions check
the permissions of the user who queries the view, not those of its definer.
A regular view can call them in its query body, but not in a `PIVOT IN`
subquery, including through nested queries or another view. Unlike the tables
it reads, the functions check the permissions of the user who queries the
view, not those of its definer. See
[administrative operations in view metadata](/docs/concepts/views/#administrative-operations-in-view-metadata).

### Examples

Expand Down
12 changes: 9 additions & 3 deletions documentation/query/sql/create-live-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,18 @@ The view query is validated at creation time and must:
`systimestamp()`, or `rnd_*()`.
- Not use
[catalogue functions](/docs/query/sql/create-mat-view/#catalogue-functions)
or `SHOW` statements, such as `tables()` in a `WHERE` sub-query, unless the
creator holds `SYSTEM ADMIN` (Enterprise). The same rules as for materialized
views apply.
or catalogue `SHOW` statements, such as schema-only
[`SHOW CREATE DATABASE`](/docs/query/sql/show/#use-in-views) in a `WHERE`
subquery, unless the creator holds `SYSTEM ADMIN` (Enterprise). The same rules
as for materialized views apply.
- Not use Enterprise administrative functions, which check the permissions of
the calling session: `active_grants()`, `active_permissions()`,
`backup_instance_name()`, `backup_status()`, `backups()`, `permissions()`,
`reload_tick_calendars()`, and `reload_tls()`.
- Not use `SHOW CREATE DATABASE` with any ACL category when access control is
enabled (QuestDB Enterprise 4.2.0 and later), even as an administrator. The
default statement includes ACL output; use `INCLUDE (SCHEMA)` under the
catalogue rules above.
- Not read another live view.

## Examples
Expand Down Expand Up @@ -476,6 +481,7 @@ the query.
| `live view select cannot filter on the designated timestamp yet` | The `WHERE` clause filters the designated timestamp |
| `non-deterministic function cannot be used in live view` | The query uses `now()`, `rnd_*()`, or a similar non-deterministic function |
| `administrative function cannot be used in live view: <function>` | The query calls an Enterprise administrative function, such as `permissions()` or `reload_tls()` |
| `administrative statement cannot be used in live view: SHOW CREATE DATABASE` | The query includes an ACL dump (Enterprise); use `INCLUDE (SCHEMA)` under the [catalogue rules](/docs/query/sql/create-mat-view/#catalogue-functions) |

### Window errors

Expand Down
15 changes: 13 additions & 2 deletions documentation/query/sql/create-mat-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,8 @@ Materialized view queries must:
- Not read [catalogue functions](#catalogue-functions) through a regular view,
and in QuestDB Enterprise call them only with `SYSTEM ADMIN`
- Not use Enterprise administrative functions, such as `permissions()` or
`backups()`
`backups()`, or `SHOW CREATE DATABASE` with any ACL category. Use
`INCLUDE (SCHEMA)` for schema metadata, subject to the catalogue rules below

See [query constraints](/docs/concepts/materialized-views/#query-constraints)
for the full list.
Expand All @@ -475,7 +476,16 @@ metadata:
`files()`, and `glob()`
- `SHOW TABLES`, `SHOW COLUMNS`, `SHOW PARTITIONS`, `SHOW CREATE TABLE`,
`SHOW CREATE VIEW`, `SHOW CREATE MATERIALIZED VIEW`, `SHOW CREATE LIVE VIEW`,
and `SHOW CREATE DATABASE`
and schema-only
[`SHOW CREATE DATABASE`](/docs/query/sql/show/#use-in-views), such as
`SHOW CREATE DATABASE INCLUDE (SCHEMA)`

In QuestDB Enterprise 4.2.0 and later, with access control enabled, a database
dump that includes any ACL category is administrative, not just a catalogue
query. Materialized and live views reject it for every principal, even one
holding `SYSTEM ADMIN` or `DATABASE ADMIN`. This includes plain
`SHOW CREATE DATABASE`, since its default is `INCLUDE ALL`. The check applies at
creation and refresh.

A materialized view refreshes under a context that sees every object, and every
reader of the view reads what the refresh stored. To keep a view from
Expand Down Expand Up @@ -568,6 +578,7 @@ GRANT DROP MATERIALIZED VIEW ON trades_hourly TO user1;
| `catalogue function from view <view> cannot be used in materialized view: <function>` | A regular view the query reads calls a [catalogue function](#catalogue-functions). A `SHOW` statement reports `catalogue statement from view` |
| `catalogue function cannot be used in materialized view without SYSTEM ADMIN: <function>` | The query calls a catalogue function and the creator lacks `SYSTEM ADMIN` (Enterprise). A `SHOW` statement reports `catalogue statement` |
| `administrative function cannot be used in materialized view: <function>` | The query calls an Enterprise administrative function, such as `active_permissions()` or `backups()` |
| `administrative statement cannot be used in materialized view: SHOW CREATE DATABASE` | The query includes an ACL dump (Enterprise); use `INCLUDE (SCHEMA)` under the [catalogue rules](#catalogue-functions) |
| `permission denied` | Missing required permission (Enterprise) |

## See also
Expand Down
Loading
Loading