@ -15,7 +15,7 @@
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
* IDENTIFICATION
* $ Header : / cvsroot / pgsql / src / backend / optimizer / prep / preptlist . c , v 1.51 2002 / 04 / 02 08 : 51 : 51 inoue Exp $
* $ Header : / cvsroot / pgsql / src / backend / optimizer / prep / preptlist . c , v 1.52 2002 / 04 / 05 05 : 47 : 05 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -27,19 +27,10 @@
# include "nodes/makefuncs.h"
# include "optimizer/prep.h"
# include "parser/parsetree.h"
# include "parser/parse_coerce.h"
# include "parser/parse_expr.h"
# include "parser/parse_target.h"
# include "utils/builtins.h"
# include "utils/lsyscache.h"
static List * expand_targetlist ( List * tlist , int command_type ,
Index result_relation , List * range_table ) ;
static TargetEntry * process_matched_tle ( TargetEntry * src_tle ,
TargetEntry * prior_tle ,
int attrno ) ;
static Node * build_column_default ( Relation rel , int attrno ) ;
/*
@ -119,31 +110,25 @@ preprocess_targetlist(List *tlist,
/*
* expand_targetlist
* Given a target list as generated by the parser and a result relation ,
* add targetlist entries for any missing attributes , and order the
* non - junk attributes in proper field order .
* add targetlist entries for any missing attributes , and ensure the
* non - junk attributes appear in proper field order .
*
* NOTE : if you are tempted to put more processing here , consider whether
* it shouldn ' t go in the rewriter ' s rewriteTargetList ( ) instead .
*/
static List *
expand_targetlist ( List * tlist , int command_type ,
Index result_relation , List * range_table )
{
int old_tlist_len = length ( tlist ) ;
List * new_tlist = NIL ;
bool * tlistentry_used ;
Relation rel ;
int attrno ,
numattrs ,
old_tlist_index ;
List * temp ;
numattrs ;
/*
* Keep a map of which tlist items we have transferred to new list .
* The rewriter should have already ensured that the TLEs are in
* correct order ; but we have to insert TLEs for any missing attributes .
*
* + 1 here just keeps palloc from complaining if old_tlist_len = = 0.
*/
tlistentry_used = ( bool * ) palloc ( ( old_tlist_len + 1 ) * sizeof ( bool ) ) ;
memset ( tlistentry_used , 0 , ( old_tlist_len + 1 ) * sizeof ( bool ) ) ;
/*
* Scan the tuple description in the relation ' s relcache entry to make
* sure we have all the user attributes in the right order .
*/
@ -154,28 +139,20 @@ expand_targetlist(List *tlist, int command_type,
for ( attrno = 1 ; attrno < = numattrs ; attrno + + )
{
Form_pg_attribute att_tup = rel - > rd_att - > attrs [ attrno - 1 ] ;
char * attrname = NameStr ( att_tup - > attname ) ;
TargetEntry * new_tle = NULL ;
/*
* We match targetlist entries to attributes using the resname .
* Junk attributes are not candidates to be matched .
*/
old_tlist_index = 0 ;
foreach ( temp , tlist )
if ( tlist ! = NIL )
{
TargetEntry * old_tle = ( TargetEntry * ) lfirst ( temp ) ;
TargetEntry * old_tle = ( TargetEntry * ) lfirst ( tlist ) ;
Resdom * resdom = old_tle - > resdom ;
if ( ! tlistentry_used [ old_tlist_index ] & &
! resdom - > resjunk & &
strcmp ( resdom - > resname , attrname ) = = 0 )
if ( ! resdom - > resjunk & & resdom - > resno = = attrno )
{
new_tle = process_matched_tle ( old_tle , new_tle , attrno ) ;
tlistentry_used [ old_tlist_index ] = true ;
/* keep scanning to detect multiple assignments to attr */
Assert ( strcmp ( resdom - > resname ,
NameStr ( att_tup - > attname ) ) = = 0 ) ;
new_tle = old_tle ;
tlist = lnext ( tlist ) ;
}
old_tlist_index + + ;
}
if ( new_tle = = NULL )
@ -183,7 +160,8 @@ expand_targetlist(List *tlist, int command_type,
/*
* Didn ' t find a matching tlist entry , so make one .
*
* For INSERT , generate an appropriate default value .
* For INSERT , generate a NULL constant . ( We assume the
* rewriter would have inserted any available default value . )
*
* For UPDATE , generate a Var reference to the existing value of
* the attribute , so that it gets copied to the new tuple .
@ -195,14 +173,20 @@ expand_targetlist(List *tlist, int command_type,
switch ( command_type )
{
case CMD_INSERT :
new_expr = build_column_default ( rel , attrno ) ;
new_expr = ( Node * ) makeConst ( atttype ,
att_tup - > attlen ,
( Datum ) 0 ,
true , /* isnull */
att_tup - > attbyval ,
false , /* not a set */
false ) ;
break ;
case CMD_UPDATE :
new_expr = ( Node * ) makeVar ( result_relation ,
attrno ,
atttype ,
atttypmod ,
0 ) ;
new_expr = ( Node * ) makeVar ( result_relation ,
attrno ,
atttype ,
atttypmod ,
0 ) ;
break ;
default :
elog ( ERROR , " expand_targetlist: unexpected command_type " ) ;
@ -213,7 +197,7 @@ expand_targetlist(List *tlist, int command_type,
new_tle = makeTargetEntry ( makeResdom ( attrno ,
atttype ,
atttypmod ,
pstrdup ( attrname ) ,
pstrdup ( NameStr ( att_tup - > attname ) ) ,
false ) ,
new_expr ) ;
}
@ -222,230 +206,32 @@ expand_targetlist(List *tlist, int command_type,
}
/*
* Copy all unprocessed tlist entries to the end of the new tlist ,
* making sure they are marked resjunk = true . Typical junk entries
* include ORDER BY or GROUP BY expressions ( are these actually
* possible in an INSERT or UPDATE ? ) , system attribute references ,
* etc .
* The remaining tlist entries should be resjunk ; append them all to
* the end of the new tlist , making sure they have resnos higher than
* the last real attribute . ( Note : although the rewriter already did
* such renumbering , we have to do it again here in case we are doing
* an UPDATE in an inh eri tan ce child table with more columns . )
*/
old_tlist_index = 0 ;
foreach ( temp , tlist )
while ( tlist )
{
TargetEntry * old_tle = ( TargetEntry * ) lfirst ( temp ) ;
TargetEntry * old_tle = ( TargetEntry * ) lfirst ( tlist ) ;
Resdom * resdom = old_tle - > resdom ;
if ( ! tlistentry_used [ old_tlist_index ] )
if ( ! resdom - > resjunk )
elog ( ERROR , " expand_targetlist: targetlist is not sorted correctly " ) ;
/* Get the resno right, but don't copy unnecessarily */
if ( resdom - > resno ! = attrno )
{
Resdom * resdom = old_tle - > resdom ;
if ( ! resdom - > resjunk )
elog ( ERROR , " Unexpected assignment to attribute \" %s \" " ,
resdom - > resname ) ;
/* Get the resno right, but don't copy unnecessarily */
if ( resdom - > resno ! = attrno )
{
resdom = ( Resdom * ) copyObject ( ( Node * ) resdom ) ;
resdom - > resno = attrno ;
old_tle = makeTargetEntry ( resdom , old_tle - > expr ) ;
}
new_tlist = lappend ( new_tlist , old_tle ) ;
attrno + + ;
resdom = ( Resdom * ) copyObject ( ( Node * ) resdom ) ;
resdom - > resno = attrno ;
old_tle = makeTargetEntry ( resdom , old_tle - > expr ) ;
}
old_tlist_index + + ;
new_tlist = lappend ( new_tlist , old_tle ) ;
attrno + + ;
tlist = lnext ( tlist ) ;
}
heap_close ( rel , AccessShareLock ) ;
pfree ( tlistentry_used ) ;
return new_tlist ;
}
/*
* Convert a matched TLE from the original tlist into a correct new TLE .
*
* This routine checks for multiple assignments to the same target attribute ,
* such as " UPDATE table SET foo = 42, foo = 43 " . This is OK only if they
* are array assignments , ie , " UPDATE table SET foo[2] = 42, foo[4] = 43 " .
* If so , we need to merge the operations into a single assignment op .
* Essentially , the expression we want to produce in this case is like
* foo = array_set ( array_set ( foo , 2 , 42 ) , 4 , 43 )
*/
static TargetEntry *
process_matched_tle ( TargetEntry * src_tle ,
TargetEntry * prior_tle ,
int attrno )
{
Resdom * resdom = src_tle - > resdom ;
Node * priorbottom ;
ArrayRef * newexpr ;
if ( prior_tle = = NULL )
{
/*
* Normal case where this is the first assignment to the
* attribute .
*
* We can recycle the old TLE + resdom if right resno ; else make a new
* one to avoid modifying the old tlist structure . ( Is preserving
* old tlist actually necessary ? Not sure , be safe . )
*/
if ( resdom - > resno = = attrno )
return src_tle ;
resdom = ( Resdom * ) copyObject ( ( Node * ) resdom ) ;
resdom - > resno = attrno ;
return makeTargetEntry ( resdom , src_tle - > expr ) ;
}
/*
* Multiple assignments to same attribute . Allow only if all are
* array - assign operators with same bottom array object .
*/
if ( src_tle - > expr = = NULL | | ! IsA ( src_tle - > expr , ArrayRef ) | |
( ( ArrayRef * ) src_tle - > expr ) - > refassgnexpr = = NULL | |
prior_tle - > expr = = NULL | | ! IsA ( prior_tle - > expr , ArrayRef ) | |
( ( ArrayRef * ) prior_tle - > expr ) - > refassgnexpr = = NULL | |
( ( ArrayRef * ) src_tle - > expr ) - > refelemtype ! =
( ( ArrayRef * ) prior_tle - > expr ) - > refelemtype )
elog ( ERROR , " Multiple assignments to same attribute \" %s \" " ,
resdom - > resname ) ;
/*
* Prior TLE could be a nest of ArrayRefs if we do this more than
* once .
*/
priorbottom = ( ( ArrayRef * ) prior_tle - > expr ) - > refexpr ;
while ( priorbottom ! = NULL & & IsA ( priorbottom , ArrayRef ) & &
( ( ArrayRef * ) priorbottom ) - > refassgnexpr ! = NULL )
priorbottom = ( ( ArrayRef * ) priorbottom ) - > refexpr ;
if ( ! equal ( priorbottom , ( ( ArrayRef * ) src_tle - > expr ) - > refexpr ) )
elog ( ERROR , " Multiple assignments to same attribute \" %s \" " ,
resdom - > resname ) ;
/*
* Looks OK to nest ' em .
*/
newexpr = makeNode ( ArrayRef ) ;
memcpy ( newexpr , src_tle - > expr , sizeof ( ArrayRef ) ) ;
newexpr - > refexpr = prior_tle - > expr ;
resdom = ( Resdom * ) copyObject ( ( Node * ) resdom ) ;
resdom - > resno = attrno ;
return makeTargetEntry ( resdom , ( Node * ) newexpr ) ;
}
/*
* Make an expression tree for the default value for a column .
*
* This is used to fill in missing attributes in an INSERT targetlist .
* We look first to see if the column has a default value expression .
* If not , generate a constant of the default value for the attribute type ,
* or a NULL if the type has no default value either .
*/
static Node *
build_column_default ( Relation rel , int attrno )
{
TupleDesc rd_att = rel - > rd_att ;
Form_pg_attribute att_tup = rd_att - > attrs [ attrno - 1 ] ;
Oid atttype = att_tup - > atttypid ;
int32 atttypmod = att_tup - > atttypmod ;
int16 typlen = att_tup - > attlen ;
bool typbyval = att_tup - > attbyval ;
Node * expr = NULL ;
/*
* Scan to see if relation has a default for this column .
*/
if ( rd_att - > constr & & rd_att - > constr - > num_defval > 0 )
{
AttrDefault * defval = rd_att - > constr - > defval ;
int ndef = rd_att - > constr - > num_defval ;
while ( - - ndef > = 0 )
{
if ( attrno = = defval [ ndef ] . adnum )
{
/*
* Found it , convert string representation to node tree .
*/
expr = stringToNode ( defval [ ndef ] . adbin ) ;
break ;
}
}
}
if ( expr = = NULL )
{
/*
* No per - column default , so look for a default for the type itself .
*/
if ( att_tup - > attisset )
{
/*
* Set attributes are represented as OIDs no matter what the set
* element type is , and the element type ' s default is irrelevant
* too .
*/
typlen = sizeof ( Oid ) ;
typbyval = true ;
}
else
{
expr = get_typdefault ( atttype ) ;
}
}
if ( expr = = NULL )
{
/*
* No default anywhere , so generate a NULL constant .
*/
expr = ( Node * ) makeConst ( atttype ,
typlen ,
( Datum ) 0 ,
true , /* isnull */
typbyval ,
false , /* not a set */
false ) ;
}
else
{
Oid exprtype ;
/*
* Make sure the value is coerced to the target column
* type ( might not be right type yet if it ' s not a
* constant ! ) This should match the parser ' s processing of
* non - defaulted expressions - - - see
* updateTargetListEntry ( ) .
*/
exprtype = exprType ( expr ) ;
if ( exprtype ! = atttype )
{
expr = CoerceTargetExpr ( NULL , expr , exprtype ,
atttype , atttypmod ) ;
/*
* This really shouldn ' t fail ; should have checked the
* default ' s type when it was created . . .
*/
if ( expr = = NULL )
elog ( ERROR , " Column \" %s \" is of type %s "
" but default expression is of type %s "
" \n \t You will need to rewrite or cast the expression " ,
NameStr ( att_tup - > attname ) ,
format_type_be ( atttype ) ,
format_type_be ( exprtype ) ) ;
}
/*
* If the column is a fixed - length type , it may need a
* length coercion as well as a type coercion .
*/
expr = coerce_type_typmod ( NULL , expr , atttype , atttypmod ) ;
}
return expr ;
}