]> granicus.if.org Git - postgresql/commitdiff
Improve behavior of WITH RECURSIVE with an untyped literal in the
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Oct 2008 22:50:55 +0000 (22:50 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Oct 2008 22:50:55 +0000 (22:50 +0000)
non-recursive term.  Per an example from Dickson S. Guedes.

src/backend/parser/parse_cte.c
src/test/regress/expected/with.out
src/test/regress/sql/with.sql

index 64f5e51c28f147fcaf21880b4c3cdfdc236a02da..29111acb968c61c1b88b409af26faecf0c4abef0 100644 (file)
@@ -8,12 +8,13 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_cte.c,v 2.1 2008/10/04 21:56:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_cte.c,v 2.2 2008/10/05 22:50:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "catalog/pg_type.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_cte.h"
@@ -339,6 +340,8 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
        foreach(tlistitem, tlist)
        {
                TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+               Oid                     coltype;
+               int32           coltypmod;
 
                if (te->resjunk)
                        continue;
@@ -351,10 +354,23 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
                        attrname = pstrdup(te->resname);
                        cte->ctecolnames = lappend(cte->ctecolnames, makeString(attrname));
                }
-               cte->ctecoltypes = lappend_oid(cte->ctecoltypes,
-                                                                          exprType((Node *) te->expr));
-               cte->ctecoltypmods = lappend_int(cte->ctecoltypmods,
-                                                                                exprTypmod((Node *) te->expr));
+               coltype = exprType((Node *) te->expr);
+               coltypmod = exprTypmod((Node *) te->expr);
+               /*
+                * If the CTE is recursive, force the exposed column type of any
+                * "unknown" column to "text".  This corresponds to the fact that
+                * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text.
+                * We might see "unknown" as a result of an untyped literal in
+                * the non-recursive term's select list, and if we don't convert
+                * to text then we'll have a mismatch against the UNION result.
+                */
+               if (cte->cterecursive && coltype == UNKNOWNOID)
+               {
+                       coltype = TEXTOID;
+                       coltypmod = -1;         /* should be -1 already, but be sure */
+               }
+               cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
+               cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
        }
        if (varattno < numaliases)
                ereport(ERROR,
index 65afc1a0c670b5d2fbe63bff38a76220ad942792..5b45ac89dfcbe997791b87989db1824482c2eb76 100644 (file)
@@ -69,6 +69,30 @@ SELECT * FROM t LIMIT 10;
  10
 (10 rows)
 
+-- Test behavior with an unknown-type literal in the WITH
+WITH q AS (SELECT 'foo' AS x)
+SELECT x, x IS OF (unknown) as is_unknown FROM q;
+  x  | is_unknown 
+-----+------------
+ foo | t
+(1 row)
+
+WITH RECURSIVE t(n) AS (
+    SELECT 'foo'
+UNION ALL
+    SELECT n || ' bar' FROM t WHERE length(n) < 20
+)
+SELECT n, n IS OF (text) as is_text FROM t;
+            n            | is_text 
+-------------------------+---------
+ foo                     | t
+ foo bar                 | t
+ foo bar bar             | t
+ foo bar bar bar         | t
+ foo bar bar bar bar     | t
+ foo bar bar bar bar bar | t
+(6 rows)
+
 --
 -- Some examples with a tree
 --
index cd8831f4b15654df48a914db44acea817f61bcb0..c3ff5e285a71747b0658e7f5dc161d1b9632d678 100644 (file)
@@ -38,6 +38,17 @@ UNION ALL
     SELECT n+1 FROM t)
 SELECT * FROM t LIMIT 10;
 
+-- Test behavior with an unknown-type literal in the WITH
+WITH q AS (SELECT 'foo' AS x)
+SELECT x, x IS OF (unknown) as is_unknown FROM q;
+
+WITH RECURSIVE t(n) AS (
+    SELECT 'foo'
+UNION ALL
+    SELECT n || ' bar' FROM t WHERE length(n) < 20
+)
+SELECT n, n IS OF (text) as is_text FROM t;
+
 --
 -- Some examples with a tree
 --