From 31a0f450cf13a4f0d951fd7a89fbc8b951af1a33 Mon Sep 17 00:00:00 2001 From: Miranda Limonczenko Date: Mon, 14 Sep 2026 17:32:36 -0700 Subject: [PATCH] docs(database): correct the Dashboard table creation steps (#50023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. ## 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`. --- apps/docs/content/guides/database/tables.mdx | 35 +++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/apps/docs/content/guides/database/tables.mdx b/apps/docs/content/guides/database/tables.mdx index 5132939f310..cbfa85a0d2f 100644 --- a/apps/docs/content/guides/database/tables.mdx +++ b/apps/docs/content/guides/database/tables.mdx @@ -76,9 +76,9 @@ and run the SQL queries yourself. 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**. @@ -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().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: