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