You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL插入分区表触发“stack depth limit exceeded”错误求助

问题修复:PostgreSQL分区表插入时栈溢出错误

问题场景

创建了按created_date范围分区的some_data表,完成以下操作后执行插入脚本触发栈溢出错误:

  • 启用uuid-ossp扩展
  • 创建主表并配置范围分区规则
  • 创建2023年度分区表
  • 添加索引
  • 编写触发器函数及BEFORE INSERT触发器

错误信息:

ERROR: stack depth limit exceeded. Increase the configuration parameter "max_stack_depth"...

相关代码如下:

主表与分区创建

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE IF NOT EXISTS some_data(
  id uuid default uuid_generate_v4() not null,
  name text,
  created_date date not null,
  constraint "some_data_pk" primary key (id, created_date)
) PARTITION BY RANGE (created_date);

-- 2023年分区表
CREATE TABLE some_data_2023 PARTITION OF some_data FOR VALUES FROM ('2023-01-01 00:00:00') TO ('2024-01-01 00:00:00');

-- 创建索引
CREATE INDEX some_data_idx ON some_data(name);

触发器函数与触发器

CREATE OR REPLACE FUNCTION update_some_data_partitions()
RETURNS trigger AS
$$
BEGIN
    IF (NEW.created_date >= DATE '2023-01-01 00:00:00' AND NEW.created_date < DATE '2024-01-01 00:00:00') THEN
        INSERT INTO some_data_2023 VALUES (NEW.*);
    ELSE
        RAISE EXCEPTION 'No partition found for created_date %', NEW.created_date;
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER some_data_trigger
    BEFORE INSERT ON some_data
    FOR EACH row
    EXECUTE FUNCTION update_some_data_partitions();

插入脚本

INSERT INTO some_data(id, name, created_date)
VALUES (uuid_generate_v4(), 'name', '2023-05-08 12:00:00');

错误原因

触发器绑定在主表some_data上,当向主表插入数据时,触发器函数会执行INSERT INTO some_data_2023 VALUES (NEW.*)。但some_data_2023是主表的分区表,向分区表插入数据时会再次触发主表的BEFORE INSERT触发器,形成无限递归调用,最终导致栈溢出。

修复方案

方案1:移除多余触发器(推荐)

PostgreSQL的声明式分区会自动将主表的插入数据路由到对应分区,完全不需要手动编写触发器处理数据分发。执行以下语句清理冗余代码:

-- 删除触发器
DROP TRIGGER some_data_trigger ON some_data;
-- 删除触发器函数
DROP FUNCTION update_some_data_partitions();

之后直接执行插入脚本,数据会自动进入some_data_2023分区,不会再出现栈溢出问题。

方案2:修改触发器避免递归(适用于动态分区场景)

如果需要动态创建未来年份的分区,可以修改触发器函数,通过禁用局部触发器避免递归:

CREATE OR REPLACE FUNCTION update_some_data_partitions()
RETURNS trigger AS
$$
BEGIN
    -- 仅处理主表的插入操作,避免递归触发
    IF TG_TABLE_NAME = 'some_data' THEN
        DECLARE
            partition_name text := 'some_data_' || to_char(NEW.created_date, 'YYYY');
        BEGIN
            -- 检查分区是否存在,不存在则创建
            IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = partition_name) THEN
                EXECUTE format('CREATE TABLE %I PARTITION OF some_data FOR VALUES FROM (%L) TO (%L)',
                    partition_name,
                    date_trunc('year', NEW.created_date)::date,
                    date_trunc('year', NEW.created_date)::date + interval '1 year'
                );
            END IF;
            -- 禁用当前触发器后插入分区,避免递归
            SET LOCAL trigger_some_data_trigger TO OFF;
            EXECUTE format('INSERT INTO %I VALUES ($1.*)', partition_name) USING NEW;
            RESET trigger_some_data_trigger;
        END;
        RETURN NULL;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

注:动态分区场景更推荐使用pg_partman扩展,比手动编写触发器更稳定可靠。


内容的提问来源于stack exchange,提问作者Developus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 07:10:25