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

12 files changed:
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/jsonb_sqljson.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/jsonb_sqljson.sql
src/test/regress/sql/with.sql
src/test/regress/sql/xml.sql

index e2baa9d852efda43d9101b273c38454ee8ecade8..249255b65f5f000572747b8546d94dc0db2e09c7 100644 (file)
@@ -1444,21 +1444,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 00469763e88c2a2d53473eed79c01083dc3cfc11..926dcbf30e2f8af4ba30ae78a464194dd089edb7 100644 (file)
@@ -1989,11 +1989,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
                               bool inFromCl)
 {
    RangeTblEntry *rte = makeNode(RangeTblEntry);
-   char       *refname = alias ? alias->aliasname :
-   pstrdup(tf->functype == TFT_XMLTABLE ? "xmltable" : "json_table");
+   char       *refname;
    Alias      *eref;
    int         numaliases;
 
+   refname = alias ? alias->aliasname :
+       pstrdup(tf->functype == TFT_XMLTABLE ? "xmltable" : "json_table");
+
    Assert(pstate != NULL);
 
    rte->rtekind = RTE_TABLEFUNC;
@@ -2013,6 +2015,13 @@ 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",
+                       tf->functype == TFT_XMLTABLE ? "XMLTABLE" : "JSON_TABLE",
+                       list_length(tf->colnames), numaliases)));
+
    rte->eref = eref;
 
    /*
@@ -2192,6 +2201,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 0306648fa287714f82a7d9a4e3fced1cb3ebf534..109cf9baaacaf473f9e3b2cf264108325472cc3c 100644 (file)
@@ -45,6 +45,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 bd3375f2bae0858380198190f69fd0a161ff1763..2538bd6a79bea2253ff89e946e33ea2a40bb9c22 100644 (file)
@@ -5872,6 +5872,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 28338b4d193c0ad269abc1cd1918168b476a161d..ec7dc50593323243c9858795216aaa40b9c581d7 100644 (file)
@@ -1031,6 +1031,8 @@ SELECT * FROM JSON_TABLE(NULL, '$' COLUMNS ());
 ERROR:  syntax error at or near ")"
 LINE 1: SELECT * FROM JSON_TABLE(NULL, '$' COLUMNS ());
                                                     ^
+SELECT * FROM JSON_TABLE (NULL::jsonb, '$' COLUMNS (v1 timestamp)) AS f (v1, v2);
+ERROR:  JSON_TABLE function has 1 columns available but 2 columns specified
 -- NULL => empty table
 SELECT * FROM JSON_TABLE(NULL::jsonb, '$' COLUMNS (foo int)) bar;
  foo 
index d7316043748f230bc898fae80f7f5e17ce0bb935..30dd900e1141e4519b4ad377cf8c943942487fc4 100644 (file)
@@ -1794,6 +1794,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 8e8d33892db62ea4b5e80b7e8eb2b144fffe22f4..ea29066b78eeadd52677ac786f15ae7fe05c83de 100644 (file)
@@ -17,6 +17,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 6dd01b022e98fb54d900d9161c2dda9cf3fab663..a27a72086ef7420e8013407a8e9516049f3e27a7 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 ba1895d42db66e9679133fdf8956acf13c1728fd..fff253748082f4aa1ec60d8888c90a1664bce91e 100644 (file)
@@ -328,6 +328,8 @@ SELECT JSON_TABLE('[]', '$');
 -- Should fail (no columns)
 SELECT * FROM JSON_TABLE(NULL, '$' COLUMNS ());
 
+SELECT * FROM JSON_TABLE (NULL::jsonb, '$' COLUMNS (v1 timestamp)) AS f (v1, v2);
+
 -- NULL => empty table
 SELECT * FROM JSON_TABLE(NULL::jsonb, '$' COLUMNS (foo int)) bar;
 
index 3251c29584bd8bcfefc007b8f851e4f9aa9af735..5c52561a8aa4295a6b59f270d318f707a2d98c37 100644 (file)
@@ -803,6 +803,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'