Supabase数据库函数 #
一、函数概述 #
1.1 什么是数据库函数 #
text
数据库函数特点
├── 在数据库服务器执行
├── 支持复杂逻辑
├── 可返回各种数据类型
├── 可被SQL和客户端调用
└── 性能高效
1.2 函数类型 #
| 类型 | 说明 |
|---|---|
| Scalar | 返回单个值 |
| Set-returning | 返回多行 |
| Trigger | 触发器函数 |
| Aggregate | 聚合函数 |
二、创建函数 #
2.1 基础语法 #
sql
CREATE OR REPLACE FUNCTION function_name(param1 type, param2 type)
RETURNS return_type AS $$
BEGIN
-- 函数体
RETURN result;
END;
$$ LANGUAGE plpgsql;
2.2 简单函数 #
sql
-- 返回单个值
CREATE OR REPLACE FUNCTION add(a INTEGER, b INTEGER)
RETURNS INTEGER AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
-- 调用
SELECT add(1, 2); -- 3
2.3 返回表数据 #
sql
-- 返回多行
CREATE OR REPLACE FUNCTION get_user_posts(user_id UUID)
RETURNS SETOF posts AS $$
BEGIN
RETURN QUERY
SELECT * FROM posts
WHERE author_id = user_id
ORDER BY created_at DESC;
END;
$$ LANGUAGE plpgsql;
-- 调用
SELECT * FROM get_user_posts('user-uuid');
2.4 返回JSON #
sql
CREATE OR REPLACE FUNCTION get_user_stats(user_id UUID)
RETURNS JSON AS $$
DECLARE
result JSON;
BEGIN
SELECT json_build_object(
'posts_count', (SELECT COUNT(*) FROM posts WHERE author_id = user_id),
'comments_count', (SELECT COUNT(*) FROM comments WHERE user_id = user_id),
'total_views', (SELECT COALESCE(SUM(view_count), 0) FROM posts WHERE author_id = user_id)
) INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql;
-- 调用
SELECT get_user_stats('user-uuid');
三、参数和返回值 #
3.1 参数模式 #
sql
CREATE OR REPLACE FUNCTION example(
IN in_param TEXT, -- 输入参数
OUT out_param TEXT, -- 输出参数
INOUT inout_param TEXT -- 输入输出参数
)
AS $$
BEGIN
out_param := in_param || ' processed';
inout_param := inout_param || ' modified';
END;
$$ LANGUAGE plpgsql;
3.2 默认参数 #
sql
CREATE OR REPLACE FUNCTION greet(
name TEXT,
greeting TEXT DEFAULT 'Hello'
)
RETURNS TEXT AS $$
BEGIN
RETURN greeting || ', ' || name || '!';
END;
$$ LANGUAGE plpgsql;
-- 调用
SELECT greet('World'); -- Hello, World!
SELECT greet('World', 'Hi'); -- Hi, World!
3.3 变长参数 #
sql
CREATE OR REPLACE FUNCTION sum_all(VARIADIC nums NUMERIC[])
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC := 0;
BEGIN
SELECT SUM(num) INTO total
FROM unnest(nums) AS num;
RETURN total;
END;
$$ LANGUAGE plpgsql;
-- 调用
SELECT sum_all(1, 2, 3, 4, 5); -- 15
四、变量和控制流 #
4.1 变量声明 #
sql
CREATE OR REPLACE FUNCTION example()
RETURNS TEXT AS $$
DECLARE
name TEXT := 'John';
count INTEGER;
created_at TIMESTAMPTZ;
data JSONB;
BEGIN
count := 10;
created_at := NOW();
data := '{"key": "value"}'::jsonb;
RETURN name;
END;
$$ LANGUAGE plpgsql;
4.2 条件语句 #
sql
CREATE OR REPLACE FUNCTION get_grade(score INTEGER)
RETURNS TEXT AS $$
BEGIN
IF score >= 90 THEN
RETURN 'A';
ELSIF score >= 80 THEN
RETURN 'B';
ELSIF score >= 70 THEN
RETURN 'C';
ELSIF score >= 60 THEN
RETURN 'D';
ELSE
RETURN 'F';
END IF;
END;
$$ LANGUAGE plpgsql;
4.3 循环语句 #
sql
CREATE OR REPLACE FUNCTION generate_series_custom(n INTEGER)
RETURNS SETOF INTEGER AS $$
BEGIN
FOR i IN 1..n LOOP
RETURN NEXT i;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
-- WHILE循环
CREATE OR REPLACE FUNCTION while_example()
RETURNS INTEGER AS $$
DECLARE
i INTEGER := 0;
total INTEGER := 0;
BEGIN
WHILE i < 10 LOOP
total := total + i;
i := i + 1;
END LOOP;
RETURN total;
END;
$$ LANGUAGE plpgsql;
五、客户端调用 #
5.1 使用RPC #
typescript
// 调用无参数函数
const { data, error } = await supabase.rpc('get_all_users')
// 调用带参数函数
const { data, error } = await supabase.rpc('get_user_posts', {
user_id: 'user-uuid'
})
// 调用返回JSON的函数
const { data, error } = await supabase.rpc('get_user_stats', {
user_id: 'user-uuid'
})
5.2 处理结果 #
typescript
const { data, error } = await supabase.rpc('get_user_posts', {
user_id: userId
})
if (error) {
console.error('Error:', error)
} else {
// data 是 posts 数组
console.log('Posts:', data)
}
六、安全权限 #
6.1 SECURITY DEFINER #
sql
-- 以函数定义者权限执行
CREATE OR REPLACE FUNCTION admin_function()
RETURNS TEXT AS $$
BEGIN
-- 可以访问定义者有权限的所有数据
RETURN 'Admin operation completed';
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
6.2 SECURITY INVOKER #
sql
-- 以调用者权限执行(默认)
CREATE OR REPLACE FUNCTION user_function()
RETURNS TEXT AS $$
BEGIN
-- 只能访问调用者有权限的数据
RETURN 'User operation completed';
END;
$$ LANGUAGE plpgsql SECURITY INVOKER;
七、常用示例 #
7.1 搜索函数 #
sql
CREATE OR REPLACE FUNCTION search_posts(search_query TEXT)
RETURNS SETOF posts AS $$
BEGIN
RETURN QUERY
SELECT *
FROM posts
WHERE
title ILIKE '%' || search_query || '%' OR
content ILIKE '%' || search_query || '%'
ORDER BY created_at DESC;
END;
$$ LANGUAGE plpgsql;
7.2 分页函数 #
sql
CREATE OR REPLACE FUNCTION paginate_posts(
page_num INTEGER DEFAULT 1,
page_size INTEGER DEFAULT 10
)
RETURNS TABLE(
id BIGINT,
title TEXT,
total_count BIGINT
) AS $$
BEGIN
RETURN QUERY
SELECT
p.id,
p.title,
COUNT(*) OVER() AS total_count
FROM posts p
ORDER BY p.created_at DESC
LIMIT page_size
OFFSET (page_num - 1) * page_size;
END;
$$ LANGUAGE plpgsql;
7.3 批量操作 #
sql
CREATE OR REPLACE FUNCTION bulk_insert_products(
products JSONB
)
RETURNS INTEGER AS $$
DECLARE
inserted_count INTEGER;
BEGIN
INSERT INTO products (name, price, category)
SELECT
p->>'name',
(p->>'price')::NUMERIC,
p->>'category'
FROM jsonb_array_elements(products) AS p;
GET DIAGNOSTICS inserted_count = ROW_COUNT;
RETURN inserted_count;
END;
$$ LANGUAGE plpgsql;
八、错误处理 #
8.1 异常捕获 #
sql
CREATE OR REPLACE FUNCTION safe_operation()
RETURNS TEXT AS $$
BEGIN
BEGIN
-- 可能出错的操作
INSERT INTO users (email) VALUES ('test@example.com');
RETURN 'Success';
EXCEPTION
WHEN unique_violation THEN
RETURN 'Email already exists';
WHEN others THEN
RETURN 'Error: ' || SQLERRM;
END;
END;
$$ LANGUAGE plpgsql;
8.2 自定义异常 #
sql
CREATE OR REPLACE FUNCTION transfer_funds(
from_id UUID,
to_id UUID,
amount NUMERIC
)
RETURNS TEXT AS $$
DECLARE
balance NUMERIC;
BEGIN
SELECT account_balance INTO balance
FROM accounts WHERE id = from_id;
IF balance < amount THEN
RAISE EXCEPTION 'Insufficient funds';
END IF;
UPDATE accounts SET account_balance = account_balance - amount
WHERE id = from_id;
UPDATE accounts SET account_balance = account_balance + amount
WHERE id = to_id;
RETURN 'Transfer completed';
END;
$$ LANGUAGE plpgsql;
九、总结 #
数据库函数要点:
| 操作 | 说明 |
|---|---|
| 创建 | CREATE FUNCTION |
| 调用 | supabase.rpc(‘name’, params) |
| 权限 | SECURITY DEFINER/INVOKER |
| 错误 | EXCEPTION WHEN |
下一步,让我们学习数据库触发器!
最后更新:2026-03-28