@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / parser / parse_relation . c , v 1.136 2008 / 10 / 04 21 : 56 : 54 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / parser / parse_relation . c , v 1.137 2008 / 10 / 06 02 : 12 : 56 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -182,6 +182,38 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
return result ;
}
/*
* Search the query ' s CTE namespace for a CTE matching the given unqualified
* refname . Return the CTE ( and its levelsup count ) if a match , or NULL
* if no match . We need not worry about multiple matches , since parse_cte . c
* rejects WITH lists containing duplicate CTE names .
*/
CommonTableExpr *
scanNameSpaceForCTE ( ParseState * pstate , const char * refname ,
Index * ctelevelsup )
{
Index levelsup ;
for ( levelsup = 0 ;
pstate ! = NULL ;
pstate = pstate - > parentParseState , levelsup + + )
{
ListCell * lc ;
foreach ( lc , pstate - > p_ctenamespace )
{
CommonTableExpr * cte = ( CommonTableExpr * ) lfirst ( lc ) ;
if ( strcmp ( cte - > ctename , refname ) = = 0 )
{
* ctelevelsup = levelsup ;
return cte ;
}
}
}
return NULL ;
}
/*
* searchRangeTable
* See if any RangeTblEntry could possibly match the RangeVar .
@ -194,16 +226,32 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
* valid matches , but only one will be returned ) . This must be used ONLY
* as a heuristic in giving suitable error messages . See warnAutoRange .
*
* Notice that we consider both matches on actual relation name and matches
* on alias .
* Notice that we consider both matches on actual relation ( or CTE ) name
* and matches on alias .
*/
static RangeTblEntry *
searchRangeTable ( ParseState * pstate , RangeVar * relation )
{
Oid relId = RangeVarGetRelid ( relation , true ) ;
char * refname = relation - > relname ;
const char * refname = relation - > relname ;
Oid relId = InvalidOid ;
CommonTableExpr * cte = NULL ;
Index ctelevelsup = 0 ;
Index levelsup ;
while ( pstate ! = NULL )
/*
* If it ' s an unqualified name , check for possible CTE matches .
* A CTE hides any real relation matches . If no CTE , look for
* a matching relation .
*/
if ( ! relation - > schemaname )
cte = scanNameSpaceForCTE ( pstate , refname , & ctelevelsup ) ;
if ( ! cte )
relId = RangeVarGetRelid ( relation , true ) ;
/* Now look for RTEs matching either the relation/CTE or the alias */
for ( levelsup = 0 ;
pstate ! = NULL ;
pstate = pstate - > parentParseState , levelsup + + )
{
ListCell * l ;
@ -211,15 +259,18 @@ searchRangeTable(ParseState *pstate, RangeVar *relation)
{
RangeTblEntry * rte = ( RangeTblEntry * ) lfirst ( l ) ;
if ( OidIsValid ( relId ) & &
rte - > rtekind = = RTE_RELATION & &
if ( rte - > rtekind = = RTE_RELATION & &
OidIsValid ( relId ) & &
rte - > relid = = relId )
return rte ;
if ( rte - > rtekind = = RTE_CTE & &
cte ! = NULL & &
rte - > ctelevelsup + levelsup = = ctelevelsup & &
strcmp ( rte - > ctename , refname ) = = 0 )
return rte ;
if ( strcmp ( rte - > eref - > aliasname , refname ) = = 0 )
return rte ;
}
pstate = pstate - > parentParseState ;
}
return NULL ;
}
@ -1293,18 +1344,27 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
RangeTblEntry *
addImplicitRTE ( ParseState * pstate , RangeVar * relation )
{
CommonTableExpr * cte = NULL ;
Index levelsup = 0 ;
RangeTblEntry * rte ;
/* issue warning or error as needed */
warnAutoRange ( pstate , relation ) ;
/* if it is an unqualified name, it might be a CTE reference */
if ( ! relation - > schemaname )
cte = scanNameSpaceForCTE ( pstate , relation - > relname , & levelsup ) ;
/*
* Note that we set inFromCl true , so that the RTE will be listed
* explicitly if the parsetree is ever decompiled by ruleutils . c . This
* provides a migration path for views / rules that were originally written
* with implicit - RTE syntax .
*/
rte = addRangeTableEntry ( pstate , relation , NULL , false , true ) ;
if ( cte )
rte = addRangeTableEntryForCTE ( pstate , cte , levelsup , NULL , true ) ;
else
rte = addRangeTableEntry ( pstate , relation , NULL , false , true ) ;
/* Add to joinlist and relnamespace, but not varnamespace */
addRTEtoQuery ( pstate , rte , true , true , false ) ;
@ -2194,8 +2254,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
if ( rte )
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_TABLE ) ,
errmsg ( " invalid reference to FROM-clause entry for table \" %s \" " ,
relation - > relname ) ,
errmsg ( " invalid reference to FROM-clause entry for table \" %s \" " ,
relation - > relname ) ,
( badAlias ?
errhint ( " Perhaps you meant to reference the table alias \" %s \" . " ,
badAlias ) :
@ -2205,11 +2265,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
else
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_TABLE ) ,
( pstate - > parentParseState ?
errmsg ( " missing FROM-clause entry in subquery for table \" %s \" " ,
relation - > relname ) :
errmsg ( " missing FROM-clause entry for table \" %s \" " ,
relation - > relname ) ) ,
errmsg ( " missing FROM-clause entry for table \" %s \" " ,
relation - > relname ) ,
parser_errposition ( pstate , relation - > location ) ) ) ;
}
else
@ -2217,11 +2274,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
/* just issue a warning */
ereport ( NOTICE ,
( errcode ( ERRCODE_UNDEFINED_TABLE ) ,
( pstate - > parentParseState ?
errmsg ( " adding missing FROM-clause entry in subquery for table \" %s \" " ,
relation - > relname ) :
errmsg ( " adding missing FROM-clause entry for table \" %s \" " ,
relation - > relname ) ) ,
errmsg ( " adding missing FROM-clause entry for table \" %s \" " ,
relation - > relname ) ,
( badAlias ?
errhint ( " Perhaps you meant to reference the table alias \" %s \" . " ,
badAlias ) :