{
if (attrmiss[firstmissingnum].am_present)
break;
+ else
+ hasNulls = true;
}
/*
- * If there are no more missing values everything else must be NULL
+ * Now walk the missing attributes. If there is a missing value
+ * make space for it. Otherwise, it's going to be NULL.
*/
- if (firstmissingnum >= natts)
- {
- hasNulls = true;
- }
- else
+ for (attnum = firstmissingnum;
+ attnum < natts;
+ attnum++)
{
-
- /*
- * Now walk the missing attributes. If there is a missing value
- * make space for it. Otherwise, it's going to be NULL.
- */
- for (attnum = firstmissingnum;
- attnum < natts;
- attnum++)
+ if (attrmiss[attnum].am_present)
{
- if (attrmiss[attnum].am_present)
- {
- Form_pg_attribute att = TupleDescAttr(tupleDesc, attnum);
+ Form_pg_attribute att = TupleDescAttr(tupleDesc, attnum);
- targetDataLen = att_align_datum(targetDataLen,
- att->attalign,
- att->attlen,
- attrmiss[attnum].am_value);
+ targetDataLen = att_align_datum(targetDataLen,
+ att->attalign,
+ att->attlen,
+ attrmiss[attnum].am_value);
- targetDataLen = att_addlength_pointer(targetDataLen,
- att->attlen,
- attrmiss[attnum].am_value);
- }
- else
- {
- /* no missing value, so it must be null */
- hasNulls = true;
- }
+ targetDataLen = att_addlength_pointer(targetDataLen,
+ att->attlen,
+ attrmiss[attnum].am_value);
+ }
+ else
+ {
+ /* no missing value, so it must be null */
+ hasNulls = true;
}
}
} /* end if have missing values */
1 | 0
(20 rows)
-DROP TABLE t1;
DROP TABLE T;
+-- test that we account for missing columns without defaults correctly
+-- in expand_tuple, and that rows are correctly expanded for triggers
+CREATE FUNCTION test_trigger()
+RETURNS trigger
+LANGUAGE plpgsql
+AS $$
+
+begin
+ raise notice 'old tuple: %', to_json(OLD)::text;
+ if TG_OP = 'DELETE'
+ then
+ return OLD;
+ else
+ return NEW;
+ end if;
+end;
+
+$$;
+-- 2 new columns, both have defaults
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | 4 | 5
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":4,"y":5}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | 4 | 2
+(1 row)
+
+DROP TABLE t;
+-- 2 new columns, first has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | 4 |
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":4,"y":null}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | 4 | 2
+(1 row)
+
+DROP TABLE t;
+-- 2 new columns, second has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | | 5
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":null,"y":5}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | | 2
+(1 row)
+
+DROP TABLE t;
+-- 2 new columns, neither has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | |
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":3,"x":null,"y":null}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | 3 | | 2
+(1 row)
+
+DROP TABLE t;
+-- same as last 4 tests but here the last original column has a NULL value
+-- 2 new columns, both have defaults
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | 4 | 5
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":4,"y":5}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | 4 | 2
+(1 row)
+
+DROP TABLE t;
+-- 2 new columns, first has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | 4 |
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":4,"y":null}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | 4 | 2
+(1 row)
+
+DROP TABLE t;
+-- 2 new columns, second has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | | 5
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":null,"y":5}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | | 2
+(1 row)
+
+DROP TABLE t;
+-- 2 new columns, neither has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | |
+(1 row)
+
+UPDATE t SET y = 2;
+NOTICE: old tuple: {"id":1,"a":1,"b":2,"c":null,"x":null,"y":null}
+SELECT * FROM t;
+ id | a | b | c | x | y
+----+---+---+---+---+---
+ 1 | 1 | 2 | | | 2
+(1 row)
+
+DROP TABLE t;
+-- cleanup
+DROP FUNCTION test_trigger();
+DROP TABLE t1;
DROP FUNCTION set(name);
DROP FUNCTION comp();
DROP TABLE m;
AS z
FROM t1;
-DROP TABLE t1;
DROP TABLE T;
+
+-- test that we account for missing columns without defaults correctly
+-- in expand_tuple, and that rows are correctly expanded for triggers
+
+CREATE FUNCTION test_trigger()
+RETURNS trigger
+LANGUAGE plpgsql
+AS $$
+
+begin
+ raise notice 'old tuple: %', to_json(OLD)::text;
+ if TG_OP = 'DELETE'
+ then
+ return OLD;
+ else
+ return NEW;
+ end if;
+end;
+
+$$;
+
+-- 2 new columns, both have defaults
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- 2 new columns, first has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- 2 new columns, second has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- 2 new columns, neither has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,3);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- same as last 4 tests but here the last original column has a NULL value
+-- 2 new columns, both have defaults
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- 2 new columns, first has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int NOT NULL DEFAULT 4;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- 2 new columns, second has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int NOT NULL DEFAULT 5;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- 2 new columns, neither has default
+CREATE TABLE t (id serial PRIMARY KEY, a int, b int, c int);
+INSERT INTO t (a,b,c) VALUES (1,2,NULL);
+ALTER TABLE t ADD COLUMN x int;
+ALTER TABLE t ADD COLUMN y int;
+CREATE TRIGGER a BEFORE UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE test_trigger();
+SELECT * FROM t;
+UPDATE t SET y = 2;
+SELECT * FROM t;
+DROP TABLE t;
+
+-- cleanup
+DROP FUNCTION test_trigger();
+DROP TABLE t1;
DROP FUNCTION set(name);
DROP FUNCTION comp();
DROP TABLE m;