Check column list length in XMLTABLE/JSON_TABLE alias
authorAlvaro Herrera
Wed, 18 May 2022 18:28:31 +0000 (20:28 +0200)
committerAlvaro Herrera
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
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 71c360bea582f2538f893fbebef455f87d1ac73a..2763af9470e0cca1414897e2491d6634a2f5fa97 100644 (file)
@@ -1438,21 +1438,6 @@ transformFromClauseItem(ParseState *pstate, Node *n,
                                    &res_colnames, &res_colvars,
                                    res_nscolumns + res_colindex);
 
-       /*
-        * 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 and nsitem for the result of the join.
         * res_nscolumns isn't totally done yet, but that's OK because
index 74659190447a243e5a73dc91f10ed988fbd78b20..863cddad10b33b8b63ecdfae0946542406618192 100644 (file)
@@ -2001,6 +2001,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;
 
    /*
@@ -2180,6 +2186,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 55ea7202cd36dacc9f6dcd1cf909ace7b736d609..8a64df725c654947f487b3e78d09e19930fd9d46 100644 (file)
@@ -51,6 +51,10 @@ SELECT * 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 i.* FROM INT2_TBL i WHERE i.f1 <> int2 '0';
    f1   
 --------
index 50b5b37b047634f958234d35cf3c6bb12560abe3..3cb1b383dc1fe5001ede6f61ccb536dc83dfa32c 100644 (file)
@@ -5870,6 +5870,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 f435c9834bd4dd090c78fdb5699921b73b72cdc5..f24b4de9b4e77c2f10ba5475c31dbc4aa19912e3 100644 (file)
@@ -1763,6 +1763,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 55b65ef324d28de8d3fd55c6fe35058f2b865869..55ac49be261c3b6080ffc45b08b0b5f54e38a1f5 100644 (file)
@@ -1145,6 +1145,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 613b344704c003c5e5889933b56383c5b6571f8d..2f5ea64e3b45dbe87f744fd13e19679e4daf283d 100644 (file)
@@ -29,6 +29,10 @@ INSERT INTO INT2_TBL(f1) VALUES ('');
 
 SELECT * FROM INT2_TBL;
 
+SELECT * FROM INT2_TBL AS f(a, b);
+
+SELECT * FROM (TABLE int2_tbl) AS s (a, b);
+
 SELECT i.* FROM INT2_TBL i WHERE i.f1 <> int2 '0';
 
 SELECT i.* FROM INT2_TBL i WHERE i.f1 <> int4 '0';
index 52240fea7e3a818c7fb1589a083d30b1955e74ef..c451efa958929d34ea26c28424911119d7713592 100644 (file)
@@ -1985,6 +1985,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 254e9b1c444deab4c3c78ef9eb5b9dcd312ae00f..97255e6b185889d8d2812a9a7b121630a545becc 100644 (file)
@@ -795,6 +795,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 f3f83c7827d1a724cb640fc134f49b38102f931c..e3f90db4d568d0b0d9a6029c6723b5d85568f621 100644 (file)
@@ -384,6 +384,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'