docs(database): correct the Dashboard table creation steps (#50023)

Part 3 of a 5-PR stack on
`apps/docs/content/guides/database/tables.mdx`. Builds on #50022.

## Problem

Three claims fail the test that a reader following the page would hit a
wrong outcome.

**The Dashboard steps don't match the product.** They said to click
**New Table**, save, then click **New Column**. Columns are defined
inside the table creation panel, so a reader who follows the steps saves
a table with no columns and then hunts for a button that isn't part of
that flow. The labels are also sentence case in the product: **New
table** and **New column**.

**The Dashboard example diverges from the rest of the page.** The steps
created a table named `todos` with a `task` column, while the SQL tab
beside them and every later example use `movies`. A reader who took the
Dashboard path and then ran the first Loading data snippet got `relation
"movies" does not exist`.

**The page's SQL doesn't compose.** Running every fence in document
order showed that the many-to-many example under "Joining tables with
foreign keys" opened by creating `movies` again. A reader who already
created it got `relation "movies" already exists`, the block stopped, so
`actors` was never created, and the `private.salaries` example two
sections later then failed with `relation "public.actors" does not
exist`. **One redundant statement broke two sections.**

**The bulk loading example couldn't work.** `COPY` accepts text, CSV,
and binary input, and the page listed JSON. `\COPY movies FROM
'./movies.csv'` expects a value for every column, and `movies` has three
while the sample file has two. The options example passed `CSV HEADER`
against a file with no header row, which silently dropped the first
record. Found by CodeRabbit.

## Solution

Rewrites the five Dashboard steps to match the panel and to produce
`movies`, so both tabs leave the reader in the same place. Drops the
redundant `create table movies` from the many-to-many block; the prose
above it already says "You have a list of `movies`". Names the columns
in both `COPY` commands, corrects the format list, points the `HEADER`
example at a file that has one, and removes the space before each quoted
CSV field.

Dashboard changes verified against `TableEditor.tsx`, which renders
`ColumnManagement` inside the creation panel; `TableEditorMenu.tsx` and
`ColumnList.tsx` for the labels; and `DEFAULT_COLUMNS` in
`TableEditor.constants.ts` for the `id` and `created_at` columns the
editor adds.

## Checked and deliberately left

- `grant all on table transcripts to authenticated`. Broader than the
example needs, but a reader gets the working result the page promises,
so it doesn't meet the bar for this branch.
- "By default, views are accessed with their creator's permission."
Accurate. `security_invoker` is opt-in.
- `salary bigint` in the private schema example. Left here; it changes
in #50025, where the page starts recommending `numeric` for money and
the example becomes inconsistent with it.

## Flagged, not changed

- The `api-create-table-sm.mp4` video in the Dashboard tab may show the
older flow. Its contents weren't verified.
- **Nothing in the Views section is runnable.** All nine of its fences
depend on `students`, `courses`, and `grades`, which the page shows as
rendered tables and never creates. Supplying that DDL is new content, so
it isn't this branch's job, but it's worth a ticket.

## Manual testing

Preview:
https://docs-git-docs-tables-technical-supabase.vercel.app/docs/guides/database/tables

1. Open the preview and read the Dashboard tab under "Creating tables".
It says **New table**, creates `movies`, and defines both columns in the
same panel.
2. Open the Table Editor in a project and click **New table**. The panel
has a **Name** field and a **Columns** section, and there is no separate
**New Column** step.
3. In a fresh local database, run the SQL fences from "Creating tables"
through `private.salaries` in page order. Each one succeeds.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Updated the “Creating tables” guide to use a `movies` table with
`name` and `description` columns.
- Reworded and consolidated the table-creation steps, including the
`created_at` column in the SQL example.
- Updated bulk data loading instructions for CSV imports, connection
setup, named columns, and header-delimited files; removed JSON from the
listed formats.
- Simplified the many-to-many example by removing the redundant `movies`
table definition.
  - Clarified schema selection based on the current `search_path`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Miranda Limonczenko authored and GitHub committed 2026-09-14 17:32:36 -07:00
1 parent 2e435986c9
commit 31a0f450cf
1 file changed
+15 -20
+15 -20
View File
@@ -76,9 +76,9 @@ and run the SQL queries yourself.
</video>
1. Go to the [Table Editor](/dashboard/project/_/editor) page in the Dashboard.
2. Click **New Table** and create a table with the name `todos`.
3. Click **Save**.
4. Click **New Column** and create a column with the name `task` and type `text`.
2. Click **New table**.
3. Enter `movies` in the **Name** field.
4. Under **Columns**, click **Add column** and enter `name` with type `text`, then add `description` with type `text`. Leave the `id` and `created_at` columns as the editor created them.
5. Click **Save**.
</TabPanel>
@@ -88,7 +88,8 @@ and run the SQL queries yourself.
create table movies (
id bigint generated by default as identity primary key,
name text,
description text
description text,
created_at timestamptz default now()
);
```
@@ -276,27 +277,27 @@ await supabase.From<Movie>().Insert(movies);
#### Bulk data loading
When inserting large data sets, use Postgres's [COPY](https://www.postgresql.org/docs/current/sql-copy.html) command.
This loads data directly from a file into a table. Several file formats are available for copying data, including text, CSV, binary, and JSON.
This loads data directly from a file into a table. `COPY` accepts text, CSV, and binary input.
For example, to load a CSV file into your `movies` table:
```text ./movies.csv
"The Empire Strikes Back", "After the Rebels are brutally overpowered by the Empire on the ice planet Hoth, Luke Skywalker begins Jedi training with Yoda."
"Return of the Jedi", "After a daring mission to rescue Han Solo from Jabba the Hutt, the Rebels dispatch to Endor to destroy the second Death Star."
"The Empire Strikes Back","After the Rebels are brutally overpowered by the Empire on the ice planet Hoth, Luke Skywalker begins Jedi training with Yoda."
"Return of the Jedi","After a daring mission to rescue Han Solo from Jabba the Hutt, the Rebels dispatch to Endor to destroy the second Death Star."
```
[Connect](/docs/guides/database/connecting-to-postgres#direct-connection) to your database directly and load the file with the `COPY` command:
Set `DATABASE_URL` to your [direct connection string](/docs/guides/database/connecting-to-postgres#direct-connection), then load the file with the `COPY` command. Name the columns the file contains, so Postgres doesn't expect a value for `id`:
```bash
psql -h DATABASE_URL -p 5432 -d postgres -U postgres \
-c "\COPY movies FROM './movies.csv';"
psql "$DATABASE_URL" \
-c "\COPY movies (name, description) FROM './movies.csv' WITH (FORMAT csv);"
```
You can also use the `DELIMITER`, `HEADER`, and `FORMAT` options as defined in the Postgres [COPY](https://www.postgresql.org/docs/current/sql-copy.html) docs.
You can also pass options such as `DELIMITER` and `HEADER`, as defined in the Postgres [COPY](https://www.postgresql.org/docs/current/sql-copy.html) docs. `HEADER` skips the first line of the file, so use it only when that line names the columns:
```bash
psql -h DATABASE_URL -p 5432 -d postgres -U postgres \
-c "\COPY movies FROM './movies.csv' WITH DELIMITER ',' CSV HEADER"
psql "$DATABASE_URL" \
-c "\COPY movies (name, description) FROM './movies-with-header.csv' WITH (FORMAT csv, HEADER, DELIMITER ';');"
```
If you receive an error `FATAL: password authentication failed for user "postgres"`, reset your database password in **Database Settings** and try again.
@@ -326,12 +327,6 @@ For example, consider this situation:
- An `actor` can perform in several movies.
```sql
create table movies (
id bigint generated by default as identity primary key,
name text,
description text
);
create table actors (
id bigint generated by default as identity primary key,
name text
@@ -411,7 +406,7 @@ width={1600}
height={1145}
/>
If you don't explicitly pass a schema when creating a table, Postgres creates the table in the `public` schema.
If you don't explicitly pass a schema when creating a table, Postgres creates the table in the first schema in the current [`search_path`](https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH). The default path is `"$user", public`, so on a new project that's the `public` schema.
You can create schemas to organize tables. For example, you might want a private schema that's hidden from your API: