为什么NEW.name返回NULL? PostgreSQL 11

问题描述

我编写了一个函数,该函数自动为表创建新分区。我创建了一个触发器,但是当触发器触发并调用函数时,什么也没有发生,只是出现了一个错误

ERROR: query string argument of EXECUTE is null

功能代码

CREATE FUNCTION public.auto_part()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF 
AS $BODY$DECLARE
    partition_date TEXT;
    partition TEXT;
    startdate TEXT;
    enddate TEXT;
    query TEXT;
BEGIN
    partition_date := TO_CHAR(new.created_on,'YYYY-MM');
    startdate := partition_date || '-01';
    enddate := to_char(to_timestamp('YYYY-MM',partition_date) + '1 MONTH'::interval,'YYYY-MM') || '-01';
    
    partition := TG_TABLE_NAME || '_' || partition_date;
    
    IF NOT EXISTS(SELECT relname FROM pg_class WHERE relname=partition) THEN
        RAISE NOTICE 'TABLE %',TG_TABLE_NAME;
        RAISE NOTICE 'ID %',new.id;
        RAISE NOTICE 'NAME %',new.username;
        RAISE NOTICE 'CREATED_ON %',new.created_on;
        RAISE NOTICE 'PARTITION_DATE %',partition_date;
        RAISE NOTICE 'STARTDATE %',startdate;
        RAISE NOTICE 'A partition has been created %',partition;
        EXECUTE 'CREATE TABLE ' || partition || ' PARTITION OF ' || TG_TABLE_NAME || ' FOR VALUES FROM ('|| startdate || ') TO (' || enddate || ');';
        --RAISE NOTICE query;
        --EXECUTE query;
        RETURN 1;
    END IF;
END
$BODY$;

结论“升高通知”:

NOTICE:  TABLE users_sec
NOTICE:  ID <NULL>
NOTICE:  NAME <NULL>
NOTICE:  CREATED_ON <NULL>
NOTICE:  PARTITION_DATE <NULL>
NOTICE:  STARTDATE <NULL>
NOTICE:  A partition has been created <NULL>

ERROR:  query string argument of EXECUTE is null
CONTEXT:  PL/pgsql function auto_part() line 22 at EXECUTE
sql state: 22004

触发代码

CREATE TRIGGER auto_part_trigger
    BEFORE INSERT OR UPDATE 
    ON public.users_sec
    FOR EACH STATEMENT
    EXECUTE PROCEDURE public.auto_part();

插入示例:

INSERT INTO users_sec(
    username,password,created_on,last_logged_on)
    VALUES (
        'qwerty',random_string( 20 ),'2021-03-23','2021-03-24'
    );

表创建代码

CREATE TABLE public.users_sec
(
    id integer NOT NULL DEFAULT nextval('users_sec_id_seq'::regclass) ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),username text COLLATE pg_catalog."default" NOT NULL,password text COLLATE pg_catalog."default",created_on timestamp with time zone NOT NULL,last_logged_on timestamp with time zone NOT NULL,CONSTRAINT users_sec_pkey PRIMARY KEY (id,created_on)
) PARTITION BY RANGE (created_on) 
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

解决方法

如果要NEW包含要插入的行,则必须使用FOR EACH ROW级触发器。

you cannot have a BEFORE trigger FOR EACH ROW on a partitioned table起,这有点像22种情况。

我们可能是这样的:

  • 创建一个DEFAULT分区。与现有分区不匹配的所有行都将插入其中。

  • 在默认分区上定义BEFORE触发器FOR EACH ROW 。触发器将根据需要创建一个新分区,并将该行插入该分区。触发函数使用RETURN NULL来避免在默认分区本身中插入任何内容。

这样,默认分区将保持为空。而且,您只需为没有任何现有分区中的行的触发器支付开销!