diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 0338e4e1ad..7abaeda000 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1223,6 +1223,11 @@ searchForDefault(RangeTblEntry *rte) * in the multi-VALUES case. The targetlist will contain simple Vars * referencing the VALUES RTE, and therefore process_matched_tle() will * reject any such attempt with "multiple assignments to same column". + * + * If target relation is a view and the default value for a given column + * is NULL, then we defer replacing the corresponding DEFAULT item in the + * VALUES list to when the list is rewritten with the underlying table as + * the target relation. */ static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos) @@ -1268,8 +1273,19 @@ rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos) /* * If there is no default (ie, default is effectively NULL), - * we've got to explicitly set the column to NULL. + * we've got to explicitly set the column to NULL. But if + * the target relation is a view and the query will be + * rewritten to be executed on the base table, we should + * not finalize the value just yet. */ + if (!new_expr && + target_relation->rd_rel->relkind == RELKIND_VIEW && + !view_has_instead_trigger(target_relation, CMD_INSERT)) + { + newList = lappend(newList, col); + continue; + } + if (!new_expr) { new_expr = (Node *) makeConst(att_tup->atttypid, diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 420c5a54f2..f5ad5630d9 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2772,3 +2772,78 @@ drop view rw_view1; drop table base_tbl; drop user regress_view_user1; drop user regress_view_user2; +-- test that default values of underlying table are properly applied +-- when inserting via a view over the table +create table base_tab_def (a int, b int default 0); +create view base_tab_def_view as select * from base_tab_def; +-- will insert 0 for b +insert into base_tab_def_view values (1); +insert into base_tab_def_view values (2), (3); +insert into base_tab_def_view values (4, default), (5, default); +select * from base_tab_def order by 1; + a | b +---+--- + 1 | 0 + 2 | 0 + 3 | 0 + 4 | 0 + 5 | 0 +(5 rows) + +alter view base_tab_def_view alter b set default 1000; +-- will insert 1000 for b +insert into base_tab_def_view values (6), (7); +insert into base_tab_def_view values (8, default), (9, default); +-- will insert 0 for b +insert into base_tab_def values (10), (11); +select * from base_tab_def order by 1; + a | b +----+------ + 1 | 0 + 2 | 0 + 3 | 0 + 4 | 0 + 5 | 0 + 6 | 1000 + 7 | 1000 + 8 | 1000 + 9 | 1000 + 10 | 0 + 11 | 0 +(11 rows) + +alter view base_tab_def_view alter b drop default; +create function base_tab_def_view_instrig_func() returns trigger +as +$$ +begin + insert into base_tab_def values (new.a, new.b); + return new; +end; +$$ +language plpgsql; +create trigger base_tab_def_view_instrig instead of insert on base_tab_def_view + for each row execute function base_tab_def_view_instrig_func(); +-- will insert null, the view's default value, for b +insert into base_tab_def_view values (12, default), (13, default); +select * from base_tab_def order by 1; + a | b +----+------ + 1 | 0 + 2 | 0 + 3 | 0 + 4 | 0 + 5 | 0 + 6 | 1000 + 7 | 1000 + 8 | 1000 + 9 | 1000 + 10 | 0 + 11 | 0 + 12 | + 13 | +(13 rows) + +drop table base_tab_def cascade; +NOTICE: drop cascades to view base_tab_def_view +drop function base_tab_def_view_instrig_func; diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql index dc6d5cbe35..7cfab2de38 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1379,3 +1379,37 @@ drop view rw_view1; drop table base_tbl; drop user regress_view_user1; drop user regress_view_user2; + +-- test that default values of underlying table are properly applied +-- when inserting via a view over the table +create table base_tab_def (a int, b int default 0); +create view base_tab_def_view as select * from base_tab_def; +-- will insert 0 for b +insert into base_tab_def_view values (1); +insert into base_tab_def_view values (2), (3); +insert into base_tab_def_view values (4, default), (5, default); +select * from base_tab_def order by 1; +alter view base_tab_def_view alter b set default 1000; +-- will insert 1000 for b +insert into base_tab_def_view values (6), (7); +insert into base_tab_def_view values (8, default), (9, default); +-- will insert 0 for b +insert into base_tab_def values (10), (11); +select * from base_tab_def order by 1; +alter view base_tab_def_view alter b drop default; +create function base_tab_def_view_instrig_func() returns trigger +as +$$ +begin + insert into base_tab_def values (new.a, new.b); + return new; +end; +$$ +language plpgsql; +create trigger base_tab_def_view_instrig instead of insert on base_tab_def_view + for each row execute function base_tab_def_view_instrig_func(); +-- will insert null, the view's default value, for b +insert into base_tab_def_view values (12, default), (13, default); +select * from base_tab_def order by 1; +drop table base_tab_def cascade; +drop function base_tab_def_view_instrig_func;