66 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
77 * Portions Copyright (c) 1994, Regents of the University of California
88 *
9- *$PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.343 2006/08/02 14:14:22 tgl Exp $
9+ *$PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.344 2006/08/10 02:36:29 tgl Exp $
1010 *
1111 *-------------------------------------------------------------------------
1212 */
@@ -131,7 +131,8 @@ static void transformFKConstraints(ParseState *pstate,
131131bool skipValidation ,
132132bool isAddConstraint );
133133static void applyColumnNames (List * dst ,List * src );
134- static List * getSetColTypes (ParseState * pstate ,Node * node );
134+ static void getSetColTypes (ParseState * pstate ,Node * node ,
135+ List * * colTypes ,List * * colTypmods );
135136static void transformLockingClause (Query * qry ,LockingClause * lc );
136137static void transformConstraintAttrs (List * constraintList );
137138static void transformColumnType (ParseState * pstate ,ColumnDef * column );
@@ -2312,7 +2313,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
23122313List * lockingClause ;
23132314Node * node ;
23142315ListCell * left_tlist ,
2315- * dtlist ,
2316+ * lct ,
2317+ * lcm ,
23162318* l ;
23172319List * targetvars ,
23182320* targetnames ,
@@ -2395,9 +2397,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
23952397targetnames = NIL ;
23962398left_tlist = list_head (leftmostQuery -> targetList );
23972399
2398- foreach ( dtlist ,sostmt -> colTypes )
2400+ forboth ( lct ,sostmt -> colTypes , lcm , sostmt -> colTypmods )
23992401{
2400- Oid colType = lfirst_oid (dtlist );
2402+ Oid colType = lfirst_oid (lct );
2403+ int32 colTypmod = lfirst_int (lcm );
24012404TargetEntry * lefttle = (TargetEntry * )lfirst (left_tlist );
24022405char * colName ;
24032406TargetEntry * tle ;
@@ -2408,7 +2411,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
24082411expr = (Expr * )makeVar (leftmostRTI ,
24092412lefttle -> resno ,
24102413colType ,
2411- -1 ,
2414+ colTypmod ,
241224150 );
24132416tle = makeTargetEntry (expr ,
24142417 (AttrNumber )pstate -> p_next_resno ++ ,
@@ -2609,8 +2612,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
26092612SetOperationStmt * op = makeNode (SetOperationStmt );
26102613List * lcoltypes ;
26112614List * rcoltypes ;
2612- ListCell * l ;
2613- ListCell * r ;
2615+ List * lcoltypmods ;
2616+ List * rcoltypmods ;
2617+ ListCell * lct ;
2618+ ListCell * rct ;
2619+ ListCell * lcm ;
2620+ ListCell * rcm ;
26142621const char * context ;
26152622
26162623context = (stmt -> op == SETOP_UNION ?"UNION" :
@@ -2630,24 +2637,43 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
26302637 * Verify that the two children have the same number of non-junk
26312638 * columns, and determine the types of the merged output columns.
26322639 */
2633- lcoltypes = getSetColTypes (pstate ,op -> larg );
2634- rcoltypes = getSetColTypes (pstate ,op -> rarg );
2640+ getSetColTypes (pstate ,op -> larg , & lcoltypes , & lcoltypmods );
2641+ getSetColTypes (pstate ,op -> rarg , & rcoltypes , & rcoltypmods );
26352642if (list_length (lcoltypes )!= list_length (rcoltypes ))
26362643ereport (ERROR ,
26372644(errcode (ERRCODE_SYNTAX_ERROR ),
26382645errmsg ("each %s query must have the same number of columns" ,
26392646context )));
2647+ Assert (list_length (lcoltypes )== list_length (lcoltypmods ));
2648+ Assert (list_length (rcoltypes )== list_length (rcoltypmods ));
26402649
26412650op -> colTypes = NIL ;
2642- forboth (l ,lcoltypes ,r ,rcoltypes )
2651+ op -> colTypmods = NIL ;
2652+ /* don't have a "foreach4", so chase two of the lists by hand */
2653+ lcm = list_head (lcoltypmods );
2654+ rcm = list_head (rcoltypmods );
2655+ forboth (lct ,lcoltypes ,rct ,rcoltypes )
26432656{
2644- Oid lcoltype = lfirst_oid (l );
2645- Oid rcoltype = lfirst_oid (r );
2657+ Oid lcoltype = lfirst_oid (lct );
2658+ Oid rcoltype = lfirst_oid (rct );
2659+ int32 lcoltypmod = lfirst_int (lcm );
2660+ int32 rcoltypmod = lfirst_int (rcm );
26462661Oid rescoltype ;
2662+ int32 rescoltypmod ;
26472663
2664+ /* select common type, same as CASE et al */
26482665rescoltype = select_common_type (list_make2_oid (lcoltype ,rcoltype ),
26492666context );
2667+ /* if same type and same typmod, use typmod; else default */
2668+ if (lcoltype == rcoltype && lcoltypmod == rcoltypmod )
2669+ rescoltypmod = lcoltypmod ;
2670+ else
2671+ rescoltypmod = -1 ;
26502672op -> colTypes = lappend_oid (op -> colTypes ,rescoltype );
2673+ op -> colTypmods = lappend_int (op -> colTypmods ,rescoltypmod );
2674+
2675+ lcm = lnext (lcm );
2676+ rcm = lnext (rcm );
26512677}
26522678
26532679return (Node * )op ;
@@ -2656,17 +2682,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
26562682
26572683/*
26582684 * getSetColTypes
2659- * Get output column types of an (already transformed) set-op node
2685+ * Get output column types/typmods of an (already transformed) set-op node
26602686 */
2661- static List *
2662- getSetColTypes (ParseState * pstate ,Node * node )
2687+ static void
2688+ getSetColTypes (ParseState * pstate ,Node * node ,
2689+ List * * colTypes ,List * * colTypmods )
26632690{
2691+ * colTypes = NIL ;
2692+ * colTypmods = NIL ;
26642693if (IsA (node ,RangeTblRef ))
26652694{
26662695RangeTblRef * rtr = (RangeTblRef * )node ;
26672696RangeTblEntry * rte = rt_fetch (rtr -> rtindex ,pstate -> p_rtable );
26682697Query * selectQuery = rte -> subquery ;
2669- List * result = NIL ;
26702698ListCell * tl ;
26712699
26722700Assert (selectQuery != NULL );
@@ -2677,23 +2705,23 @@ getSetColTypes(ParseState *pstate, Node *node)
26772705
26782706if (tle -> resjunk )
26792707continue ;
2680- result = lappend_oid (result ,exprType ((Node * )tle -> expr ));
2708+ * colTypes = lappend_oid (* colTypes ,
2709+ exprType ((Node * )tle -> expr ));
2710+ * colTypmods = lappend_int (* colTypmods ,
2711+ exprTypmod ((Node * )tle -> expr ));
26812712}
2682- return result ;
26832713}
26842714else if (IsA (node ,SetOperationStmt ))
26852715{
26862716SetOperationStmt * op = (SetOperationStmt * )node ;
26872717
26882718/* Result already computed during transformation of node */
26892719Assert (op -> colTypes != NIL );
2690- return op -> colTypes ;
2720+ * colTypes = op -> colTypes ;
2721+ * colTypmods = op -> colTypmods ;
26912722}
26922723else
2693- {
26942724elog (ERROR ,"unrecognized node type: %d" , (int )nodeTag (node ));
2695- return NIL ;/* keep compiler quiet */
2696- }
26972725}
26982726
26992727/* Attach column names from a ColumnDef list to a TargetEntry list */