鉴于此模式:
CREATE TABLE posts (
id uuid NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(),
title text NOT NULL CHECK (char_length(title) > 2),
author uuid NOT NULL DEFAULT auth.uid() REFERENCES profiles(id)
ON DELETE CASCADE ON UPDATE CASCADE,
content text NOT NULL CHECK (char_length(content) > 3),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE tags (
name text,
pid uuid REFERENCES posts(id) ON DELETE CASCADE ON UPDATE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (name, pid)
);
CREATE POLICY "rls_tags_read_public"
ON tags FOR SELECT
USING (true);
CREATE POLICY "rls_tags_create_authenticated_own_posts"
ON tags FOR INSERT TO "authenticated"
WITH CHECK (EXISTS (
SELECT 1 FROM posts p WHERE p.author = auth.uid()
AND p.id = pid
));
我正在尝试使用以下方式插入帖子:
CREATE OR REPLACE FUNCTION insert_post(
title text,
content text,
tags text[]
)
RETURNS SETOF posts
LANGUAGE sql
AS $$
WITH new_post AS (
INSERT INTO posts (title, content)
VALUES (title, content)
RETURNING *
),
insert_tags AS (
INSERT INTO tags (name, pid)
SELECT unnest(insert_post.tags), id FROM new_post
)
SELECT * FROM new_post;
$$;
但是,我得到:
'new row violates row-level security policy for table "tags"'
如果我取消 RLS 政策,它似乎会起作用。
我还可以在没有 CTE 的情况下将语言更改为
plpgsql
,而且似乎有效:
CREATE OR REPLACE FUNCTION insert_post(
title text,
content text,
tags text[]
)
RETURNS SETOF posts
LANGUAGE plpgsql
AS $$
DECLARE
new_post posts%ROWTYPE;
BEGIN
INSERT INTO posts (title, content)
VALUES (title, content)
RETURNING * INTO new_post;
INSERT INTO tags (name, pid)
SELECT unnest(tags), new_post.id;
RETURN QUERY SELECT * FROM posts WHERE id = new_post.id;
END;
$$;
我想写一些更复杂的交易,但我需要将
sql
和 CTE
用于其他目的。
RLS 不适用于 CTE 交易吗?
J
我实际上重新看了看我的桌子。我检查外键的原因是为了确认它是包含帖子作者 ID 的同一个表。因此,除非您将标签添加到帖子中,并且您是该帖子的作者,否则您无法添加标签。我的问题仍然存在,我无法使用语言 sql 来实现此功能。
这就是复合 辅助键(又名
UNIQUE CONSTRAINT
)索引的用途。
像这样:
pid
、 或不明确的名称,如 id
)DELETE CASCADE
很好,但是 ON UPDATE CASCADE
则不然。auth.uid()
扩展 - 我不推荐这样做,因为这意味着在存储层和应用程序代码层之间引入硬依赖关系 - 这意味着您将无法轻松手动编辑使用其他工具处理数据库中的数据,除非您小心地禁用所有引用 Supabase 扩展的 DEFAULT
约束。tags.name
和 tags.post_id
列缺少显式 NOT NULL
约束。虽然这些列是隐式的 NOT NULL
因为它们是 PRIMARY KEY
的一部分,如果您要更改 PK 定义,则需要将 NOT NULL
添加到您的 db-schema-in-source-control 否则重新部署将看到它们具有NULL
可用的列(并且您将数据库模式保留在源代码控制中,对吗?)tags.post_id
比 tags.name
更具选择性,因此 post_id
应该排在第一位。CREATE TABLE
语句的部分对齐到列中的习惯,这使它们的可读性显着提高:CREATE TABLE posts (
post_id uuid NOT NULL DEFAULT uuid_generate_v4(),
title text NOT NULL CHECK ( char_length( title ) > 2 ),
author_user_id uuid NOT NULL DEFAULT auth.uid() REFERENCES profiles( user_id ) ON DELETE CASCADE,
content text NOT NULL CHECK ( char_length( content ) > 3 ),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT PK_posts PRIMARY KEY ( post_id ),
CONSTRAINT UK_posts_author UNIQUE ( post_id, author_user_id ) /* Referenced by tags.FK_tags_posts */
);
CREATE TABLE tags (
name text NOT NULL,
post_id uuid NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
author_user_id uuid NOT NULL DEFAULT auth.uid() REFERENCES profiles( user_id ) ON DELETE CASCADE,
CONSTRAINT PK_tags PRIMARY KEY ( post_id, name ),
CONSTRAINT FK_tags_posts FOREIGN KEY ( post_id, author_user_id ) REFERENCES posts ( post_id, author_user_id ) /* This will use `UK_posts_author` */
);