pgsql 分區試驗及創建規則失誤導致創建觸發器異常

鑑於mySQL前途不明決定轉向PostgreSQL。初步瞭解pgsql後發現其相當的強大,於是考慮把一個小項目遷移到pgsql上。對其中日誌表想玩玩pgsql的分區,最開始是參考pgsql手冊以規則(RULE)的方式來實現每一年一個日誌表

 

-- 禁止更新主表
CREATE RULE no_insert_on_tb_log AS ON INSERT TO tb_log
    DO INSTEAD NOTHING;

DROP TABLE IF EXISTS tb_log_y2010 CASCADE;

CREATE TABLE tb_log_y2010 (
	FOREIGN KEY (cid) REFERENCES tb_user ON DELETE CASCADE ON UPDATE CASCADE,
	CHECK ( log_time >= '2010-01-01' AND log_time <  '2010-12-31 24:0:0' )
) INHERITS (tb_log)
WITH (OIDS=FALSE);



CREATE OR REPLACE RULE insert_log_y2010 AS ON INSERT TO tb_log
	WHERE ( ctime >= '2010-01-01' AND ctime <  '2010-12-31 24:0:0' ) 
	DO INSTEAD	INSERT INTO tb_log_y2010 (log_code, log_stat, uid, log_ip, log_msg, log_time
 ) VALUES 
	( NEW.log_code, NEW.log_stat, NEW.uid, NEW.log_ip, NEW.log_msg, NEW.log_time ) ;
 

後來覺得每年需要生成新的日誌表以及相應的規則,用shell腳本來做不熟悉。於是決定換成觸發器的方式:

 

DROP TABLE IF EXISTS tb_log_y2010;
CREATE TABLE tb_log_y2010 (
	FOREIGN KEY (uid) REFERENCES tb_users ON DELETE CASCADE ON UPDATE CASCADE,
	CHECK ( log_time >=  '2010-01-01' AND log_time < '2011-01-01' )
) INHERITS (tb_log)
WITH (OIDS=FALSE);

INSERT INTO row_counts VALUES ('tb_log_y2010', 0);	
CREATE TRIGGER tb_log_y2010_count AFTER INSERT OR DELETE ON tb_log_y2010 
    FOR EACH ROW EXECUTE PROCEDURE count_trig();


