2013-10-07 21 views
2

問題:在PostgreSQL,如果從temp_persontemp_person_two繼承,子表上默認的列值被忽略,如果表被改變。改變父表在PostgreSQL 8.4中斷子表默認

如何複製:

首先,創建表和子表。子表應該有一列具有默認值。

CREATE TEMPORARY TABLE temp_person (
    person_id SERIAL, 
    name  VARCHAR 
); 

CREATE TEMPORARY TABLE temp_person_two (
    has_default character varying(4) DEFAULT 'en'::character varying NOT NULL 
) INHERITS (temp_person); 

接下來,創建父表,其數據複製到子表(我知道這看起來像不好的設計,但是這是一個最小的測試用例來說明問題)的觸發器。

CREATE FUNCTION temp_person_insert() RETURNS trigger 
LANGUAGE plpgsql 
AS ' 
BEGIN 
INSERT INTO temp_person_two VALUES (NEW.*); 
RETURN NULL; 
END; 
'; 

CREATE TRIGGER temp_person_insert_trigger 
    BEFORE INSERT ON temp_person 
    FOR EACH ROW 
    EXECUTE PROCEDURE temp_person_insert(); 

然後將數據插入父項並從子項中選擇數據。數據應該是正確的。

INSERT INTO temp_person (name) VALUES ('ovid'); 
SELECT * FROM temp_person_two; 
person_id | name | has_default 
-----------+------+------------- 
     1 | ovid | en 
(1 row) 

最後,通過添加一個新的無關列來更改父表。嘗試插入數據和觀看「非空約束」衝突發生:

ALTER TABLE temp_person ADD column foo text; 
INSERT INTO temp_person(name) VALUES ('Corinna'); 
ERROR: null value in column "has_default" violates not-null constraint 
CONTEXT: SQL statement "INSERT INTO temp_person_two VALUES ( $1 .*)" 
PL/pgSQL function "temp_person_insert" line 2 at SQL statement 

我的版本:

testing=# select version(); 
               version 
------------------------------------------------------------------------------------------------------- 
PostgreSQL 8.4.17 on x86_64-pc-linux-gnu, compiled by GCC gcc-4.4.real (Debian 4.4.5-8) 4.4.5, 64-bit 
(1 row) 
+0

9.2中的相同行爲。 –

回答

2

它一直到9.3,但修復起來會非常棘手,我不確定這只是不良的行爲而不是錯誤。

約束仍然存在,但看看列順序。

        Table "pg_temp_2.temp_person" 
    Column |  Type  |       Modifiers        
-----------+-------------------+----------------------------------------------------------------- 
person_id | integer   | not null default nextval('temp_person_person_id_seq'::regclass) 
name  | character varying | 
Number of child tables: 1 (Use \d+ to list them.) 

            Table "pg_temp_2.temp_person_two" 
    Column |   Type   |       Modifiers        
-------------+----------------------+----------------------------------------------------------------- 
person_id | integer    | not null default nextval('temp_person_person_id_seq'::regclass) 
name  | character varying | 
has_default | character varying(4) | not null default 'en'::character varying 
Inherits: temp_person 

ALTER TABLE 
            Table "pg_temp_2.temp_person_two" 
    Column |   Type   |       Modifiers        
-------------+----------------------+----------------------------------------------------------------- 
person_id | integer    | not null default nextval('temp_person_person_id_seq'::regclass) 
name  | character varying | 
has_default | character varying(4) | not null default 'en'::character varying 
foo   | text     | 
Inherits: temp_person 

它可以在你的第一個例子,因爲你是切實做好:

INSERT INTO temp_person_two (person_id,name) 
VALUES (person_id, name) 

但看您的新列在子表中添加 - 底!所以,你最終

INSERT INTO temp_person_two (person_id,name,has_default) 
VALUES (person_id, name, foo) 

,而不是你所希望的內容:

INSERT INTO temp_person_two (person_id,name,foo)... 

那麼 - 這裏是正確的行爲?如果PostgreSQL洗牌可能破壞代碼的子表中的列。如果沒有,那也可以破壞代碼。碰巧,我不認爲沒有實質性的PG代碼改變,第一種選擇是可行的,所以在中期內不太可能這樣做。

道德故事:明確列出您的INSERT列名。

可能需要一段時間手動。你知道任何語言與正則表達式?;-)

0

這不是一個錯誤。 NEW.*展開爲新行中每列的值,因此您正在執行INSERT INTO temp_person_two VALUES (NEW.person_id, NEW.name, NEW.foo),如果您未指定最後一個值爲NULL(如果您沒有指定,則爲錯誤)。

即使在添加新列之前,我感到驚訝,因爲值的數量與子表中的字段數不匹配。據推測,它假定缺失尾隨值的默認值。

+0

我不知道爲什麼你會驚訝它以前的工作。 'NEW。*'確實可以正確擴展,並且由於我們沒有提供'has_default'列,所以該列的默認值已被正確使用。因此,「之前」版本就是我所期望的那種。 – Ovid

+0

我假設一個沒有明確列列表的INSERT,意味着所有列,而不是與列表長度匹配的列表前綴。 – ilmari