-
Supabase 2022
+
+
© Supabase Inc
+
—
{footerData.map((item) => (
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 676be0ee00b..feda6da0a93 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -471,6 +471,10 @@ export const database = {
name: 'Managing Indexes',
url: '/guides/database/postgres/indexes',
},
+ {
+ name: 'Cascade Deletes',
+ url: '/guides/database/postgres/cascade-deletes',
+ },
{
name: 'Drop All Tables in Schema',
url: '/guides/database/postgres/dropping-all-tables-in-schema',
diff --git a/apps/docs/pages/guides/api/joins-and-nesting.mdx b/apps/docs/pages/guides/api/joins-and-nesting.mdx
index a8fccc6720e..205e1ceece0 100644
--- a/apps/docs/pages/guides/api/joins-and-nesting.mdx
+++ b/apps/docs/pages/guides/api/joins-and-nesting.mdx
@@ -138,9 +138,9 @@ create table
create table
members (
- "id" serial primary key,
"user_id" int references users,
- "team_id" int references teams
+ "team_id" int references teams,
+ primary key(user_id, team_id)
);
```
diff --git a/apps/docs/pages/guides/database/extensions/pgjwt.mdx b/apps/docs/pages/guides/database/extensions/pgjwt.mdx
index 77c8bcc8dea..1643f7e8ca4 100644
--- a/apps/docs/pages/guides/database/extensions/pgjwt.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgjwt.mdx
@@ -44,7 +44,7 @@ It's good practice to create the extension within a separate schema (like `exten
## API
-- [`sign(payload json, secret text, algorithm text default 'HSA256')`](https://github.com/michelp/pgjwt#usage): Signs a JWT containng _payload_ with _secret_ using _algorithm_.
+- [`sign(payload json, secret text, algorithm text default 'HSA256')`](https://github.com/michelp/pgjwt#usage): Signs a JWT containing _payload_ with _secret_ using _algorithm_.
- [`verify(token text, secret text, algorithm text default 'HSA256')`](https://github.com/michelp/pgjwt#usage): Decodes a JWT _token_ that was signed with _secret_ using _algorithm_.
Where:
diff --git a/apps/docs/pages/guides/database/postgres/cascade-deletes.mdx b/apps/docs/pages/guides/database/postgres/cascade-deletes.mdx
new file mode 100644
index 00000000000..8093a2083f1
--- /dev/null
+++ b/apps/docs/pages/guides/database/postgres/cascade-deletes.mdx
@@ -0,0 +1,195 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ title: 'Cascade Deletes',
+ description: 'Understand the types of foreign key constraint deletes',
+ footerHelpType: 'postgres',
+}
+
+There are 5 options for foreign key constraint deletes:
+
+1. **CASCADE:** When a row is deleted from the parent table, all related rows in the child table(s) are deleted as well.
+2. **RESTRICT:** When a row is deleted from the parent table, the delete operation is aborted if there are any related rows in the child table(s).
+3. **SET NULL:** When a row is deleted from the parent table, the values of the foreign key columns in the child table(s) are set to NULL.
+4. **SET DEFAULT:** When a row is deleted from the parent table, the values of the foreign key columns in the child table(s) are set to their default values.
+5. **NO ACTION:** This option is similar to RESTRICT, but it also has the option to be “deferred” to the end of a transaction. This means that other cascading deletes can run first, and then this delete constraint will only throw an error if there is referenced data remaining _at the end of the transaction_.
+
+These options can be specified when defining a foreign key constraint using the "ON DELETE" clause. For example, the following SQL statement creates a foreign key constraint with the `CASCADE` option:
+
+```sql
+alter table child_table
+add constraint fk_parent foreign key (parent_id) references parent_table (id)
+ on delete cascade;
+```
+
+This means that when a row is deleted from the "parent_table", all related rows in the "child_table" will be deleted as well.
+
+## `RESTRICT` vs `NO ACTION`
+
+The difference between `NO ACTION` and `RESTRICT` is subtle and can be a bit confusing.
+
+Both `NO ACTION` and `RESTRICT` are used to prevent deletion of a row in a parent table if there are related rows in a child table. However, there is a subtle difference in how they behave.
+
+When a foreign key constraint is defined with the option `RESTRICT`, it means that if a row in the parent table is deleted, the database will immediately raise an error and prevent the deletion of the row in the parent table. The database will not delete, update or set to NULL any rows in the referenced table(s).
+
+When a foreign key constraint is defined with the option `NO ACTION`, it means that if a row in the parent table is deleted, the database will also raise an error and prevent the deletion of the row in the parent table. However unlike `RESTRICT`, `NO ACTION` has the option defer the check using `INITIALLY DEFERRED`. This will only raise the above error _if_ the referenced rows still exist at the end of the transaction.
+
+The difference from `RESTRICT` is that a constraint marked as `NO ACTION INITIALLY DEFERRED` is deferred until the end of the transaction, rather than running immediately. If, for example there is another foreign key constraint between the same tables marked as `CASCADE`, the cascade will occur first and delete the referenced rows, and no error will be thrown by the deferred constraint. Otherwise if there are still rows referencing the parent row by the end of the transaction, an error will be raised just like before. Just like `RESTRICT`, the database will not delete, update or set to NULL any rows in the referenced table(s).
+
+In practice, you can use either `NO ACTION` or `RESTRICT` depending on your needs. `NO ACTION` is the default behavior if you do not specify anything. If you prefer to defer the check until the end of the transaction, use `NO ACTION INITIALLY DEFERRED`.
+
+## Example
+
+Let's further illustrate the difference with an example. We'll use the following data:
+
+`grandparent`
+
+| id | name |
+| --- | --------- |
+| 1 | Elizabeth |
+
+`parent`
+
+| id | name | parent_id |
+| --- | ------- | --------- |
+| 1 | Charles | 1 |
+| 2 | Diana | 1 |
+
+`child`
+
+| id | name | father | mother |
+| --- | ------- | ------ | ------ |
+| 1 | William | 1 | 2 |
+
+To create these tables and their data, we run:
+
+```sql
+create table grandparent (
+ id serial primary key,
+ name text
+);
+
+create table parent (
+ id serial primary key,
+ name text,
+ parent_id integer references grandparent (id)
+ on delete cascade
+);
+
+create table child (
+ id serial primary key,
+ name text,
+ father integer references parent (id)
+ on delete restrict
+);
+
+insert into grandparent
+ (id, name)
+values
+ (1, 'Elizabeth');
+
+insert into parent
+ (id, name, parent_id)
+values
+ (1, 'Charles', 1);
+
+insert into parent
+ (id, name, parent_id)
+values
+ (2, 'Diana', 1);
+
+-- We'll just link the father for now
+insert into child
+ (id, name, father)
+values
+ (1, 'William', 1);
+```
+
+### `RESTRICT`
+
+`RESTRICT` will prevent a delete and raise an error:
+
+```shell
+postgres=# delete from grandparent;
+ERROR: update or delete on table "parent" violates foreign key constraint "child_father_fkey" on table "child"
+DETAIL: Key (id)=(1) is still referenced from table "child".
+```
+
+Even though the foreign key constraint between parent and grandparent is `CASCADE`, the constraint between child and father is `RESTRICT`. Therefore an error is raised and no records are deleted.
+
+### `NO ACTION`
+
+Let's change the child-father relationship to `NO ACTION`:
+
+```sql
+alter table child
+drop constraint child_father_fkey;
+
+alter table child
+add constraint child_father_fkey foreign key (father) references parent (id)
+ on delete no action;
+```
+
+We see that `NO ACTION` will also prevent a delete and raise an error:
+
+```shell
+postgres=# delete from grandparent;
+ERROR: update or delete on table "parent" violates foreign key constraint "child_father_fkey" on table "child"
+DETAIL: Key (id)=(1) is still referenced from table "child".
+```
+
+### `NO ACTION INITIALLY DEFERRED`
+
+We'll change the foreign key constraint between child and father to be `NO ACTION INITIALLY DEFERRED`:
+
+```sql
+alter table child
+drop constraint child_father_fkey;
+
+alter table child
+add constraint child_father_fkey foreign key (father) references parent (id)
+ on delete no action initially deferred;
+```
+
+Here you will see that `INITIALLY DEFFERED` seems to operate like `NO ACTION` or `RESTRICT`. When we run a delete, it seems to make no difference:
+
+```shell
+postgres=# delete from grandparent;
+ERROR: update or delete on table "parent" violates foreign key constraint "child_father_fkey" on table "child"
+DETAIL: Key (id)=(1) is still referenced from table "child".
+```
+
+But, when we combine it with _other_ constraints, then any other constraints take precedence. For example, let's run the same but add a `mother` column that has a `CASCADE` delete:
+
+```sql
+alter table child
+add column mother integer references parent (id)
+ on delete cascade;
+
+update child
+set mother = 2
+where id = 1;
+```
+
+Then let's run a delete on the `grandparent` table:
+
+```shell
+postgres=# delete from grandparent;
+DELETE 1
+
+postgres=# select * from parent;
+ id | name | parent_id
+----+------+-----------
+(0 rows)
+
+postgres=# select * from child;
+ id | name | father | mother
+----+------+--------+--------
+(0 rows)
+```
+
+The `mother` deletion took precedence over the `father`, and so William was deleted. After William was deleted, there was no reference to “Charles” and so he was free to be deleted, even though previously he wasn't (without `INITIALLY DEFERRED`).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx
index 31bc5e274b2..a12b14f6c79 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx
@@ -22,7 +22,7 @@ Let's start building the Vue 3 app from scratch.
### Initialize a Nuxt 3 app
-We can use [`nuxi init`](https://v3.nuxtjs.org/getting-started/quick-start/) to create an app called `nuxt-user-management`:
+We can use [`nuxi init`](https://nuxt.com/docs/getting-started/installation) to create an app called `nuxt-user-management`:
```bash
npx nuxi init nuxt-user-management
diff --git a/apps/docs/pages/guides/platform/performance.mdx b/apps/docs/pages/guides/platform/performance.mdx
index e0c8a00f402..527d3e1c9f5 100644
--- a/apps/docs/pages/guides/platform/performance.mdx
+++ b/apps/docs/pages/guides/platform/performance.mdx
@@ -207,7 +207,7 @@ You can configure Postgres by executing the following statement, followed by a s
alter system set max_connections = '
';
```
-Note that [the default configuration used by the Supabase platform](https://github.com/supabase/supabase-admin-api/blob/master/optimizations/postgres.go) optimizes the database to maximize resource utilization, and as a result, you might also need to configure other options (e.g. `work_mem`, `shared_buffers`, `maintenance_work_mem`) in order to tune them towards your use-case, and to avoid causing instability in your database.
+Note that the default configuration used by the Supabase platform optimizes the database to maximize resource utilization, and as a result, you might also need to configure other options (e.g. `work_mem`, `shared_buffers`, `maintenance_work_mem`) in order to tune them towards your use-case, and to avoid causing instability in your database.
Once overridden, the Supabase platform will continue to respect your manually configured value (even if the add-on size is changed), unless the override is removed with the following statement, followed by a server restart:
diff --git a/apps/docs/pages/guides/resources.mdx b/apps/docs/pages/guides/resources.mdx
index 5bda171db2f..3e023445640 100644
--- a/apps/docs/pages/guides/resources.mdx
+++ b/apps/docs/pages/guides/resources.mdx
@@ -153,25 +153,31 @@ export const postgres = [
{
title: 'Managing Indexes',
hasLightIcon: true,
- href: '/guides/resources/postgres/indexes',
+ href: '/guides/database/postgres/indexes',
description: 'Improve query performance using various index types in Postgres.',
},
+ {
+ title: 'Cascade Deletes',
+ hasLightIcon: true,
+ href: '/guides/database/postgres/cascade-deletes',
+ description: 'Understand the types of foreign key constraint deletes.',
+ },
{
title: 'Drop all tables in schema',
hasLightIcon: true,
- href: '/guides/resources/postgres/dropping-all-tables-in-schema',
+ href: '/guides/database/postgres/dropping-all-tables-in-schema',
description: 'Delete all tables in a given schema.',
},
{
title: 'Select first row per group',
hasLightIcon: true,
- href: '/guides/resources/postgres/first-row-in-group',
+ href: '/guides/database/postgres/first-row-in-group',
description: 'Retrieve the first row in each distinct group.',
},
{
title: 'Print PostgreSQL version',
hasLightIcon: true,
- href: '/guides/resources/postgres/which-version-of-postgres',
+ href: '/guides/database/postgres/which-version-of-postgres',
description: 'Find out which version of Postgres you are running.',
},
]
diff --git a/apps/docs/pages/guides/resources/examples.mdx b/apps/docs/pages/guides/resources/examples.mdx
index ae03bc48105..6155462bedd 100644
--- a/apps/docs/pages/guides/resources/examples.mdx
+++ b/apps/docs/pages/guides/resources/examples.mdx
@@ -109,6 +109,7 @@ Build a basic Todo List with Supabase and your favorite frontend framework:
- Subscriptions with Supabase and Stripe Billing [Blog](https://www.sandromaglione.com/2021/04/29/supabase-auth-create-stripe-customer-subscription-supabase-stripe-billing-part-1/)
- Flutter Supabase Authentication [Blog](https://www.sandromaglione.com/2021/04/24/flutter-supabase-authentication/)
- Supabase in 6 minutes [Video](https://www.youtube.com/watch?v=c8DNV9yl0mg)
+- Supabase React Auth UI Tutorial [Video](https://youtu.be/6ch1PtIqCUw)
- Let's build SupaAuth - Build an Authentication System using Supabase, Next.js and Typescript [6-part Blog Series](https://aalam.in/blog/supabase-auth-intro-setup-next)
- In-depth self-hosting guide using Nginx [Blog](https://dev.to/chronsyn/self-hosting-with-supabase-1aii)
- Build an Email and Social Auth for Next JS with Supabase, Tailwind CSS 3.0 and TypeScript [Blog](https://creativedesignsguru.com/next-js-supabase-auth/)
diff --git a/apps/www/_blog/2023-04-28-postgres-pluggable-strorage.mdx b/apps/www/_blog/2023-04-28-postgres-pluggable-strorage.mdx
new file mode 100644
index 00000000000..50e4720cc6c
--- /dev/null
+++ b/apps/www/_blog/2023-04-28-postgres-pluggable-strorage.mdx
@@ -0,0 +1,70 @@
+---
+title: 'Next steps for Postgres pluggable storage'
+description: Exploring history of Postgres pluggable storage and the possibility of landing it in the Postgres core.
+author: paul_copplestone
+image: pluggable-storage/pluggable-storage-og.jpg
+thumb: pluggable-storage/pluggable-storage.jpg
+tags:
+ - postgres
+ - database
+date: '2023-04-28'
+toc_depth: 2
+---
+
+You might have recently read “[The Part of PostgreSQL We Hate the Most](https://ottertune.com/blog/the-part-of-postgresql-we-hate-the-most/)” which highlights some of the deficiencies with Postgres storage. Fortunately, there is ongoing work in the Postgres ecosystem to solve this using “pluggable storage”.
+
+This article explores the history of Postgres pluggable storage and the possibility of landing it in the Postgres core.
+
+## What is pluggable storage?
+
+Pluggable storage gives developers the ability to use different storage engines for different tables within the same database.
+
+Developers will be able to choose a storage method that is optimized for their specific needs. Some tables could be configured for high transactional loads, others for analytics workloads, and still others for archiving.
+
+Something like this is already [available in MySQL](https://en.wikipedia.org/wiki/Comparison_of_MySQL_database_engines), which uses the [InnoDB](https://en.wikipedia.org/wiki/InnoDB) as the default storage engine since MySQL 5.5. This replaced [MyISAM](https://en.wikipedia.org/wiki/MyISAM) which had it’s own [set of problems](https://www.percona.com/blog/using-myisam-in-production/).
+
+The “pluggable” part refers to the ability for developers to develop their own storage methods, similar to Postgres Extensions. While some databases offer multiple built-in storage methods by default, Postgres follows a core philosophy of flexibility and extensibility, and using a pluggable approach makes a lot of sense.
+
+## Current progress for pluggable storage
+
+In version 12, PostgreSQL introduced basic support for pluggable storage with the goal of [adding ZHeap](https://anarazel.de/talks/2019-05-30-pgcon-pluggable-table-storage/pluggable.pdf). Andres Freund and several contributors enabled that feature by refactoring and expanding a variety of table access method (TAM) APIs.
+
+ZHeap [was released](https://github.com/EnterpriseDB/zheap) with [promising results](https://www.pgcon.org/2018/schedule/attachments/501_zheap-a-new-storage-format-postgresql-5.pdf), providing an undo log and solving some long-term table bloat issues in Postgres.
+
+While it was a great start, unfortunately it appears that the work on ZHeap and the TAM API [has stalled](https://twitter.com/andy_pavlo/status/1590703943176589312).
+This leaves a few holes in that need to be fixed before the community can use the TAM API for pluggable storage. In particular:
+
+- The TAM API doesn’t provide much flexibility for working with indexes. For example, when you update a row an index update is an all-or-nothing procedure.
+- Row versions need to be identified by the pairing of a block number and offset number. This means you can’t implement some useful functions, like an index-organized table or undo-based versioning.
+
+Even without a full API for pluggable storage, there are a couple of solutions available on the market today:
+
+- **Citus’s** [Columnar Storage](https://docs.citusdata.com/en/v11.1/admin_guide/table_management.html#columnar-storage), which powers Microsoft’s [Hyperscale](https://azure.microsoft.com/en-us/updates/azure-database-for-postgresql-hyperscale-citus-is-now-available/) offering. Citus has a few [limitations](https://docs.citusdata.com/en/v11.1/admin_guide/table_management.html#limitations) due to the restrictions of the current API.
+- EDB’s [Advanced Storage Pack](https://www.infoworld.com/article/3681112/3-key-features-in-edb-postgresql-15.html), which is appears to provide some enhancements to heap storage, although the source is not available for us to confirm.
+
+## The future of pluggable storage
+
+Right now, pluggable storage requires a fork of Postgres. But there are several initiatives in the Postgres community that we’re excited about.
+
+- There have been community updates for [custom resource managers](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=5c279a6d350205cc98f91fb8e1d3e4442a6b25d1) that expand [write-ahead log (WAL)](https://www.postgresql.org/docs/current/wal-intro.html) support for TAM.
+- The creators at [OrioleDB](https://github.com/orioledb/orioledb) started with a fork of Postgres and have been quietly upstreaming code that would make pluggable storage a reality. In Postgres 14 their patchset was ~5,000 lines of code, representing the total number of changes required in their fork. In the upcoming Postgres 16 release, it is ~2,000 lines of code. That means that around 60% of the code is already committed to the Postgres core. (Disclosure: Supabase started [sponsoring OrioleDB](https://supabase.com/blog/supabase-series-b#where-were-going) in 2022).
+
+
+
+Alexander Korotkov, the maintainer of OrioleDB, will be talking at PGCON 23 about some of the [changes](https://www.pgcon.org/events/pgcon_2023/schedule/session/470-future-of-table-access-methods/) they are making. They are solving many ["wicked Postgres problems"](https://www.slideshare.net/AlexanderKorotkov/solving-postgresql-wicked-problems) with [dual pointers](https://www.orioledata.com/blog/buffer-management/) instead of buffer mapping, row-level WAL instead of block-level WAL, undo logs instead of bloat-prone MVCC, and index-organized tables instead of heap.
+
+
+
+## Timelines for pluggable storage
+
+Ambitiously, many of the changes required for pluggable storage could land in Postgres 17. If that happens, new storage engines like OrioleDB would be available as a simple extension for developers to use in any Postgres instance. This paves the way for other Pluggable Storage Engines, just like MySQL’s [support for multiple storage engines](https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html).
+
+As a community project, Postgres requires support for these initiatives. You can support pluggable storage by getting involved during Postgres [Commitfests](https://supabase.com/blog/postgresql-commitfest). Postgres is already one of the best database engines in the world, and pluggable storage would make it even more attractive and maintainable.
+
+## More Postgres content
+
+- [Storing OpenAI embeddings in Postgres with pgvector](https://supabase.com/blog/openai-embeddings-postgres-vector)
+- [Choosing a Postgres Primary Key](https://supabase.com/blog/choosing-a-postgres-primary-key)
+- [SQL or NoSQL? Why not use both (with PostgreSQL)?](https://supabase.com/blog/sql-or-nosql-both-with-postgresql)
+- [pg_jsonschema: JSON Schema support for Postgres](https://supabase.com/blog/pg-jsonschema-a-postgres-extension-for-json-validation)
+- [Implementing "seen by" functionality with Postgres](https://supabase.com/blog/seen-by-in-postgresql)
diff --git a/apps/www/components/CodeBlock/CodeBlock.tsx b/apps/www/components/CodeBlock/CodeBlock.tsx
index 5e6b106350a..c45e96b6021 100644
--- a/apps/www/components/CodeBlock/CodeBlock.tsx
+++ b/apps/www/components/CodeBlock/CodeBlock.tsx
@@ -87,6 +87,7 @@ function CodeBlock(props: CodeBlockProps) {