fix-multiple-assignment-to-domain-over-array.patch
text/x-diff
Filename: fix-multiple-assignment-to-domain-over-array.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/rewrite/rewriteHandler.c | 0 | 0 |
| src/test/regress/expected/domain.out | 0 | 0 |
| src/test/regress/sql/domain.sql | 0 | 0 |
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index f3c7526..0a6f4f3 100644
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
*************** process_matched_tle(TargetEntry *src_tle
*** 934,939 ****
--- 934,940 ----
const char *attrName)
{
TargetEntry *result;
+ CoerceToDomain *coerce_expr = NULL;
Node *src_expr;
Node *prior_expr;
Node *src_input;
*************** process_matched_tle(TargetEntry *src_tle
*** 970,979 ****
--- 971,1000 ----
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
* ArrayRefs are involved though.
+ *
+ * As a further complication, the destination column might be a domain,
+ * resulting in each assignment containing a CoerceToDomain node over a
+ * FieldStore or ArrayRef. These should have matching target domains, so
+ * we strip them and reconstitute a single CoerceToDomain over the
+ * combined FieldStore/ArrayRef nodes. (Notice that this has the result
+ * that the domain's checks are applied only after we do all the field or
+ * element updates, not after each one. This is arguably desirable.)
*----------
*/
src_expr = (Node *) src_tle->expr;
prior_expr = (Node *) prior_tle->expr;
+
+ if (src_expr && IsA(src_expr, CoerceToDomain) &&
+ prior_expr && IsA(prior_expr, CoerceToDomain) &&
+ ((CoerceToDomain *) src_expr)->resulttype ==
+ ((CoerceToDomain *) prior_expr)->resulttype)
+ {
+ /* we assume without checking that resulttypmod/resultcollid match */
+ coerce_expr = (CoerceToDomain *) src_expr;
+ src_expr = (Node *) ((CoerceToDomain *) src_expr)->arg;
+ prior_expr = (Node *) ((CoerceToDomain *) prior_expr)->arg;
+ }
+
src_input = get_assignment_input(src_expr);
prior_input = get_assignment_input(prior_expr);
if (src_input == NULL ||
*************** process_matched_tle(TargetEntry *src_tle
*** 1042,1047 ****
--- 1063,1078 ----
newexpr = NULL;
}
+ if (coerce_expr)
+ {
+ /* put back the CoerceToDomain */
+ CoerceToDomain *newcoerce = makeNode(CoerceToDomain);
+
+ memcpy(newcoerce, coerce_expr, sizeof(CoerceToDomain));
+ newcoerce->arg = (Expr *) newexpr;
+ newexpr = (Node *) newcoerce;
+ }
+
result = flatCopyTargetEntry(src_tle);
result->expr = (Expr *) newexpr;
return result;
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 41b70e2..9fb750d 100644
*** a/src/test/regress/expected/domain.out
--- b/src/test/regress/expected/domain.out
*************** INSERT INTO domarrtest values ('{2,2}',
*** 107,112 ****
--- 107,113 ----
INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}');
INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}');
ERROR: value too long for type character varying(4)
+ INSERT INTO domarrtest (testint4arr[1], testint4arr[3]) values (11,22);
select * from domarrtest;
testint4arr | testchar4arr
---------------+---------------------
*************** select * from domarrtest;
*** 115,121 ****
{2,2} | {{a,b},{c,d},{e,f}}
{2,2} | {{a},{c}}
| {{a,b,c},{d,e,f}}
! (5 rows)
select testint4arr[1], testchar4arr[2:2] from domarrtest;
testint4arr | testchar4arr
--- 116,123 ----
{2,2} | {{a,b},{c,d},{e,f}}
{2,2} | {{a},{c}}
| {{a,b,c},{d,e,f}}
! {11,NULL,22} |
! (6 rows)
select testint4arr[1], testchar4arr[2:2] from domarrtest;
testint4arr | testchar4arr
*************** select testint4arr[1], testchar4arr[2:2]
*** 125,131 ****
2 | {{c,d}}
2 | {{c}}
| {{d,e,f}}
! (5 rows)
select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest;
array_dims | array_dims
--- 127,134 ----
2 | {{c,d}}
2 | {{c}}
| {{d,e,f}}
! 11 |
! (6 rows)
select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest;
array_dims | array_dims
*************** select array_dims(testint4arr), array_di
*** 135,141 ****
[1:2] | [1:3][1:2]
[1:2] | [1:2][1:1]
| [1:2][1:3]
! (5 rows)
COPY domarrtest FROM stdin;
COPY domarrtest FROM stdin; -- fail
--- 138,145 ----
[1:2] | [1:3][1:2]
[1:2] | [1:2][1:1]
| [1:2][1:3]
! [1:3] |
! (6 rows)
COPY domarrtest FROM stdin;
COPY domarrtest FROM stdin; -- fail
*************** select * from domarrtest;
*** 149,157 ****
{2,2} | {{a,b},{c,d},{e,f}}
{2,2} | {{a},{c}}
| {{a,b,c},{d,e,f}}
{3,4} | {q,w,e}
|
! (7 rows)
drop table domarrtest;
drop domain domainint4arr restrict;
--- 153,173 ----
{2,2} | {{a,b},{c,d},{e,f}}
{2,2} | {{a},{c}}
| {{a,b,c},{d,e,f}}
+ {11,NULL,22} |
{3,4} | {q,w,e}
|
! (8 rows)
!
! update domarrtest set
! testint4arr[1] = testint4arr[1] + 1,
! testint4arr[3] = testint4arr[3] - 1
! where testchar4arr is null;
! select * from domarrtest where testchar4arr is null;
! testint4arr | testchar4arr
! ------------------+--------------
! {12,NULL,21} |
! {NULL,NULL,NULL} |
! (2 rows)
drop table domarrtest;
drop domain domainint4arr restrict;
*************** select pg_typeof('{1,2,3}'::dia || 42);
*** 182,187 ****
--- 198,289 ----
(1 row)
drop domain dia;
+ -- Test domains over arrays of composite
+ create type comptype as (r float8, i float8);
+ create domain dcomptypea as comptype[];
+ create table dcomptable (d1 dcomptypea unique);
+ insert into dcomptable values (array[row(1,2)]::dcomptypea);
+ insert into dcomptable values (array[row(3,4), row(5,6)]::comptype[]);
+ insert into dcomptable values (array[row(7,8)::comptype, row(9,10)::comptype]);
+ insert into dcomptable values (array[row(1,2)]::dcomptypea); -- fail on uniqueness
+ ERROR: duplicate key value violates unique constraint "dcomptable_d1_key"
+ DETAIL: Key (d1)=({"(1,2)"}) already exists.
+ insert into dcomptable (d1[1]) values(row(9,10));
+ insert into dcomptable (d1[1].r) values(11);
+ select * from dcomptable;
+ d1
+ --------------------
+ {"(1,2)"}
+ {"(3,4)","(5,6)"}
+ {"(7,8)","(9,10)"}
+ {"(9,10)"}
+ {"(11,)"}
+ (5 rows)
+
+ select d1[2], d1[1].r, d1[1].i from dcomptable;
+ d1 | r | i
+ --------+----+----
+ | 1 | 2
+ (5,6) | 3 | 4
+ (9,10) | 7 | 8
+ | 9 | 10
+ | 11 |
+ (5 rows)
+
+ update dcomptable set d1[2] = row(d1[2].i, d1[2].r);
+ select * from dcomptable;
+ d1
+ --------------------
+ {"(1,2)","(,)"}
+ {"(3,4)","(6,5)"}
+ {"(7,8)","(10,9)"}
+ {"(9,10)","(,)"}
+ {"(11,)","(,)"}
+ (5 rows)
+
+ update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0;
+ select * from dcomptable;
+ d1
+ --------------------
+ {"(11,)","(,)"}
+ {"(2,2)","(,)"}
+ {"(4,4)","(6,5)"}
+ {"(8,8)","(10,9)"}
+ {"(10,10)","(,)"}
+ (5 rows)
+
+ alter domain dcomptypea add constraint c1 check (value[1].r <= value[1].i);
+ alter domain dcomptypea add constraint c2 check (value[1].r > value[1].i); -- fail
+ ERROR: column "d1" of table "dcomptable" contains values that violate the new constraint
+ select array[row(2,1)]::dcomptypea; -- fail
+ ERROR: value for domain dcomptypea violates check constraint "c1"
+ insert into dcomptable values (array[row(1,2)]::comptype[]);
+ insert into dcomptable values (array[row(2,1)]::comptype[]); -- fail
+ ERROR: value for domain dcomptypea violates check constraint "c1"
+ insert into dcomptable (d1[1].r) values(99);
+ insert into dcomptable (d1[1].r, d1[1].i) values(99, 100);
+ insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail
+ ERROR: value for domain dcomptypea violates check constraint "c1"
+ update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail
+ ERROR: value for domain dcomptypea violates check constraint "c1"
+ update dcomptable set d1[1].r = d1[1].r - 1 where d1[1].i > 0;
+ select * from dcomptable;
+ d1
+ --------------------
+ {"(11,)","(,)"}
+ {"(99,)"}
+ {"(1,2)","(,)"}
+ {"(3,4)","(6,5)"}
+ {"(7,8)","(10,9)"}
+ {"(9,10)","(,)"}
+ {"(0,2)"}
+ {"(98,100)"}
+ (8 rows)
+
+ drop table dcomptable;
+ drop type comptype cascade;
+ NOTICE: drop cascades to type dcomptypea
+ -- Test not-null restrictions
create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15);
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 407d3ef..1fd7b11 100644
*** a/src/test/regress/sql/domain.sql
--- b/src/test/regress/sql/domain.sql
*************** INSERT INTO domarrtest values ('{2,2}',
*** 85,90 ****
--- 85,91 ----
INSERT INTO domarrtest values ('{2,2}', '{{"a"},{"c"}}');
INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}');
INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}');
+ INSERT INTO domarrtest (testint4arr[1], testint4arr[3]) values (11,22);
select * from domarrtest;
select testint4arr[1], testchar4arr[2:2] from domarrtest;
select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest;
*************** COPY domarrtest FROM stdin; -- fail
*** 100,105 ****
--- 101,113 ----
select * from domarrtest;
+ update domarrtest set
+ testint4arr[1] = testint4arr[1] + 1,
+ testint4arr[3] = testint4arr[3] - 1
+ where testchar4arr is null;
+
+ select * from domarrtest where testchar4arr is null;
+
drop table domarrtest;
drop domain domainint4arr restrict;
drop domain domainchar4arr restrict;
*************** select pg_typeof('{1,2,3}'::dia);
*** 111,116 ****
--- 119,164 ----
select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia
drop domain dia;
+
+ -- Test domains over arrays of composite
+
+ create type comptype as (r float8, i float8);
+ create domain dcomptypea as comptype[];
+ create table dcomptable (d1 dcomptypea unique);
+
+ insert into dcomptable values (array[row(1,2)]::dcomptypea);
+ insert into dcomptable values (array[row(3,4), row(5,6)]::comptype[]);
+ insert into dcomptable values (array[row(7,8)::comptype, row(9,10)::comptype]);
+ insert into dcomptable values (array[row(1,2)]::dcomptypea); -- fail on uniqueness
+ insert into dcomptable (d1[1]) values(row(9,10));
+ insert into dcomptable (d1[1].r) values(11);
+
+ select * from dcomptable;
+ select d1[2], d1[1].r, d1[1].i from dcomptable;
+ update dcomptable set d1[2] = row(d1[2].i, d1[2].r);
+ select * from dcomptable;
+ update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0;
+ select * from dcomptable;
+
+ alter domain dcomptypea add constraint c1 check (value[1].r <= value[1].i);
+ alter domain dcomptypea add constraint c2 check (value[1].r > value[1].i); -- fail
+
+ select array[row(2,1)]::dcomptypea; -- fail
+ insert into dcomptable values (array[row(1,2)]::comptype[]);
+ insert into dcomptable values (array[row(2,1)]::comptype[]); -- fail
+ insert into dcomptable (d1[1].r) values(99);
+ insert into dcomptable (d1[1].r, d1[1].i) values(99, 100);
+ insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail
+ update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail
+ update dcomptable set d1[1].r = d1[1].r - 1 where d1[1].i > 0;
+ select * from dcomptable;
+
+ drop table dcomptable;
+ drop type comptype cascade;
+
+
+ -- Test not-null restrictions
+
create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15);
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');