Make sure to prevent INSERT INTO ... SELECT queries involving subfield or sublink (#7912)

DESCRIPTION: Makes sure to prevent `INSERT INTO ... SELECT` queries involving subfield or sublink, to avoid crashes

The following query was crashing the backend:

```
INSERT INTO field_indirection_test_1 (
  int_col, ct1_col.int_1,ct1_col.int_2
) SELECT 0, 1, 2;
-- crash
```

En passant, added more tests with sublink in distributed_types and found
another query with wrong behavior:

```
INSERT INTO domain_indirection_test (f1,f3.if1) SELECT 0, 1;
ERROR:  could not find a conversion path from type 23 to 17619
-- not the expected ERROR
```

Fixed them by using `strip_implicit_coercions()` on target entry
expression before checking for the presence of a subscript or
fieldstore, else we fail to find the existing ones and wrongly accept to
execute unsafe query.
udf-citus-node-list
Cédric Villemain 2025-03-27 10:39:43 +01:00 committed by GitHub
parent 88904eda97
commit a7e686c106
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 3 deletions

View File

@ -1083,9 +1083,11 @@ ReorderInsertSelectTargetLists(Query *originalQuery, RangeTblEntry *insertRte,
AttrNumber originalAttrNo = get_attnum(insertRelationId,
oldInsertTargetEntry->resname);
/* we need to explore the underlying expression */
Node *expr = strip_implicit_coercions((Node *) oldInsertTargetEntry->expr);
/* see transformInsertRow() for the details */
if (IsA(oldInsertTargetEntry->expr, SubscriptingRef) ||
IsA(oldInsertTargetEntry->expr, FieldStore))
if (IsA(expr, SubscriptingRef) || IsA(expr, FieldStore))
{
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg(

View File

@ -386,6 +386,12 @@ HINT: Use the column name to insert or update the composite type as a single va
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1) VALUES (0, 1);
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1, ct1_col.int_2) SELECT 0, 1, 2;
ERROR: cannot plan distributed INSERT INTO ... SELECT query
HINT: Do not use array references and field stores on the INSERT target list.
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1) SELECT 0, 1;
ERROR: cannot plan distributed INSERT INTO ... SELECT query
HINT: Do not use array references and field stores on the INSERT target list.
CREATE TYPE ct2 as (int_2 int, text_1 text, int_1 int);
CREATE TABLE field_indirection_test_2 (int_col int, ct2_col ct2, ct1_col ct1);
SELECT create_distributed_table('field_indirection_test_2', 'int_col');
@ -399,10 +405,17 @@ INSERT INTO field_indirection_test_2 (ct2_col.int_1, int_col, ct2_col.text_1, ct
VALUES (0, 1, 'text1', 2), (3, 4, 'text1', 5);
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
INSERT INTO field_indirection_test_2 (ct2_col.int_1, int_col, ct2_col.text_1, ct1_col.int_2)
SELECT * FROM (VALUES (0, 1, 'text1', 2), (3, 4, 'text1', 5)) qq(int_1, int_col2, text_1, int_2);
ERROR: cannot plan distributed INSERT INTO ... SELECT query
HINT: Do not use array references and field stores on the INSERT target list.
-- not supported (field indirection in update)
UPDATE field_indirection_test_2 SET (ct2_col.text_1, ct1_col.int_2) = ('text2', 10) WHERE int_col=4;
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
UPDATE field_indirection_test_2 SET (ct2_col.text_1, ct1_col.int_2) = (SELECT 'text2', 10) WHERE int_col=4;
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
CREATE TYPE two_ints as (if1 int, if2 int);
CREATE DOMAIN domain AS two_ints CHECK ((VALUE).if1 > 0);
CREATE TABLE domain_indirection_test (f1 int, f3 domain, domain_array domain[]);
@ -416,22 +429,36 @@ SELECT create_distributed_table('domain_indirection_test', 'f1');
INSERT INTO domain_indirection_test (f1,f3.if1, f3.if2) VALUES (0, 1, 2);
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
INSERT INTO domain_indirection_test (f1,f3.if1, f3.if2) SELECT 0, 1, 2;
ERROR: cannot plan distributed INSERT INTO ... SELECT query
HINT: Do not use array references and field stores on the INSERT target list.
INSERT INTO domain_indirection_test (f1,f3.if1) VALUES (0, 1);
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
INSERT INTO domain_indirection_test (f1,f3.if1) SELECT 0, 1;
ERROR: cannot plan distributed INSERT INTO ... SELECT query
HINT: Do not use array references and field stores on the INSERT target list.
UPDATE domain_indirection_test SET domain_array[0].if2 = 5;
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
UPDATE domain_indirection_test SET domain_array[0].if2 = (SELECT 5);
ERROR: inserting or modifying composite type fields is not supported
HINT: Use the column name to insert or update the composite type as a single value
-- below are supported as we don't do any field indirection
INSERT INTO field_indirection_test_2 (ct2_col, int_col, ct1_col)
VALUES ('(1, "text1", 2)', 3, '(4, 5)'), ('(6, "text2", 7)', 8, '(9, 10)');
INSERT INTO field_indirection_test_2 (ct2_col, int_col, ct1_col)
SELECT * FROM (VALUES ('(1, "text1", 2)'::ct2, 3, '(4, 5)'::ct1), ('(6, "text2", 7)'::ct2, 8, '(9, 10)'::ct1)) qq(ct2_col, int_col, ct1_col);
UPDATE field_indirection_test_2 SET (ct2_col, ct1_col) = ('(10, "text10", 20)', '(40, 50)') WHERE int_col=8;
UPDATE field_indirection_test_2 SET (ct2_col, ct1_col) = (SELECT '(10, "text10", 20)'::ct2, '(40, 50)'::ct1) WHERE int_col=8;
SELECT * FROM field_indirection_test_2 ORDER BY 1,2,3;
int_col | ct2_col | ct1_col
---------------------------------------------------------------------
3 | (1," text1",2) | (4,5)
3 | (1," text1",2) | (4,5)
8 | (10," text10",20) | (40,50)
(2 rows)
8 | (10," text10",20) | (40,50)
(4 rows)
-- test different ddl propagation modes
SET citus.create_object_propagation TO deferred;
@ -583,3 +610,21 @@ SET client_min_messages TO error; -- suppress cascading objects dropping
DROP SCHEMA type_tests CASCADE;
DROP SCHEMA type_tests2 CASCADE;
DROP USER typeuser;
RESET client_min_messages;
CREATE SCHEMA issue_4704;
SET search_path TO issue_4704;
-- https://github.com/citusdata/citus/issues/4704
-- known to have crash citus before
CREATE TYPE comptype as (r float8, i float8);
CREATE DOMAIN dcomptypea as comptype[];
CREATE TABLE dcomptable (d1 dcomptypea unique);
SELECT create_distributed_table('dcomptable', 'd1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
insert into dcomptable values (array[row(1,2)]::dcomptypea);
SET client_min_messages TO error; -- suppress cascading objects dropping
DROP TABLE dcomptable;
DROP SCHEMA issue_4704 CASCADE;

View File

@ -245,6 +245,8 @@ SELECT create_distributed_table('field_indirection_test_1', 'int_col');
-- not supported (field indirection in single row insert)
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1, ct1_col.int_2) VALUES (0, 1, 2);
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1) VALUES (0, 1);
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1, ct1_col.int_2) SELECT 0, 1, 2;
INSERT INTO field_indirection_test_1 (int_col, ct1_col.int_1) SELECT 0, 1;
CREATE TYPE ct2 as (int_2 int, text_1 text, int_1 int);
CREATE TABLE field_indirection_test_2 (int_col int, ct2_col ct2, ct1_col ct1);
@ -253,9 +255,12 @@ SELECT create_distributed_table('field_indirection_test_2', 'int_col');
-- not supported (field indirection in multi row insert)
INSERT INTO field_indirection_test_2 (ct2_col.int_1, int_col, ct2_col.text_1, ct1_col.int_2)
VALUES (0, 1, 'text1', 2), (3, 4, 'text1', 5);
INSERT INTO field_indirection_test_2 (ct2_col.int_1, int_col, ct2_col.text_1, ct1_col.int_2)
SELECT * FROM (VALUES (0, 1, 'text1', 2), (3, 4, 'text1', 5)) qq(int_1, int_col2, text_1, int_2);
-- not supported (field indirection in update)
UPDATE field_indirection_test_2 SET (ct2_col.text_1, ct1_col.int_2) = ('text2', 10) WHERE int_col=4;
UPDATE field_indirection_test_2 SET (ct2_col.text_1, ct1_col.int_2) = (SELECT 'text2', 10) WHERE int_col=4;
CREATE TYPE two_ints as (if1 int, if2 int);
CREATE DOMAIN domain AS two_ints CHECK ((VALUE).if1 > 0);
@ -264,13 +269,19 @@ SELECT create_distributed_table('domain_indirection_test', 'f1');
-- not supported (field indirection to underlying composite type)
INSERT INTO domain_indirection_test (f1,f3.if1, f3.if2) VALUES (0, 1, 2);
INSERT INTO domain_indirection_test (f1,f3.if1, f3.if2) SELECT 0, 1, 2;
INSERT INTO domain_indirection_test (f1,f3.if1) VALUES (0, 1);
INSERT INTO domain_indirection_test (f1,f3.if1) SELECT 0, 1;
UPDATE domain_indirection_test SET domain_array[0].if2 = 5;
UPDATE domain_indirection_test SET domain_array[0].if2 = (SELECT 5);
-- below are supported as we don't do any field indirection
INSERT INTO field_indirection_test_2 (ct2_col, int_col, ct1_col)
VALUES ('(1, "text1", 2)', 3, '(4, 5)'), ('(6, "text2", 7)', 8, '(9, 10)');
INSERT INTO field_indirection_test_2 (ct2_col, int_col, ct1_col)
SELECT * FROM (VALUES ('(1, "text1", 2)'::ct2, 3, '(4, 5)'::ct1), ('(6, "text2", 7)'::ct2, 8, '(9, 10)'::ct1)) qq(ct2_col, int_col, ct1_col);
UPDATE field_indirection_test_2 SET (ct2_col, ct1_col) = ('(10, "text10", 20)', '(40, 50)') WHERE int_col=8;
UPDATE field_indirection_test_2 SET (ct2_col, ct1_col) = (SELECT '(10, "text10", 20)'::ct2, '(40, 50)'::ct1) WHERE int_col=8;
SELECT * FROM field_indirection_test_2 ORDER BY 1,2,3;
@ -355,3 +366,19 @@ SET client_min_messages TO error; -- suppress cascading objects dropping
DROP SCHEMA type_tests CASCADE;
DROP SCHEMA type_tests2 CASCADE;
DROP USER typeuser;
RESET client_min_messages;
CREATE SCHEMA issue_4704;
SET search_path TO issue_4704;
-- https://github.com/citusdata/citus/issues/4704
-- known to have crash citus before
CREATE TYPE comptype as (r float8, i float8);
CREATE DOMAIN dcomptypea as comptype[];
CREATE TABLE dcomptable (d1 dcomptypea unique);
SELECT create_distributed_table('dcomptable', 'd1');
insert into dcomptable values (array[row(1,2)]::dcomptypea);
SET client_min_messages TO error; -- suppress cascading objects dropping
DROP TABLE dcomptable;
DROP SCHEMA issue_4704 CASCADE;