@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / parser / parse_clause . c , v 1.190 2009 / 07 / 16 06 : 33 : 43 petere Exp $
* $ PostgreSQL : pgsql / src / backend / parser / parse_clause . c , v 1.191 2009 / 08 / 27 20 : 08 : 02 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -37,16 +37,15 @@
# include "utils/rel.h"
/* clause types for findTargetlistEntrySQL92 */
# define ORDER_CLAUSE 0
# define GROUP_CLAUSE 1
# define DISTINCT_ON_CLAUSE 2
# define PARTITION_CLAUSE 3
static const char * const clauseText [ ] = {
" ORDER BY " ,
" GROUP BY " ,
" DISTINCT ON " ,
" PARTITION BY "
" DISTINCT ON "
} ;
static void extractRemainingColumns ( List * common_colnames ,
@ -73,8 +72,10 @@ static Node *transformFromClauseItem(ParseState *pstate, Node *n,
Relids * containedRels ) ;
static Node * buildMergedJoinVar ( ParseState * pstate , JoinType jointype ,
Var * l_colvar , Var * r_colvar ) ;
static TargetEntry * findTargetlistEntry ( ParseState * pstate , Node * node ,
List * * tlist , int clause ) ;
static TargetEntry * findTargetlistEntrySQL92 ( ParseState * pstate , Node * node ,
List * * tlist , int clause ) ;
static TargetEntry * findTargetlistEntrySQL99 ( ParseState * pstate , Node * node ,
List * * tlist ) ;
static int get_matching_location ( int sortgroupref ,
List * sortgrouprefs , List * exprs ) ;
static List * addTargetToSortList ( ParseState * pstate , TargetEntry * tle ,
@ -1216,21 +1217,27 @@ transformLimitClause(ParseState *pstate, Node *clause,
/*
* findTargetlistEntry -
* findTargetlistEntrySQL92 -
* Returns the targetlist entry matching the given ( untransformed ) node .
* If no matching entry exists , one is created and appended to the target
* list as a " resjunk " node .
*
* This function supports the old SQL92 ORDER BY interpretation , where the
* expression is an output column name or number . If we fail to find a
* match of that sort , we fall through to the SQL99 rules . For historical
* reasons , Postgres also allows this interpretation for GROUP BY , though
* the standard never did . However , for GROUP BY we prefer a SQL99 match .
* This function is * not * used for WINDOW definitions .
*
* node the ORDER BY , GROUP BY , or DISTINCT ON expression to be matched
* tlist the target list ( passed by reference so we can append to it )
* clause identifies clause type being processed
*/
static TargetEntry *
findTargetlistEntry ( ParseState * pstate , Node * node , List * * tlist , int clause )
findTargetlistEntrySQL92 ( ParseState * pstate , Node * node , List * * tlist ,
int clause )
{
TargetEntry * target_result = NULL ;
ListCell * tl ;
Node * expr ;
/*----------
* Handle two special cases as mandated by the SQL92 spec :
@ -1258,8 +1265,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
* 2. IntegerConstant
* This means to use the n ' th item in the existing target list .
* Note that it would make no sense to order / group / distinct by an
* actual constant , so this does not create a conflict with our
* extension to order / group by an expression .
* actual constant , so this does not create a conflict with SQL99 .
* GROUP BY column - number is not allowed by SQL92 , but since
* the standard has no other behavior defined for this syntax ,
* we may as well accept this common extension .
@ -1268,7 +1274,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
* since the user didn ' t write them in his SELECT list .
*
* If neither special case applies , fall through to treat the item as
* an expression .
* an expression per SQL99 .
* - - - - - - - - - -
*/
if ( IsA ( node , ColumnRef ) & &
@ -1278,15 +1284,15 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
char * name = strVal ( linitial ( ( ( ColumnRef * ) node ) - > fields ) ) ;
int location = ( ( ColumnRef * ) node ) - > location ;
if ( clause = = GROUP_CLAUSE | | clause = = PARTITION_CLAUSE )
if ( clause = = GROUP_CLAUSE )
{
/*
* In GROUP BY , we must prefer a match against a FROM - clause
* column to one against the targetlist . Look to see if there is
* a matching column . If so , fall through to let transformExpr ( )
* do the rest . NOTE : if name could refer ambiguously to more
* than one column name exposed by FROM , colNameToVar will
* ereport ( ERROR ) . That ' s just what we want here .
* a matching column . If so , fall through to use SQL99 rules .
* NOTE : if name could refer ambiguously to more than one column
* name exposed by FROM , colNameToVar will ereport ( ERROR ) . That ' s
* just what we want here .
*
* Small tweak for 7.4 .3 : ignore matches in upper query levels .
* This effectively changes the search order for bare names to ( 1 )
@ -1295,8 +1301,6 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
* SQL99 do not allow GROUPing BY an outer reference , so this
* breaks no cases that are legal per spec , and it seems a more
* self - consistent behavior .
*
* Window PARTITION BY clauses should act exactly like GROUP BY .
*/
if ( colNameToVar ( pstate , name , true , location ) ! = NULL )
name = NULL ;
@ -1304,6 +1308,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
if ( name ! = NULL )
{
TargetEntry * target_result = NULL ;
foreach ( tl , * tlist )
{
TargetEntry * tle = ( TargetEntry * ) lfirst ( tl ) ;
@ -1367,12 +1373,36 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
}
/*
* Otherwise , we have an expression ( this is a Postgres extension not
* found in SQL92 ) . Convert the untransformed node to a transformed
* expression , and search for a match in the tlist . NOTE : it doesn ' t
* really matter whether there is more than one match . Also , we are
* willing to match a resjunk target here , though the above cases must
* ignore resjunk targets .
* Otherwise , we have an expression , so process it per SQL99 rules .
*/
return findTargetlistEntrySQL99 ( pstate , node , tlist ) ;
}
/*
* findTargetlistEntrySQL99 -
* Returns the targetlist entry matching the given ( untransformed ) node .
* If no matching entry exists , one is created and appended to the target
* list as a " resjunk " node .
*
* This function supports the SQL99 interpretation , wherein the expression
* is just an ordinary expression referencing input column names .
*
* node the ORDER BY , GROUP BY , etc expression to be matched
* tlist the target list ( passed by reference so we can append to it )
*/
static TargetEntry *
findTargetlistEntrySQL99 ( ParseState * pstate , Node * node , List * * tlist )
{
TargetEntry * target_result ;
ListCell * tl ;
Node * expr ;
/*
* Convert the untransformed node to a transformed expression , and search
* for a match in the tlist . NOTE : it doesn ' t really matter whether there
* is more than one match . Also , we are willing to match an existing
* resjunk target here , though the SQL92 cases above must ignore resjunk
* targets .
*/
expr = transformExpr ( pstate , node ) ;
@ -1403,16 +1433,15 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
* GROUP BY items will be added to the targetlist ( as resjunk columns )
* if not already present , so the targetlist must be passed by reference .
*
* This is also used for window PARTITION BY clauses ( which actually act
* just the same , except for the clause name used in error messag es) .
* This is also used for window PARTITION BY clauses ( which act almost the
* same , but are always interpreted per SQL99 rul es) .
*/
List *
transformGroupClause ( ParseState * pstate , List * grouplist ,
List * * targetlist , List * sortClause ,
bool isPartition )
bool isWindowFunc )
{
List * result = NIL ;
int clause = isPartition ? PARTITION_CLAUSE : GROUP_CLAUSE ;
ListCell * gl ;
foreach ( gl , grouplist )
@ -1421,7 +1450,11 @@ transformGroupClause(ParseState *pstate, List *grouplist,
TargetEntry * tle ;
bool found = false ;
tle = findTargetlistEntry ( pstate , gexpr , targetlist , clause ) ;
if ( isWindowFunc )
tle = findTargetlistEntrySQL99 ( pstate , gexpr , targetlist ) ;
else
tle = findTargetlistEntrySQL92 ( pstate , gexpr , targetlist ,
GROUP_CLAUSE ) ;
/* Eliminate duplicates (GROUP BY x, x) */
if ( targetIsInSortList ( tle , InvalidOid , result ) )
@ -1475,12 +1508,16 @@ transformGroupClause(ParseState *pstate, List *grouplist,
*
* ORDER BY items will be added to the targetlist ( as resjunk columns )
* if not already present , so the targetlist must be passed by reference .
*
* This is also used for window ORDER BY clauses ( which act almost the
* same , but are always interpreted per SQL99 rules ) .
*/
List *
transformSortClause ( ParseState * pstate ,
List * orderlist ,
List * * targetlist ,
bool resolveUnknown )
bool resolveUnknown ,
bool isWindowFunc )
{
List * sortlist = NIL ;
ListCell * olitem ;
@ -1490,8 +1527,11 @@ transformSortClause(ParseState *pstate,
SortBy * sortby = ( SortBy * ) lfirst ( olitem ) ;
TargetEntry * tle ;
tle = findTargetlistEntry ( pstate , sortby - > node ,
targetlist , ORDER_CLAUSE ) ;
if ( isWindowFunc )
tle = findTargetlistEntrySQL99 ( pstate , sortby - > node , targetlist ) ;
else
tle = findTargetlistEntrySQL92 ( pstate , sortby - > node , targetlist ,
ORDER_CLAUSE ) ;
sortlist = addTargetToSortList ( pstate , tle ,
sortlist , * targetlist , sortby ,
@ -1550,18 +1590,19 @@ transformWindowDefinitions(ParseState *pstate,
/*
* Transform PARTITION and ORDER specs , if any . These are treated
* exactly like top - level GROUP BY and ORDER BY clauses , including the
* special handling of nondefault operator semantics .
* almost exactly like top - level GROUP BY and ORDER BY clauses ,
* including the special handling of nondefault operator semantics .
*/
orderClause = transformSortClause ( pstate ,
windef - > orderClause ,
targetlist ,
true ) ;
true /* fix unknowns */ ,
true /* window function */ ) ;
partitionClause = transformGroupClause ( pstate ,
windef - > partitionClause ,
targetlist ,
orderClause ,
true ) ;
true /* window function */ ) ;
/*
* And prepare the new WindowClause .
@ -1736,8 +1777,8 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist,
int sortgroupref ;
TargetEntry * tle ;
tle = findTargetlistEntry ( pstate , dexpr ,
targetlist , DISTINCT_ON_CLAUSE ) ;
tle = findTargetlistEntrySQL92 ( pstate , dexpr , targetlist ,
DISTINCT_ON_CLAUSE ) ;
sortgroupref = assignSortGroupRef ( tle , * targetlist ) ;
sortgrouprefs = lappend_int ( sortgrouprefs , sortgroupref ) ;
}