Check column list length in XMLTABLE/JSON_TABLE alias
authorAlvaro Herrera <[email protected]>
Wed, 18 May 2022 18:28:31 +0000 (20:28 +0200)
committerAlvaro Herrera <[email protected]>
Wed, 18 May 2022 18:28:31 +0000 (20:28 +0200)
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 <[email protected]>
Discussion: https://postgr.es/m/17480-1c9d73565bb28e90@postgresql.org

src/backend/parser/parse_clause.c
src/backend/parser/parse_relation.c
src/test/regress/expected/int2.out
src/test/regress/expected/join.out
src/test/regress/expected/with.out
src/test/regress/expected/xml.out
src/test/regress/sql/int2.sql
src/test/regress/sql/join.sql
src/test/regress/sql/with.sql
src/test/regress/sql/xml.sql

index 14ba4270dab3c9947d0740d3777fa796b58d0d96..8ebee4fa5f38703f34ab311911895c26a312bd6e 100644 (file)
@@ -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
         */
index bbbb0b618ab749ece17fe4542f1fe739add60a0b..25efa71234db521de237e8bace344c36729740ed 100644 (file)
@@ -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;
 
    /*
index 3ea4ed93a0aad5819b0f660fd619b6fa2ea4183f..f740f17b4917d8b67105b6f1f5833f52a4df2c6a 100644 (file)
@@ -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   
 ------+--------
index 774c8cb97466a6b00c16ec09b525d51ee77bcfdf..e8f7a13a0a8fb4de94109e7d8591b13eeadbc045 100644 (file)
@@ -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
index 9f739faf1cc519ea066de7d4c24113cd8d14b923..286fe92dcc22f61ab3f2debc9c89b49a965d0ef8 100644 (file)
@@ -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;
index d887a31481552c0fc9239521fec6489a500ae5e3..cb6e923d163406fa5055d425bd97e51fba876634 100644 (file)
@@ -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'
index 7dbafb6dacc815e9f96bd5b0a7fddf9cbe03cfa9..23339aee089d868eae2e9c5a98edcca3542f9da4 100644 (file)
@@ -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';
index 80f3e9593beb834be52200314541fe3a832d4f05..9404ec86bb6edbb3cd679d598128bb3a9e992576 100644 (file)
@@ -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
index 7717ac1e52e770f2de0457c429d0b5b6149c34da..c4fe97a02ed29a432f86989d36825c3ed4a8dd1a 100644 (file)
@@ -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;
index f7f5edc7171c40c2c77dbe318da20fa22be15dac..73c323dc6c539d33dcf6a483b4f3837e8de6cf01 100644 (file)
@@ -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'