From: Alvaro Herrera Date: Wed, 18 May 2022 18:28:31 +0000 (+0200) Subject: Check column list length in XMLTABLE/JSON_TABLE alias X-Git-Tag: REL_10_22~70 X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=16cb7db34f3c0a3b6e2973f6efbc6f2afa58c66a;p=postgresql.git Check column list length in XMLTABLE/JSON_TABLE alias We weren't checking the length of the column list in the alias clause of an XMLTABLE or JSON_TABLE function (a "tablefunc" RTE), and it was possible to make the server crash by passing an overly long one. Fix it by throwing an error in that case, like the other places that deal with alias lists. In passing, modify the equivalent test used for join RTEs to look like the other ones, which was different for no apparent reason. This bug came in when XMLTABLE was born in version 10; backpatch to all stable versions. Reported-by: Wang Ke Discussion: https://postgr.es/m/17480-1c9d73565bb28e90@postgresql.org --- diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 14ba4270dab..8ebee4fa5f3 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -1459,21 +1459,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, res_colnames = list_concat(res_colnames, r_colnames); res_colvars = list_concat(res_colvars, r_colvars); - /* - * Check alias (AS clause), if any. - */ - if (j->alias) - { - if (j->alias->colnames != NIL) - { - if (list_length(j->alias->colnames) > list_length(res_colnames)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("column alias list for \"%s\" has too many entries", - j->alias->aliasname))); - } - } - /* * Now build an RTE for the result of the join */ diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index bbbb0b618ab..25efa71234d 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1684,6 +1684,12 @@ addRangeTableEntryForTableFunc(ParseState *pstate, eref->colnames = list_concat(eref->colnames, list_copy_tail(tf->colnames, numaliases)); + if (numaliases > list_length(tf->colnames)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("%s function has %d columns available but %d columns specified", + "XMLTABLE", list_length(tf->colnames), numaliases))); + rte->eref = eref; /* @@ -1833,6 +1839,12 @@ addRangeTableEntryForJoin(ParseState *pstate, eref->colnames = list_concat(eref->colnames, list_copy_tail(colnames, numaliases)); + if (numaliases > list_length(colnames)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("join expression \"%s\" has %d columns available but %d columns specified", + eref->aliasname, list_length(colnames), numaliases))); + rte->eref = eref; /* diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out index 3ea4ed93a0a..f740f17b491 100644 --- a/src/test/regress/expected/int2.out +++ b/src/test/regress/expected/int2.out @@ -51,6 +51,10 @@ SELECT '' AS five, * FROM INT2_TBL; | -32767 (5 rows) +SELECT * FROM INT2_TBL AS f(a, b); +ERROR: table "f" has 1 columns available but 2 columns specified +SELECT * FROM (TABLE int2_tbl) AS s (a, b); +ERROR: table "s" has 1 columns available but 2 columns specified SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int2 '0'; four | f1 ------+-------- diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 774c8cb9746..e8f7a13a0a8 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -5403,6 +5403,9 @@ select * from 3 | 3 (6 rows) +-- check the number of columns specified +SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); +ERROR: join expression "ss" has 3 columns available but 4 columns specified -- check we don't try to do a unique-ified semijoin with LATERAL explain (verbose, costs off) select * from diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 9f739faf1cc..286fe92dcc2 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -956,6 +956,11 @@ DROP TABLE y; -- -- error cases -- +WITH x(n, b) AS (SELECT 1) +SELECT * FROM x; +ERROR: WITH query "x" has 1 columns available but 2 columns specified +LINE 1: WITH x(n, b) AS (SELECT 1) + ^ -- INTERSECT WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) SELECT * FROM x; diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index d887a314815..cb6e923d163 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -1142,6 +1142,9 @@ EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM xmltableview1; Table Function Call: XMLTABLE(('/ROWS/ROW'::text) PASSING (xmldata.data) COLUMNS id integer PATH ('@id'::text), _id FOR ORDINALITY, country_name text PATH ('COUNTRY_NAME/text()'::text) NOT NULL, country_id text PATH ('COUNTRY_ID'::text), region_id integer PATH ('REGION_ID'::text), size double precision PATH ('SIZE'::text), unit text PATH ('SIZE/@unit'::text), premier_name text DEFAULT ('not specified'::text) PATH ('PREMIER_NAME'::text)) (7 rows) +-- errors +SELECT * FROM XMLTABLE (ROW () PASSING null COLUMNS v1 timestamp) AS f (v1, v2); +ERROR: XMLTABLE function has 1 columns available but 2 columns specified -- XMLNAMESPACES tests SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), '/zz:rows/zz:row' diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql index 7dbafb6dacc..23339aee089 100644 --- a/src/test/regress/sql/int2.sql +++ b/src/test/regress/sql/int2.sql @@ -29,6 +29,10 @@ INSERT INTO INT2_TBL(f1) VALUES (''); SELECT '' AS five, * FROM INT2_TBL; +SELECT * FROM INT2_TBL AS f(a, b); + +SELECT * FROM (TABLE int2_tbl) AS s (a, b); + SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int2 '0'; SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> int4 '0'; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 80f3e9593be..9404ec86bb6 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1775,6 +1775,9 @@ select * from (select q1.v) ) as q2; +-- check the number of columns specified +SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); + -- check we don't try to do a unique-ified semijoin with LATERAL explain (verbose, costs off) select * from diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql index 7717ac1e52e..c4fe97a02ed 100644 --- a/src/test/regress/sql/with.sql +++ b/src/test/regress/sql/with.sql @@ -410,6 +410,9 @@ DROP TABLE y; -- error cases -- +WITH x(n, b) AS (SELECT 1) +SELECT * FROM x; + -- INTERSECT WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) SELECT * FROM x; diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index f7f5edc7171..73c323dc6c5 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -383,6 +383,9 @@ SELECT * FROM xmltableview1; EXPLAIN (COSTS OFF) SELECT * FROM xmltableview1; EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM xmltableview1; +-- errors +SELECT * FROM XMLTABLE (ROW () PASSING null COLUMNS v1 timestamp) AS f (v1, v2); + -- XMLNAMESPACES tests SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), '/zz:rows/zz:row'