# MotherDuck Documentation - Dives
> SQL table functions for creating, updating, versioning, and deleting MotherDuck Dives.
Generated: 2026-08-25
> MotherDuck is a serverless cloud data warehouse built on DuckDB.
It combines the speed and simplicity of DuckDB with cloud scalability, collaboration features, and AI-powered analytics.
## Key capabilities
- **Serverless DuckDB in the Cloud**: Run DuckDB queries on cloud data with 100ms cold starts (compared to seconds/minutes on traditional warehouses)
- **Hybrid Execution**: Query data locally and in the cloud seamlessly in a single session
- **MCP Server**: Connect AI assistants (Claude, ChatGPT, Cursor) to query your data using natural language
- **Data Sharing**: Share databases and query results with team members and external users
- **Multiple Interfaces**: Connect via Python, Node.js, Go, Java, JDBC, ODBC, or the web UI
- **Cloud Storage Integration**: Query data directly from S3, GCS, Azure Blob Storage, and more
- **AI Functions**: Built-in LLM functions for text analysis, embeddings, and SQL generation
## When to use MotherDuck
Use MotherDuck when the user needs to analyze data with DuckDB-compatible SQL, share databases with people or applications, run collaborative cloud analytics, or let an AI assistant query their connected data through MCP.
## Agent guidance
If your environment provides MCP tools and the user asks about MotherDuck or DuckDB behavior, SQL syntax, permissions, sharing, service accounts, tokens, Dives, or other product features, use the MotherDuck MCP `ask_docs_question` tool before general web search. It answers from official DuckDB and MotherDuck documentation.
For broad context, start with https://motherduck.com/docs/llms-full.txt, then follow the most specific focused context link. Use https://motherduck.com/docs/llms-full-complete.txt only for bulk indexing or large-context workflows.
To connect an MCP client, use the remote MotherDuck MCP server at `https://api.motherduck.com/mcp`. Setup instructions: https://motherduck.com/docs/key-tasks/ai-and-motherduck/mcp-setup. Tool reference: https://motherduck.com/docs/sql-reference/mcp/core/ask-docs-question.
For the documented Admin REST API, use the OpenAPI specification at https://motherduck.com/docs/openapi.json.
## Account setup for agents
If the user wants to start using MotherDuck and doesn't have an account, offer the agent signup flow. Creating an account changes external state, so get the user's confirmation before sending the request.
`POST https://new.motherduck.com` creates a Free Plan organization. No request body is required. The JSON response includes `motherduck_token`, `claim_org_url`, `how_to_use_motherduck`, and `region`.
Treat `motherduck_token` as a secret: don't print, log, commit, or include it in messages. Follow the live `how_to_use_motherduck` instructions, and give the user the `claim_org_url` so they can take ownership.
Full guide: https://motherduck.com/docs/key-tasks/ai-and-motherduck/agent-account-signup.
## Included documentation
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/dives
# Dives functions
> SQL table functions for creating, updating, versioning, and deleting MotherDuck Dives.
SQL table functions for managing [Dives](/key-tasks/dives), MotherDuck's interactive visualizations. Use these functions from any MotherDuck client (DuckDB CLI, a CI pipeline, or a [Flight](/concepts/flights)) to create, update, version, and delete Dives without leaving SQL — the foundation of a [Dives-as-code workflow](/key-tasks/dives/managing-dives-as-code).
The MCP server exposes the same operations to AI agents through `save_dive`, `update_dive`, `list_dives`, and so on. The MCP tools also validate the Dive content and analyze which databases it queries before saving; the SQL functions store content as-is. See the [MCP reference](/sql-reference/mcp/) for the agent-facing tools.
:::note
These functions execute server-side on MotherDuck. They are not available on local-only DuckDB connections.
:::
A minimal end-to-end Dive from SQL:
```sql
-- Create the Dive
SELECT id, current_version
FROM MD_CREATE_DIVE(
title := 'Revenue overview',
content := $$
import { useSQLQuery } from "@motherduck/react-sql-query";
export default function Dive() {
const { data } = useSQLQuery(`SELECT SUM(revenue) AS total FROM analytics.sales`);
return
Total revenue: {data?.[0]?.total}
;
}
$$
);
-- Publish a new version of the component code
SELECT version
FROM MD_UPDATE_DIVE_CONTENT(
id := '',
content := ''
);
-- Inspect the version history
SELECT version, created_at
FROM MD_LIST_DIVE_VERSIONS(id := '');
```
## Available functions
## Included pages
- [MD_CREATE_DIVE](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-create-dive): Create a new Dive in your MotherDuck workspace.
- [MD_LIST_DIVES](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-list-dives): List all Dives in your MotherDuck account with pagination support.
- [MD_GET_DIVE](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-get-dive): Retrieve a Dive by ID including its full React component content.
- [MD_UPDATE_DIVE_CONTENT](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-update-dive-content): Publish a new version of a Dive's component code.
- [MD_UPDATE_DIVE_METADATA](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-update-dive-metadata): Update a Dive's title or description without creating a new version.
- [MD_DELETE_DIVE](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-delete-dive): Delete a Dive and its version history.
- [MD_UPDATE_DIVE_STATUS](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-update-dive-status): Set a Dive's governance status (draft, ready, endorsed, or archived).
- [MD_GET_DIVE_VERSION](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-get-dive-version): Fetch the content of a specific Dive version.
- [MD_LIST_DIVE_VERSIONS](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-list-dive-versions): List the version history of a Dive.
- [useSQLQuery hook](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/use-sql-query): React hook for querying MotherDuck data from within Dives.
- [useDiveState hook](https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/use-dive-state): React hook for storing shareable UI state in MotherDuck Dives.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-create-dive
# MD_CREATE_DIVE
> Create a new Dive in your MotherDuck workspace.
Creates a new [Dive](/key-tasks/dives) and returns its summary along with the initial version. The initial version (version `1`) is captured from `content`, `description`, `api_version`, and `required_resources`.
Unlike the [`save_dive` MCP tool](/sql-reference/mcp/dives/save-dive), `MD_CREATE_DIVE` does not validate the component code or analyze which databases it queries — the content is stored as-is. Follow the component contract from [`get_dive_guide`](/sql-reference/mcp/dives/get-dive-guide) to produce content that renders in the Dive viewer.
## Syntax
```sql
SELECT * FROM MD_CREATE_DIVE(
title := 'my_dive',
content := '',
description := 'What this Dive shows',
required_resources := [{'url': 'md:my_db', 'alias': 'my_db'}]
);
```
Function arguments accept literals and `getvariable()` calls. Subqueries and lateral column references are rejected, so store dynamic values with `SET VARIABLE` first.
## Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | `VARCHAR` | Yes | Human-readable Dive title. |
| `content` | `VARCHAR` | Yes | JSX/React source for the Dive: a single component with a default export, using `useSQLQuery` for live queries. |
| `description` | `VARCHAR` | No | A brief description of the Dive. |
| `required_resources` | `LIST(STRUCT(url VARCHAR, alias VARCHAR))` | No | Databases and shares the Dive queries, so the Dive viewer can attach them. `url` is a database (`md:`) or share (`md:_share//`) URL; `alias` is the name the Dive's SQL uses to reference it. |
| `api_version` | `UINTEGER` | No | Dive API version of the content. Defaults to `1`. |
## Return columns
| Column | Type | Description |
|---|---|---|
| `id` | `UUID` | Unique identifier of the created Dive. |
| `title` | `VARCHAR` | The Dive title. |
| `description` | `VARCHAR` | The Dive description, or `NULL`. |
| `owner_id` | `UUID` | Identifier of the owning user. |
| `current_version` | `INTEGER` | Always `1` for a newly created Dive. |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | Creation timestamp. |
| `updated_at` | `TIMESTAMP WITH TIME ZONE` | Last update timestamp. |
| `owner_name` | `VARCHAR` | Username of the owner. |
| `version_id` | `UUID` | Identifier of the initial version. |
| `version_storage_url` | `VARCHAR` | Internal storage location of the version's content. |
| `version_description` | `VARCHAR` | Description attached to the version, or `NULL`. |
| `version_created_at` | `TIMESTAMP WITH TIME ZONE` | When the version was created. |
| `version_api_version` | `UINTEGER` | Dive API version of the content. |
| `version_required_resources` | `LIST(STRUCT(name VARCHAR, alias VARCHAR, url VARCHAR, id UUID, resource_type VARCHAR))` | The resolved resources: each input `url` is resolved to its `name`, `id`, and `resource_type` (`database` or `share`). |
## Examples
Minimal Dive:
```sql
SELECT id, title, current_version
FROM MD_CREATE_DIVE(
title := 'Hello Dive',
content := 'export default function Dive() {
return
Hello from SQL
;
}'
);
```
A Dive querying a database, with the resource declared so viewers can attach it:
```sql
SELECT id
FROM MD_CREATE_DIVE(
title := 'Daily orders',
description := 'Order counts by day over the last 30 days',
content := $$
import { useSQLQuery } from "@motherduck/react-sql-query";
export default function Dive() {
const { data, isLoading } = useSQLQuery(`
SELECT order_date, COUNT(*) AS orders
FROM analytics.main.orders
WHERE order_date > now() - INTERVAL 30 DAY
GROUP BY ALL ORDER BY 1
`);
if (isLoading) return
Loading…
;
return
{JSON.stringify(data, null, 2)}
;
}
$$,
required_resources := [{'url': 'md:analytics', 'alias': 'analytics'}]
);
```
## Related
- [`MD_UPDATE_DIVE_CONTENT`](../md-update-dive-content) — Publish a new version of the component code.
- [`MD_UPDATE_DIVE_METADATA`](../md-update-dive-metadata) — Rename or re-describe a Dive.
- [`MD_DELETE_DIVE`](../md-delete-dive) — Delete a Dive.
- [`save_dive` MCP tool](/sql-reference/mcp/dives/save-dive) — AI-agent equivalent, with content validation.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-list-dives
# MD_LIST_DIVES
> List all Dives in your MotherDuck account with pagination support.
Lists all [Dives](/key-tasks/dives) in your MotherDuck account. Returns metadata for each Dive without the component content.
## Syntax
```sql
SELECT * FROM MD_LIST_DIVES();
SELECT * FROM MD_LIST_DIVES("limit" =100, "offset" =0);
```
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | `UINTEGER` | No | Maximum number of Dives to return |
| `offset` | `UINTEGER` | No | Number of Dives to skip |
| `include_org_shares` | `BOOLEAN` | No | Include Dives shared with your organization. Defaults to `false`. |
:::tip
`limit` and `offset` are reserved SQL keywords and must be double-quoted when used as named parameters.
:::
## Return columns
| Column | Type | Description |
|--------|------|-------------|
| `id` | `UUID` | Unique identifier of the Dive |
| `title` | `VARCHAR` | Dive title |
| `description` | `VARCHAR` | Dive description |
| `owner_id` | `UUID` | UUID of the Dive owner |
| `current_version` | `INTEGER` | Latest version number (1-based) |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | When the Dive was created |
| `updated_at` | `TIMESTAMP WITH TIME ZONE` | When the Dive was last updated |
| `owner_name` | `VARCHAR` | Name of the Dive owner |
| `status` | `VARCHAR` | The Dive's [status](/key-tasks/dives/dive-statuses): `draft`, `ready`, `endorsed`, or `archived` |
| `status_changed_at` | `TIMESTAMP WITH TIME ZONE` | When the status was last set (`NULL` until someone sets it) |
| `status_set_by` | `UUID` | UUID of the user who set the status (`NULL` until someone sets it) |
| `status_applies_to_version` | `UINTEGER` | The version the status was set against (`NULL` until someone sets it) |
## Examples
List all Dives:
```sql
SELECT * FROM MD_LIST_DIVES();
```
List the 10 Dives with the latest updates:
```sql
SELECT id, title, owner_name, updated_at
FROM MD_LIST_DIVES()
ORDER BY updated_at DESC
LIMIT 10;
```
List endorsed Dives:
```sql
SELECT id, title, owner_name, status_changed_at
FROM MD_LIST_DIVES()
WHERE status = 'endorsed';
```
Paginate through Dives:
```sql
SELECT * FROM MD_LIST_DIVES("limit" =20, "offset" =0); -- first page
SELECT * FROM MD_LIST_DIVES("limit" =20, "offset" =20); -- second page
```
## Related
- [`MD_GET_DIVE`](../md-get-dive) — Read a Dive including its content
- [`MD_UPDATE_DIVE_STATUS`](../md-update-dive-status) — Set a Dive's status
- [`MD_LIST_DIVE_VERSIONS`](../md-list-dive-versions) — List version history for a Dive
- [`list_dives` MCP tool](/sql-reference/mcp/dives/list-dives) — AI assistant equivalent
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-get-dive
# MD_GET_DIVE
> Retrieve a Dive by ID including its full React component content.
Retrieves a single [Dive](/key-tasks/dives) by ID, including the full React component source code for the current version.
## Syntax
```sql
SELECT * FROM MD_GET_DIVE(id ='your-dive-uuid'::UUID);
```
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | `UUID` | Yes | The unique identifier of the Dive |
## Return columns
| Column | Type | Description |
|--------|------|-------------|
| `id` | `UUID` | Unique identifier of the Dive |
| `title` | `VARCHAR` | Dive title |
| `description` | `VARCHAR` | Dive description |
| `owner_id` | `UUID` | UUID of the Dive owner |
| `current_version` | `INTEGER` | Latest version number (1-based) |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | When the Dive was created |
| `updated_at` | `TIMESTAMP WITH TIME ZONE` | When the Dive was last updated |
| `owner_name` | `VARCHAR` | Name of the Dive owner |
| `status` | `VARCHAR` | The Dive's [status](/key-tasks/dives/dive-statuses): `draft`, `ready`, `endorsed`, or `archived` |
| `status_changed_at` | `TIMESTAMP WITH TIME ZONE` | When the status was last set (`NULL` until someone sets it) |
| `status_set_by` | `UUID` | UUID of the user who set the status (`NULL` until someone sets it) |
| `status_applies_to_version` | `UINTEGER` | The version the status was set against (`NULL` until someone sets it) |
| `version_id` | `UUID` | UUID of the current version |
| `version_storage_url` | `VARCHAR` | Storage URL of the current version content |
| `version_description` | `VARCHAR` | Description/commit message for the current version |
| `version_created_at` | `TIMESTAMP WITH TIME ZONE` | When the current version was created |
| `version_api_version` | `UINTEGER` | API version used to create this version |
| `content` | `VARCHAR` | Full JSX/React component source code |
## Examples
Read a Dive by ID:
```sql
SELECT title, description, content
FROM MD_GET_DIVE(id ='a1b2c3d4-e5f6-7890-abcd-ef1234567890'::UUID);
```
Get the metadata without the content:
```sql
SELECT id, title, owner_name, current_version, updated_at
FROM MD_GET_DIVE(id ='a1b2c3d4-e5f6-7890-abcd-ef1234567890'::UUID);
```
## Errors
Returns an error if the Dive does not exist.
## Related
- [`MD_GET_DIVE_VERSION`](../md-get-dive-version) — Retrieve a specific historical version
- [`MD_LIST_DIVES`](../md-list-dives) — List all Dives to find IDs
- [`read_dive` MCP tool](/sql-reference/mcp/dives/read-dive) — AI assistant equivalent
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-update-dive-content
# MD_UPDATE_DIVE_CONTENT
> Publish a new version of a Dive's component code.
Publishes a new version of an existing [Dive](/key-tasks/dives). Every call creates a fresh version with the supplied `content` and increments the Dive's `current_version`; earlier versions remain readable through [`MD_GET_DIVE_VERSION`](../md-get-dive-version).
To change the Dive's title or description without creating a new version, use [`MD_UPDATE_DIVE_METADATA`](../md-update-dive-metadata) instead.
## Syntax
```sql
SELECT * FROM MD_UPDATE_DIVE_CONTENT(
id := '',
content := '',
description := 'What changed in this version',
required_resources := [{'url': 'md:my_db', 'alias': 'my_db'}]
);
```
Function arguments accept literals and `getvariable()` calls. Subqueries and lateral column references are rejected, so store dynamic values with `SET VARIABLE` first.
## Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | `UUID` | Yes | Identifier of the Dive to update. |
| `content` | `VARCHAR` | Yes | New JSX/React source for the Dive. |
| `description` | `VARCHAR` | No | Description attached to this version (separate from the Dive's own description). Useful as a change note. |
| `required_resources` | `LIST(STRUCT(url VARCHAR, alias VARCHAR))` | No | Databases and shares the new content queries. Replaces the previous version's list; resources are not carried forward, so pass the full list each time. |
| `api_version` | `UINTEGER` | No | Dive API version of the content. Defaults to `1`. |
## Return columns
Returns the newly created version:
| Column | Type | Description |
|---|---|---|
| `id` | `UUID` | Identifier of the new version (not the Dive). |
| `version` | `UINTEGER` | The new version number. |
| `storage_url` | `VARCHAR` | Internal storage location of the version's content. |
| `description` | `VARCHAR` | The version description, or `NULL`. |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | When the version was created. |
| `api_version` | `UINTEGER` | Dive API version of the content. |
| `required_resources` | `LIST(STRUCT(name VARCHAR, alias VARCHAR, url VARCHAR, id UUID, resource_type VARCHAR))` | The resolved resources for this version. |
## Examples
Publish a new version with a change note:
```sql
SELECT version, created_at
FROM MD_UPDATE_DIVE_CONTENT(
id := '',
content := $$
import { useSQLQuery } from "@motherduck/react-sql-query";
export default function Dive() {
const { data } = useSQLQuery(`SELECT COUNT(*) AS n FROM analytics.main.orders`);
return
Orders: {data?.[0]?.n}
;
}
$$,
description := 'Switch from daily breakdown to a single total',
required_resources := [{'url': 'md:analytics', 'alias': 'analytics'}]
);
```
## Related
- [`MD_CREATE_DIVE`](../md-create-dive) — Create a Dive.
- [`MD_LIST_DIVE_VERSIONS`](../md-list-dive-versions) — Browse the version history.
- [`MD_GET_DIVE_VERSION`](../md-get-dive-version) — Read an older version's content.
- [`update_dive` MCP tool](/sql-reference/mcp/dives/update-dive) — AI-agent equivalent, with content validation.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-update-dive-metadata
# MD_UPDATE_DIVE_METADATA
> Update a Dive's title or description without creating a new version.
Updates the title, description, or both of an existing [Dive](/key-tasks/dives). Metadata updates do not create a new version — `current_version` is unchanged. To publish new component code, use [`MD_UPDATE_DIVE_CONTENT`](../md-update-dive-content).
## Syntax
```sql
SELECT * FROM MD_UPDATE_DIVE_METADATA(
id := '',
title := '',
description := ''
);
```
## Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | `UUID` | Yes | Identifier of the Dive to update. |
| `title` | `VARCHAR` | No | New Dive title. Omit to leave unchanged. |
| `description` | `VARCHAR` | No | New Dive description. Omit to leave unchanged. |
## Return columns
Returns the updated Dive summary:
| Column | Type | Description |
|---|---|---|
| `id` | `UUID` | The Dive identifier. |
| `title` | `VARCHAR` | The current title. |
| `description` | `VARCHAR` | The current description, or `NULL`. |
| `owner_id` | `UUID` | Identifier of the owning user. |
| `current_version` | `INTEGER` | Latest version number (unchanged by this call). |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | Original creation timestamp. |
| `updated_at` | `TIMESTAMP WITH TIME ZONE` | Most recent update timestamp. |
| `owner_name` | `VARCHAR` | Username of the owner. |
## Examples
Rename a Dive:
```sql
SELECT id, title, current_version
FROM MD_UPDATE_DIVE_METADATA(
id := '',
title := 'Q1 revenue dashboard'
);
```
Update only the description:
```sql
SELECT id, description
FROM MD_UPDATE_DIVE_METADATA(
id := '',
description := 'Revenue trends filtered to Q1'
);
```
## Related
- [`MD_UPDATE_DIVE_CONTENT`](../md-update-dive-content) — Publish new component code as a new version.
- [`MD_GET_DIVE`](../md-get-dive) — Inspect the current summary and content.
- [`update_dive` MCP tool](/sql-reference/mcp/dives/update-dive) — AI-agent equivalent.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-delete-dive
# MD_DELETE_DIVE
> Delete a Dive and its version history.
Deletes a [Dive](/key-tasks/dives), including all of its versions. Deletion is permanent — there is no undo.
Deleting a Dive that does not exist (or was already deleted) returns a `Could not find Dive` error.
## Syntax
```sql
SELECT * FROM MD_DELETE_DIVE(id := '');
```
## Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | `UUID` | Yes | Identifier of the Dive to delete. |
## Return columns
| Column | Type | Description |
|---|---|---|
| `success` | `BOOLEAN` | `true` when the Dive was deleted. |
## Examples
```sql
SELECT success
FROM MD_DELETE_DIVE(id := '80000000-0000-0000-0000-000000000001');
```
After deletion, the Dive is no longer reachable through [`MD_LIST_DIVES`](../md-list-dives), [`MD_GET_DIVE`](../md-get-dive), or any other Dive function.
## Related
- [`MD_LIST_DIVES`](../md-list-dives) — List remaining Dives.
- [`delete_dive` MCP tool](/sql-reference/mcp/dives/delete-dive) — AI-agent equivalent.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-update-dive-status
# MD_UPDATE_DIVE_STATUS
> Set a Dive's governance status (draft, ready, endorsed, or archived).
Sets the [status](/key-tasks/dives/dive-statuses) of an existing [Dive](/key-tasks/dives). The status is recorded against a specific version of the Dive; later content updates don't change it.
## Syntax
```sql
SELECT * FROM MD_UPDATE_DIVE_STATUS(
id ='your-dive-uuid'::UUID,
status ='ready'
);
```
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | `UUID` | Yes | The unique identifier of the Dive |
| `status` | `VARCHAR` | Yes | One of `draft`, `ready`, `endorsed`, or `archived` |
| `version` | `UINTEGER` | No | The reviewed version the status applies to. Defaults to the Dive's current version. |
## Permissions
**Requires permission:** Update organization-wide Dive statuses — included by default in the Admin preset role. A custom role qualifies when it inherits a role that includes the permission. Owners can set draft, ready, or archived on their own Dives without this permission. Any status on any Dive, including endorsed, requires it. See [Roles and access control](/concepts/roles-and-access-control/#what-each-role-can-do).
## Return columns
Returns the updated Dive summary.
| Column | Type | Description |
|--------|------|-------------|
| `id` | `UUID` | Unique identifier of the Dive |
| `title` | `VARCHAR` | Dive title |
| `description` | `VARCHAR` | Dive description |
| `owner_id` | `UUID` | UUID of the Dive owner |
| `current_version` | `INTEGER` | Latest version number (1-based) |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | When the Dive was created |
| `updated_at` | `TIMESTAMP WITH TIME ZONE` | When the Dive was last updated |
| `owner_name` | `VARCHAR` | Name of the Dive owner |
| `status` | `VARCHAR` | The Dive's status |
| `status_changed_at` | `TIMESTAMP WITH TIME ZONE` | When the status was last set |
| `status_set_by` | `UUID` | UUID of the user who set the status |
| `status_applies_to_version` | `UINTEGER` | The version the status was set against |
## Examples
Mark a Dive as ready for others to use:
```sql
SELECT id, title, status, status_applies_to_version
FROM MD_UPDATE_DIVE_STATUS(
id ='a1b2c3d4-e5f6-7890-abcd-ef1234567890'::UUID,
status ='ready'
);
```
Endorse a specific reviewed version. This requires permission to update organization-wide Dive statuses:
```sql
SELECT *
FROM MD_UPDATE_DIVE_STATUS(
id ='a1b2c3d4-e5f6-7890-abcd-ef1234567890'::UUID,
status ='endorsed',
version =3
);
```
## Errors
- Returns an error if the Dive does not exist or you don't own it and don't have permission to update organization-wide Dive statuses.
- Returns an error if `status` isn't one of the four valid values.
- Returns an authorization error if you set `endorsed` without permission to update organization-wide Dive statuses.
- Returns an error if `version` doesn't exist for the Dive.
## Related
- [Dive statuses](/key-tasks/dives/dive-statuses) — What each status means and where it appears
- [`MD_LIST_DIVES`](../md-list-dives) — List Dives, including their statuses
- [`MD_GET_DIVE`](../md-get-dive) — Read a Dive, including its status
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-get-dive-version
# MD_GET_DIVE_VERSION
> Fetch the content of a specific Dive version.
Returns a single version of a [Dive](/key-tasks/dives), including its component code. Use this to read the code that was live at an earlier point, or to restore it by passing it back to [`MD_UPDATE_DIVE_CONTENT`](../md-update-dive-content).
## Syntax
```sql
SELECT * FROM MD_GET_DIVE_VERSION(
id := '',
version :=
);
```
## Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | `UUID` | Yes | Identifier of the Dive. |
| `version` | `UINTEGER` | Yes | The version to fetch. Version numbers start at `1` and increment on each content update. |
## Return columns
| Column | Type | Description |
|---|---|---|
| `id` | `UUID` | Identifier of the version (not the Dive). |
| `version` | `UINTEGER` | The version number. |
| `storage_url` | `VARCHAR` | Internal storage location of the version's content. |
| `description` | `VARCHAR` | Description attached to the version, or `NULL`. |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | When the version was created. |
| `api_version` | `UINTEGER` | Dive API version of the content. |
| `required_resources` | `LIST(STRUCT(name VARCHAR, alias VARCHAR, url VARCHAR, id UUID, resource_type VARCHAR))` | Databases and shares this version queries. |
| `content` | `VARCHAR` | The JSX/React source of this version. |
## Examples
Read the first version's code:
```sql
SELECT version, content
FROM MD_GET_DIVE_VERSION(
id := '80000000-0000-0000-0000-000000000001',
version := 1
);
```
Roll back by republishing an older version's content. Function arguments accept literals and `getvariable()` calls, but not subqueries or lateral column references. Store the old content in a SQL variable, then pass it back with `getvariable()`:
```sql
-- Step 1: read the content to restore into a SQL variable
SET VARIABLE rollback_content = (
SELECT content
FROM MD_GET_DIVE_VERSION(id := '', version := 2)
);
-- Step 2: republish it as a new version
SELECT version
FROM MD_UPDATE_DIVE_CONTENT(
id := '',
content := getvariable('rollback_content'),
description := 'Roll back to version 2'
);
```
## Related
- [`MD_LIST_DIVE_VERSIONS`](../md-list-dive-versions) — List all versions of a Dive.
- [`MD_GET_DIVE`](../md-get-dive) — Fetch the latest version and summary in one call.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/md-list-dive-versions
# MD_LIST_DIVE_VERSIONS
> List the version history of a Dive.
Returns every version of a single [Dive](/key-tasks/dives), newest first. Each row carries the version's metadata; fetch a version's component code with [`MD_GET_DIVE_VERSION`](../md-get-dive-version).
## Syntax
```sql
SELECT * FROM MD_LIST_DIVE_VERSIONS(
id := '',
"limit" := ,
"offset" :=
);
```
## Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | `UUID` | Yes | Identifier of the Dive. |
| `limit` | `UINTEGER` | No | Maximum number of versions to return. |
| `offset` | `UINTEGER` | No | Skip this many versions before returning. |
`limit` and `offset` collide with SQL keywords and must be quoted with double quotes when passed as named arguments.
## Return columns
| Column | Type | Description |
|---|---|---|
| `id` | `UUID` | Identifier of the version (not the Dive). |
| `version` | `UINTEGER` | Version number, newest first. |
| `storage_url` | `VARCHAR` | Internal storage location of the version's content. |
| `description` | `VARCHAR` | Description attached to the version, or `NULL`. |
| `created_at` | `TIMESTAMP WITH TIME ZONE` | When the version was created. |
| `api_version` | `UINTEGER` | Dive API version of the content. |
| `required_resources` | `LIST(STRUCT(name VARCHAR, alias VARCHAR, url VARCHAR, id UUID, resource_type VARCHAR))` | Databases and shares this version queries. |
## Examples
Get the full version history:
```sql
SELECT version, description, created_at
FROM MD_LIST_DIVE_VERSIONS(id := '');
```
Just the latest version:
```sql
SELECT version, created_at
FROM MD_LIST_DIVE_VERSIONS(id := '', "limit" := 1);
```
Skip the latest, get the rest:
```sql
SELECT version
FROM MD_LIST_DIVE_VERSIONS(id := '', "offset" := 1);
```
## Related
- [`MD_GET_DIVE_VERSION`](../md-get-dive-version) — Fetch a single version's content.
- [`MD_UPDATE_DIVE_CONTENT`](../md-update-dive-content) — Publish a new version.
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/use-sql-query
# useSQLQuery hook
> React hook for querying MotherDuck data from within Dives.
The `useSQLQuery` hook is a React hook that runs SQL queries against MotherDuck from within a [Dive](/key-tasks/dives). It handles loading states and error reporting so your component can focus on rendering data.
## Import
```jsx
import { useSQLQuery } from "@motherduck/react-sql-query";
```
## Syntax
```jsx
const { data, isLoading, isError, error, exportAs } = useSQLQuery(sql, options);
```
## Parameters
### `sql`
| Type | Required |
|------|----------|
| `string` | Yes |
A SQL query string to run against MotherDuck. The Dive runtime returns rows as objects.
Use fully qualified, double-quoted table names in your queries (`"database"."schema"."table"`) to avoid issues when the Dive runs outside your current database context.
### `options`
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | `boolean` | `true` | Set to `false` to skip query execution. Useful when a query depends on user input that isn't available yet. |
## Return value
| Property | Type | Description |
|----------|------|-------------|
| `data` | array or `undefined` | Query result as an array of row objects. `undefined` while loading. |
| `isLoading` | `boolean` | `true` while the query is running |
| `isError` | `boolean` | `true` if the query failed |
| `error` | `Error` or `null` | Error object if the query failed |
| `exportAs` | `(options) => Promise` | Exports this query as a file |
:::warning
`data` is the row array directly — there is no `data.rows` wrapper. Always guard against `undefined`:
```jsx
const rows = Array.isArray(data) ? data : [];
```
:::
## Numeric values
DuckDB returns `BIGINT`, `HUGEINT`, and `DECIMAL` as JavaScript `BigInt` or special objects, not `number`. These crash if rendered in JSX or used with `.toFixed()`. Define this helper at the top of every Dive and wrap all numeric values:
```jsx
const N = (v) => (v != null ? Number(v) : 0);
```
## Examples
### Basic query
```jsx
import { useSQLQuery } from "@motherduck/react-sql-query";
const N = (v) => (v != null ? Number(v) : 0);
export default function Dive() {
const { data, isLoading } = useSQLQuery(`
SELECT category, SUM(amount) AS total
FROM "my_db"."main"."sales"
GROUP BY ALL
`);
if (isLoading) return
);
}
```
### Conditional queries with `enabled`
Skip a query until a user selection is available:
```jsx
const [selected, setSelected] = useState(null);
const { data: details } = useSQLQuery(`
SELECT * FROM "my_db"."main"."products"
WHERE category = '${selected}'
`, { enabled: !!selected });
```
### Multiple independent queries
Each `useSQLQuery` call loads independently. Render the page layout immediately and show inline placeholders per section instead of a single loading spinner:
```jsx
const summary = useSQLQuery(`
SELECT COUNT(*) AS total, SUM(revenue) AS revenue
FROM "my_db"."main"."orders"
`);
const monthly = useSQLQuery(`
SELECT strftime(date_trunc('month', order_date), '%Y-%m') AS month,
SUM(revenue) AS revenue
FROM "my_db"."main"."orders"
GROUP BY 1 ORDER BY 1
`);
return (
{summary.isLoading
?
:
{N(summary.data?.[0]?.revenue)}
}
{monthly.isLoading
?
:
}
);
```
### Export query results
Use `exportAs()` when you want to export the same SQL query used by a `useSQLQuery()` hook. Exports must start from a user action, such as a button click.
```jsx
import { useSQLQuery } from "@motherduck/react-sql-query";
export default function Dive() {
const orders = useSQLQuery(`
SELECT *
FROM "my_db"."main"."orders"
ORDER BY order_date DESC
`);
return (
);
}
```
Use `useExport()` when the export should run a different SQL query than the rows rendered on the page:
```jsx
import { useExport } from "@motherduck/react-sql-query";
export default function DiveExportButton() {
const { exportQuery } = useExport();
return (
);
}
```
Supported formats are `csv`, `json`, `parquet`, and `xlsx`. The `filename` value is a base name; MotherDuck adds the file extension. You can pass DuckDB `COPY` writer options under `csv`, `json`, `parquet`, or `xlsx`:
```jsx
orders.exportAs({
format: "xlsx",
filename: "orders",
xlsx: {
sheet: "Orders",
header: true,
},
});
```
Exports run with DuckDB `COPY TO`, not from the rows already materialized in React. This means an export can contain more rows than the Dive renders. Export SQL must be read-only.
## Tips
- **Format dates in SQL, not JavaScript.** Use DuckDB's `strftime()` to format dates and timestamps as strings. DuckDB date types are returned as special objects that don't render in JSX.
- **Fill time series gaps in SQL.** Recharts does not interpolate missing time periods. Use `generate_series` with a `LEFT JOIN` to produce a continuous date spine.
- **Dollar-quoted strings.** When passing Dive content through SQL (for example, with [`MD_CREATE_DIVE`](/sql-reference/motherduck-sql-reference/dives/md-create-dive)), use [dollar-quoted string literals](https://duckdb.org/docs/stable/sql/data_types/literal_types#dollar-quoted-string-literals) to avoid escaping issues with nested quotes.
## Related
- [Dives SQL functions](/sql-reference/motherduck-sql-reference/dives/) — Manage Dives with SQL
- [`useDiveState` hook](/sql-reference/motherduck-sql-reference/dives/use-dive-state) — Store shareable filters, sorting, and view state in the Dive URL
- [Creating visualizations with Dives](/key-tasks/dives) — How-to guide
- [Embedding Dives in your web application](/key-tasks/dives/embedding-dives) — Handle embedded Dive sessions and exports
- [MCP Server](/sql-reference/mcp/) — Create Dives through AI assistants
---
Source: https://motherduck.com/docs/sql-reference/motherduck-sql-reference/dives/use-dive-state
# useDiveState hook
> React hook for storing shareable UI state in MotherDuck Dives.
The `useDiveState` hook is a React hook for storing interactive state in a [Dive](/key-tasks/dives). It works like React's `useState`, but it also syncs the value to the Dive URL so viewers can share the current filters, sorting, selected view, and drill-down state.
Use `useDiveState` for state that should survive a page refresh or travel with a shared URL. Use React's `useState` for temporary UI state such as input drafts, open dialogs, or dismissed banners.
## Import
```jsx
import { useDiveState } from "@motherduck/react-sql-query";
```
## Syntax
```jsx
const [value, setValue] = useDiveState(key, initialValue);
```
In TypeScript, pass the value type explicitly when you want a narrowed literal union or when the initial value is an empty array:
```tsx
const [view, setView] = useDiveState<"cards" | "list" | "compact">(
"view",
"cards"
);
const [selectedRegions, setSelectedRegions] = useDiveState(
"selectedRegions",
[]
);
```
## Parameters
### `key`
| Type | Required |
|------|----------|
| `string` | Yes |
A stable key for the state value. If two components in the same Dive call `useDiveState` with the same key, they read and write the same value.
Use short, descriptive keys such as `region`, `sortBy`, `selectedCustomer`, or `chartView`.
### `initialValue`
| Type | Required |
|------|----------|
| JSON-serializable value | Yes |
The value to use when the URL does not contain a value for `key`.
Values must be JSON-serializable: strings, numbers, booleans, `null`, arrays, and plain objects made from those values. Do not store `Date`, `Map`, `Set`, functions, class instances, credentials, or large row payloads in Dive state.
If TypeScript reports `Expected 3 arguments, but got 2` with a label about `useDiveState defaultValue must be JSON-serializable`, the `initialValue` is not a supported JSON value. There is no third argument to pass; change the value shape instead.
## Return value
| Property | Type | Description |
|----------|------|-------------|
| `value` | same type as `initialValue` | The current value for `key`, read from the URL state when present. |
| `setValue` | function | Updates the value and syncs it to the URL state. |
`setValue` accepts either a new value or an updater function, matching React's `useState` setter pattern.
## Reset state
Pass `undefined` to the setter to remove the key from the URL state. After reset, the hook reads from `initialValue` again.
```jsx
const [view, setView] = useDiveState("view", "summary");
return (
);
```
## Examples
### Share a filtered view
```jsx
import { useDiveState, useSQLQuery } from "@motherduck/react-sql-query";
export default function Dive() {
const [region, setRegion] = useDiveState("region", "all");
const regionFilter = {
all: "",
amer: "WHERE region = 'Americas'",
emea: "WHERE region = 'EMEA'",
apac: "WHERE region = 'APAC'",
}[region] ?? "";
const { data = [], isLoading } = useSQLQuery(`
SELECT region, SUM(revenue) AS revenue
FROM "analytics"."main"."orders"
${regionFilter}
GROUP BY ALL
ORDER BY revenue DESC
`);
if (isLoading) return
);
}
```
When a viewer changes the selected region, the Dive URL updates. Copying that URL shares the same selected region with another viewer.
### Share sort order
```jsx
const [sortBy, setSortBy] = useDiveState("sortBy", "revenue_desc");
const orderBy = {
revenue_desc: "revenue DESC",
revenue_asc: "revenue ASC",
customer: "customer_name ASC",
}[sortBy] ?? "revenue DESC";
```
Use a controlled set of sort values and map them to SQL fragments. Do not concatenate unrestricted user input into SQL.
### Share state between components
```jsx
function RegionPicker() {
const [region, setRegion] = useDiveState("region", "all");
return