CREATE OR REPLACE FUNCTION insert_log() RETURNS TRIGGER AS $$
	-- 重定向對tb_log表寫入到相應年份分區表,如果目標表不存在則由common.php中 write_log() 自動創建
	DECLARE
		tbname		TEXT :=  'tb_log_y' || to_char(current_timestamp, 'YYYY');
	BEGIN
		EXECUTE 'INSERT INTO ' || tbname  || 
                       ' (log_code, log_stat, uid, log_ip, log_msg, log_time) ' ||
			' VALUES ( ''' || NEW.log_code ||''','''|| NEW.log_stat ||''', '''|| NEW.uid ||
                        ''','''|| NEW.log_ip ||''','''||
			NEW.log_msg ||''','''|| NEW.log_time || ''' ) ';

		RETURN NULL;
	END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS insert_log_trig ON tb_log CASCADE;
CREATE TRIGGER insert_log_trig BEFORE INSERT ON tb_log 	
    FOR EACH ROW EXECUTE PROCEDURE public.insert_log() ;

 

然後在PHP寫日誌的函數裏面判斷錯誤代碼如果爲 ‘42P01’ 則表示(該年)日誌表不存在,於是就新建當年日誌表並且重新寫入出錯時的SQL語句。

 

接下來就寫PHP函數來遷移數據,但是遇上錯誤

42703:錯誤: 記錄"new"沒有字段"log_user" CONTEXT: 在EXECUTE 語句的第27行的PL/pgSQL函數"insert_log":

 

仔細回憶原來log_user字段已經被uid字段替代,不過寫的觸發器、觸發器函數以及日誌表都已經更改了的啊。用Navicat Lite 查看觸發器函數也沒問題。後來用EMS查看就發現了問題,怎麼函數定義完全不相同呢?

 

 

 

一點擊Function name下拉列表發現具有有兩個地方存在同樣的名稱:

 

 

 

再回憶,想起最開始用規則方式時曾經試圖在日誌的重寫規則中判斷table是否存在並新建:

 

CREATE OR REPLACE FUNCTION insert_log () RETURNS trigger AS $BODY$
	DECLARE
		curryear	TEXT := to_char(current_timestamp, 'YYYY');
		yearbegin	TEXT;
		yearend		TEXT;
		tblname		TEXT ;
		q			TEXT;
	BEGIN
		tblname := 'tb_log_y' || curryear ;

		IF NOT EXISTS (SELECT relname FROM pg_class WHERE relname = tblname) THEN
			yearbegin	:= curryear || '-01-01';
			yearend		:= curryear || '-01-01 24:0:0';
			q := $q$CREATE TABLE tblname (
					FOREIGN KEY (log_user) REFERENCES tb_users ON DELETE CASCADE 
                                             ON UPDATE CASCADE,
					CHECK ( log_time >= '$q$ || yearbegin || $q$' AND log_time < '$q$ ||
                                              yearend || $q$' )
				) INHERITS (tb_log)
				WITH (OIDS=FALSE);
			EXECUTE q;
		END IF;

		EXECUTE 'INSERT INTO tb_log_y' ||  || ' (log_code, log_stat, log_user,log_ip, log_msg,
                        log_time) ' || ' VALUES ( ''' || NEW.log_code ||''','''|| NEW.log_stat ||''','''|| 
                        NEW.log_user || ''','''|| NEW.log_ip ||''','''|| NEW.log_msg ||''','''||
                        NEW.log_time ||''' ) ';
		RETURN NULL;
	END;
$BODY$
  LANGUAGE 'plpgsql' ;
 

不知怎麼地就跑到pg_catalog裏面去了,導致pg_catalog和pulic存在同名函數。前者是最開始的規則,後者是觸發器函數,結果在創建insert_log_trig()觸發器時就出了問題使用了pg_catalog中的過程函數。指定過程函數shema解決問題:

DROP TRIGGER IF EXISTS insert_log_trig ON tb_log CASCADE;
CREATE TRIGGER insert_log_trig BEFORE INSERT
    ON tb_log FOR EACH ROW EXECUTE PROCEDURE public .insert_log() ;

 

再看看pg_catalog裏面那個的DDL,沒明白是怎麼創建到pg_catalog上面去的,因爲創建所有對象時都沒使用shema前綴

 

CREATE OR REPLACE FUNCTION "pg_catalog"."NewProc"()
  RETURNS "pg_catalog"."trigger" AS $BODY$
	DECLARE
		curryear	TEXT := to_char(current_timestamp, 'YYYY');
		yearbegin	TEXT;
		yearend		TEXT;
		tblname		TEXT ;
		q			TEXT;
	BEGIN
		tblname := 'tb_log_y' || curryear ;

		IF NOT EXISTS (SELECT relname FROM pg_class WHERE relname = tblname) THEN
			yearbegin	:= curryear || '-01-01';
			yearend		:= curryear || '-01-01 24:0:0';
			q := $q$CREATE TABLE tblname (
					FOREIGN KEY (log_user) REFERENCES tb_user ON DELETE CASCADE ON UPDATE CASCADE,
					CHECK ( log_time >= '$q$ || yearbegin || $q$' AND log_time < '$q$ || yearend || $q$' )
				) INHERITS (tb_log)
				WITH (OIDS=FALSE);
			EXECUTE q;
		END IF;

		EXECUTE 'INSERT INTO tb_log_y' ||  || ' (log_code, log_stat, log_user,log_ip, log_msg, log_time) ' ||
			' VALUES ( ''' || NEW.log_code ||''','''|| NEW.log_stat ||''','''|| NEW.log_user ||
			''','''|| NEW.log_ip ||''','''|| NEW.log_msg ||''','''|| NEW.log_time ||''' ) ';
		RETURN NULL;
	END;
$BODY$
  LANGUAGE 'plpgsql' ;
 

生命在於折騰。pgsql也挺好玩的

 

 

 

 

.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章