@ -39,53 +39,8 @@
# include "utils/xml.h"
/* GUC parameters */
bool operator_precedence_warning = false ;
bool Transform_null_equals = false ;
/*
* Node - type groups for operator precedence warnings
* We use zero for everything not otherwise classified
*/
# define PREC_GROUP_POSTFIX_IS 1 /* postfix IS tests (NullTest, etc) */
# define PREC_GROUP_INFIX_IS 2 /* infix IS (IS DISTINCT FROM, etc) */
# define PREC_GROUP_LESS 3 /* < > */
# define PREC_GROUP_EQUAL 4 /* = */
# define PREC_GROUP_LESS_EQUAL 5 /* <= >= <> */
# define PREC_GROUP_LIKE 6 /* LIKE ILIKE SIMILAR */
# define PREC_GROUP_BETWEEN 7 /* BETWEEN */
# define PREC_GROUP_IN 8 /* IN */
# define PREC_GROUP_NOT_LIKE 9 /* NOT LIKE/ILIKE/SIMILAR */
# define PREC_GROUP_NOT_BETWEEN 10 /* NOT BETWEEN */
# define PREC_GROUP_NOT_IN 11 /* NOT IN */
# define PREC_GROUP_ANY_ALL 12 /* ANY/ALL */
# define PREC_GROUP_INFIX_OP 13 /* generic infix operators */
# define PREC_GROUP_PREFIX_OP 14 /* generic prefix operators */
/*
* Map precedence groupings to old precedence ordering
*
* Old precedence order :
* 1. NOT
* 2. =
* 3. < >
* 4. LIKE ILIKE SIMILAR
* 5. BETWEEN
* 6. IN
* 7. ANY ALL
* 8. generic Op , including < = = > < >
* 9. generic prefix Op
* 10. IS tests ( NullTest , BooleanTest , etc )
*
* NOT BETWEEN etc map to BETWEEN etc when considered as being on the left ,
* but to NOT when considered as being on the right , because of the buggy
* precedence handling of those productions in the old grammar .
*/
static const int oldprecedence_l [ ] = {
0 , 10 , 10 , 3 , 2 , 8 , 4 , 5 , 6 , 4 , 5 , 6 , 7 , 8 , 9
} ;
static const int oldprecedence_r [ ] = {
0 , 10 , 10 , 3 , 2 , 8 , 4 , 5 , 6 , 1 , 1 , 1 , 7 , 8 , 9
} ;
static Node * transformExprRecurse ( ParseState * pstate , Node * expr ) ;
static Node * transformParamRef ( ParseState * pstate , ParamRef * pref ) ;
@ -127,11 +82,6 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node * ltree , Node * rtree , int location ) ;
static Node * make_nulltest_from_distinct ( ParseState * pstate ,
A_Expr * distincta , Node * arg ) ;
static int operator_precedence_group ( Node * node , const char * * nodename ) ;
static void emit_precedence_warnings ( ParseState * pstate ,
int opgroup , const char * opname ,
Node * lchild , Node * rchild ,
int location ) ;
/*
@ -242,9 +192,6 @@ transformExprRecurse(ParseState *pstate, Node *expr)
case AEXPR_NOT_BETWEEN_SYM :
result = transformAExprBetween ( pstate , a ) ;
break ;
case AEXPR_PAREN :
result = transformExprRecurse ( pstate , a - > lexpr ) ;
break ;
default :
elog ( ERROR , " unrecognized A_Expr kind: %d " , a - > kind ) ;
result = NULL ; /* keep compiler quiet */
@ -315,11 +262,6 @@ transformExprRecurse(ParseState *pstate, Node *expr)
{
NullTest * n = ( NullTest * ) expr ;
if ( operator_precedence_warning )
emit_precedence_warnings ( pstate , PREC_GROUP_POSTFIX_IS , " IS " ,
( Node * ) n - > arg , NULL ,
n - > location ) ;
n - > arg = ( Expr * ) transformExprRecurse ( pstate , ( Node * ) n - > arg ) ;
/* the argument can be any type, so don't coerce it */
n - > argisrow = type_is_rowtype ( exprType ( ( Node * ) n - > arg ) ) ;
@ -926,26 +868,6 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
Node * rexpr = a - > rexpr ;
Node * result ;
if ( operator_precedence_warning )
{
int opgroup ;
const char * opname ;
opgroup = operator_precedence_group ( ( Node * ) a , & opname ) ;
if ( opgroup > 0 )
emit_precedence_warnings ( pstate , opgroup , opname ,
lexpr , rexpr ,
a - > location ) ;
/* Look through AEXPR_PAREN nodes so they don't affect tests below */
while ( lexpr & & IsA ( lexpr , A_Expr ) & &
( ( A_Expr * ) lexpr ) - > kind = = AEXPR_PAREN )
lexpr = ( ( A_Expr * ) lexpr ) - > lexpr ;
while ( rexpr & & IsA ( rexpr , A_Expr ) & &
( ( A_Expr * ) rexpr ) - > kind = = AEXPR_PAREN )
rexpr = ( ( A_Expr * ) rexpr ) - > lexpr ;
}
/*
* Special - case " foo = NULL " and " NULL = foo " for compatibility with
* standards - broken products ( like Microsoft ' s ) . Turn these into IS NULL
@ -1023,17 +945,8 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
static Node *
transformAExprOpAny ( ParseState * pstate , A_Expr * a )
{
Node * lexpr = a - > lexpr ;
Node * rexpr = a - > rexpr ;
if ( operator_precedence_warning )
emit_precedence_warnings ( pstate , PREC_GROUP_ANY_ALL ,
strVal ( llast ( a - > name ) ) ,
lexpr , NULL ,
a - > location ) ;
lexpr = transformExprRecurse ( pstate , lexpr ) ;
rexpr = transformExprRecurse ( pstate , rexpr ) ;
Node * lexpr = transformExprRecurse ( pstate , a - > lexpr ) ;
Node * rexpr = transformExprRecurse ( pstate , a - > rexpr ) ;
return ( Node * ) make_scalar_array_op ( pstate ,
a - > name ,
@ -1046,17 +959,8 @@ transformAExprOpAny(ParseState *pstate, A_Expr *a)
static Node *
transformAExprOpAll ( ParseState * pstate , A_Expr * a )
{
Node * lexpr = a - > lexpr ;
Node * rexpr = a - > rexpr ;
if ( operator_precedence_warning )
emit_precedence_warnings ( pstate , PREC_GROUP_ANY_ALL ,
strVal ( llast ( a - > name ) ) ,
lexpr , NULL ,
a - > location ) ;
lexpr = transformExprRecurse ( pstate , lexpr ) ;
rexpr = transformExprRecurse ( pstate , rexpr ) ;
Node * lexpr = transformExprRecurse ( pstate , a - > lexpr ) ;
Node * rexpr = transformExprRecurse ( pstate , a - > rexpr ) ;
return ( Node * ) make_scalar_array_op ( pstate ,
a - > name ,
@ -1073,11 +977,6 @@ transformAExprDistinct(ParseState *pstate, A_Expr *a)
Node * rexpr = a - > rexpr ;
Node * result ;
if ( operator_precedence_warning )
emit_precedence_warnings ( pstate , PREC_GROUP_INFIX_IS , " IS " ,
lexpr , rexpr ,
a - > location ) ;
/*
* If either input is an undecorated NULL literal , transform to a NullTest
* on the other input . That ' s simpler to process than a full DistinctExpr ,
@ -1183,13 +1082,6 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
else
useOr = true ;
if ( operator_precedence_warning )
emit_precedence_warnings ( pstate ,
useOr ? PREC_GROUP_IN : PREC_GROUP_NOT_IN ,
" IN " ,
a - > lexpr , NULL ,
a - > location ) ;
/*
* We try to generate a ScalarArrayOpExpr from IN / NOT IN , but this is only
* possible if there is a suitable array type available . If not , we fall
@ -1342,22 +1234,6 @@ transformAExprBetween(ParseState *pstate, A_Expr *a)
bexpr = ( Node * ) linitial ( args ) ;
cexpr = ( Node * ) lsecond ( args ) ;
if ( operator_precedence_warning )
{
int opgroup ;
const char * opname ;
opgroup = operator_precedence_group ( ( Node * ) a , & opname ) ;
emit_precedence_warnings ( pstate , opgroup , opname ,
aexpr , cexpr ,
a - > location ) ;
/* We can ignore bexpr thanks to syntactic restrictions */
/* Wrap subexpressions to prevent extra warnings */
aexpr = ( Node * ) makeA_Expr ( AEXPR_PAREN , NIL , aexpr , NULL , - 1 ) ;
bexpr = ( Node * ) makeA_Expr ( AEXPR_PAREN , NIL , bexpr , NULL , - 1 ) ;
cexpr = ( Node * ) makeA_Expr ( AEXPR_PAREN , NIL , cexpr , NULL , - 1 ) ;
}
/*
* Build the equivalent comparison expression . Make copies of
* multiply - referenced subexpressions for safety . ( XXX this is really
@ -1964,19 +1840,6 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
List * right_list ;
ListCell * l ;
if ( operator_precedence_warning )
{
if ( sublink - > operName = = NIL )
emit_precedence_warnings ( pstate , PREC_GROUP_IN , " IN " ,
sublink - > testexpr , NULL ,
sublink - > location ) ;
else
emit_precedence_warnings ( pstate , PREC_GROUP_ANY_ALL ,
strVal ( llast ( sublink - > operName ) ) ,
sublink - > testexpr , NULL ,
sublink - > location ) ;
}
/*
* If the source was " x IN (select) " , convert to " x = ANY (select) " .
*/
@ -2076,11 +1939,6 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Node * e = ( Node * ) lfirst ( element ) ;
Node * newe ;
/* Look through AEXPR_PAREN nodes so they don't affect test below */
while ( e & & IsA ( e , A_Expr ) & &
( ( A_Expr * ) e ) - > kind = = AEXPR_PAREN )
e = ( ( A_Expr * ) e ) - > lexpr ;
/*
* If an element is itself an A_ArrayExpr , recurse directly so that we
* can pass down any target type we were given .
@ -2389,11 +2247,6 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
ListCell * lc ;
int i ;
if ( operator_precedence_warning & & x - > op = = IS_DOCUMENT )
emit_precedence_warnings ( pstate , PREC_GROUP_POSTFIX_IS , " IS " ,
( Node * ) linitial ( x - > args ) , NULL ,
x - > location ) ;
newx = makeNode ( XmlExpr ) ;
newx - > op = x - > op ;
if ( x - > name )
@ -2564,11 +2417,6 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b)
{
const char * clausename ;
if ( operator_precedence_warning )
emit_precedence_warnings ( pstate , PREC_GROUP_POSTFIX_IS , " IS " ,
( Node * ) b - > arg , NULL ,
b - > location ) ;
switch ( b - > booltesttype )
{
case IS_TRUE :
@ -2702,15 +2550,6 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
/* Look up the type name first */
typenameTypeIdAndMod ( pstate , tc - > typeName , & targetType , & targetTypmod ) ;
/*
* Look through any AEXPR_PAREN nodes that may have been inserted thanks
* to operator_precedence_warning . Otherwise , ARRAY [ ] : : foo [ ] behaves
* differently from ( ARRAY [ ] ) : : foo [ ] .
*/
while ( arg & & IsA ( arg , A_Expr ) & &
( ( A_Expr * ) arg ) - > kind = = AEXPR_PAREN )
arg = ( ( A_Expr * ) arg ) - > lexpr ;
/*
* If the subject of the typecast is an ARRAY [ ] construct and the target
* type is an array type , we invoke transformArrayExpr ( ) directly so that
@ -3117,287 +2956,6 @@ make_nulltest_from_distinct(ParseState *pstate, A_Expr *distincta, Node *arg)
return ( Node * ) nt ;
}
/*
* Identify node ' s group for operator precedence warnings
*
* For items in nonzero groups , also return a suitable node name into * nodename
*
* Note : group zero is used for nodes that are higher or lower precedence
* than everything that changed precedence ; we need never issue warnings
* related to such nodes .
*/
static int
operator_precedence_group ( Node * node , const char * * nodename )
{
int group = 0 ;
* nodename = NULL ;
if ( node = = NULL )
return 0 ;
if ( IsA ( node , A_Expr ) )
{
A_Expr * aexpr = ( A_Expr * ) node ;
if ( aexpr - > kind = = AEXPR_OP & &
aexpr - > lexpr ! = NULL & &
aexpr - > rexpr ! = NULL )
{
/* binary operator */
if ( list_length ( aexpr - > name ) = = 1 )
{
* nodename = strVal ( linitial ( aexpr - > name ) ) ;
/* Ignore if op was always higher priority than IS-tests */
if ( strcmp ( * nodename , " + " ) = = 0 | |
strcmp ( * nodename , " - " ) = = 0 | |
strcmp ( * nodename , " * " ) = = 0 | |
strcmp ( * nodename , " / " ) = = 0 | |
strcmp ( * nodename , " % " ) = = 0 | |
strcmp ( * nodename , " ^ " ) = = 0 )
group = 0 ;
else if ( strcmp ( * nodename , " < " ) = = 0 | |
strcmp ( * nodename , " > " ) = = 0 )
group = PREC_GROUP_LESS ;
else if ( strcmp ( * nodename , " = " ) = = 0 )
group = PREC_GROUP_EQUAL ;
else if ( strcmp ( * nodename , " <= " ) = = 0 | |
strcmp ( * nodename , " >= " ) = = 0 | |
strcmp ( * nodename , " <> " ) = = 0 )
group = PREC_GROUP_LESS_EQUAL ;
else
group = PREC_GROUP_INFIX_OP ;
}
else
{
/* schema-qualified operator syntax */
* nodename = " OPERATOR() " ;
group = PREC_GROUP_INFIX_OP ;
}
}
else if ( aexpr - > kind = = AEXPR_OP & &
aexpr - > lexpr = = NULL & &
aexpr - > rexpr ! = NULL )
{
/* prefix operator */
if ( list_length ( aexpr - > name ) = = 1 )
{
* nodename = strVal ( linitial ( aexpr - > name ) ) ;
/* Ignore if op was always higher priority than IS-tests */
if ( strcmp ( * nodename , " + " ) = = 0 | |
strcmp ( * nodename , " - " ) = = 0 )
group = 0 ;
else
group = PREC_GROUP_PREFIX_OP ;
}
else
{
/* schema-qualified operator syntax */
* nodename = " OPERATOR() " ;
group = PREC_GROUP_PREFIX_OP ;
}
}
else if ( aexpr - > kind = = AEXPR_OP_ANY | |
aexpr - > kind = = AEXPR_OP_ALL )
{
* nodename = strVal ( llast ( aexpr - > name ) ) ;
group = PREC_GROUP_ANY_ALL ;
}
else if ( aexpr - > kind = = AEXPR_DISTINCT | |
aexpr - > kind = = AEXPR_NOT_DISTINCT )
{
* nodename = " IS " ;
group = PREC_GROUP_INFIX_IS ;
}
else if ( aexpr - > kind = = AEXPR_IN )
{
* nodename = " IN " ;
if ( strcmp ( strVal ( linitial ( aexpr - > name ) ) , " = " ) = = 0 )
group = PREC_GROUP_IN ;
else
group = PREC_GROUP_NOT_IN ;
}
else if ( aexpr - > kind = = AEXPR_LIKE )
{
* nodename = " LIKE " ;
if ( strcmp ( strVal ( linitial ( aexpr - > name ) ) , " ~~ " ) = = 0 )
group = PREC_GROUP_LIKE ;
else
group = PREC_GROUP_NOT_LIKE ;
}
else if ( aexpr - > kind = = AEXPR_ILIKE )
{
* nodename = " ILIKE " ;
if ( strcmp ( strVal ( linitial ( aexpr - > name ) ) , " ~~* " ) = = 0 )
group = PREC_GROUP_LIKE ;
else
group = PREC_GROUP_NOT_LIKE ;
}
else if ( aexpr - > kind = = AEXPR_SIMILAR )
{
* nodename = " SIMILAR " ;
if ( strcmp ( strVal ( linitial ( aexpr - > name ) ) , " ~ " ) = = 0 )
group = PREC_GROUP_LIKE ;
else
group = PREC_GROUP_NOT_LIKE ;
}
else if ( aexpr - > kind = = AEXPR_BETWEEN | |
aexpr - > kind = = AEXPR_BETWEEN_SYM )
{
Assert ( list_length ( aexpr - > name ) = = 1 ) ;
* nodename = strVal ( linitial ( aexpr - > name ) ) ;
group = PREC_GROUP_BETWEEN ;
}
else if ( aexpr - > kind = = AEXPR_NOT_BETWEEN | |
aexpr - > kind = = AEXPR_NOT_BETWEEN_SYM )
{
Assert ( list_length ( aexpr - > name ) = = 1 ) ;
* nodename = strVal ( linitial ( aexpr - > name ) ) ;
group = PREC_GROUP_NOT_BETWEEN ;
}
}
else if ( IsA ( node , NullTest ) | |
IsA ( node , BooleanTest ) )
{
* nodename = " IS " ;
group = PREC_GROUP_POSTFIX_IS ;
}
else if ( IsA ( node , XmlExpr ) )
{
XmlExpr * x = ( XmlExpr * ) node ;
if ( x - > op = = IS_DOCUMENT )
{
* nodename = " IS " ;
group = PREC_GROUP_POSTFIX_IS ;
}
}
else if ( IsA ( node , SubLink ) )
{
SubLink * s = ( SubLink * ) node ;
if ( s - > subLinkType = = ANY_SUBLINK | |
s - > subLinkType = = ALL_SUBLINK )
{
if ( s - > operName = = NIL )
{
* nodename = " IN " ;
group = PREC_GROUP_IN ;
}
else
{
* nodename = strVal ( llast ( s - > operName ) ) ;
group = PREC_GROUP_ANY_ALL ;
}
}
}
else if ( IsA ( node , BoolExpr ) )
{
/*
* Must dig into NOTs to see if it ' s IS NOT DOCUMENT or NOT IN . This
* opens us to possibly misrecognizing , eg , NOT ( x IS DOCUMENT ) as a
* problematic construct . We can tell the difference by checking
* whether the parse locations of the two nodes are identical .
*
* Note that when we are comparing the child node to its own children ,
* we will not know that it was a NOT . Fortunately , that doesn ' t
* matter for these cases .
*/
BoolExpr * b = ( BoolExpr * ) node ;
if ( b - > boolop = = NOT_EXPR )
{
Node * child = ( Node * ) linitial ( b - > args ) ;
if ( IsA ( child , XmlExpr ) )
{
XmlExpr * x = ( XmlExpr * ) child ;
if ( x - > op = = IS_DOCUMENT & &
x - > location = = b - > location )
{
* nodename = " IS " ;
group = PREC_GROUP_POSTFIX_IS ;
}
}
else if ( IsA ( child , SubLink ) )
{
SubLink * s = ( SubLink * ) child ;
if ( s - > subLinkType = = ANY_SUBLINK & & s - > operName = = NIL & &
s - > location = = b - > location )
{
* nodename = " IN " ;
group = PREC_GROUP_NOT_IN ;
}
}
}
}
return group ;
}
/*
* helper routine for delivering 9.4 - to - 9.5 operator precedence warnings
*
* opgroup / opname / location represent some parent node
* lchild , rchild are its left and right children ( either could be NULL )
*
* This should be called before transforming the child nodes , since if a
* precedence - driven parsing change has occurred in a query that used to work ,
* it ' s quite possible that we ' ll get a semantic failure while analyzing the
* child expression . We want to produce the warning before that happens .
* In any case , operator_precedence_group ( ) expects untransformed input .
*/
static void
emit_precedence_warnings ( ParseState * pstate ,
int opgroup , const char * opname ,
Node * lchild , Node * rchild ,
int location )
{
int cgroup ;
const char * copname ;
Assert ( opgroup > 0 ) ;
/*
* Complain if left child , which should be same or higher precedence
* according to current rules , used to be lower precedence .
*
* Exception to precedence rules : if left child is IN or NOT IN the
* grouping is syntactically forced regardless of precedence .
*/
cgroup = operator_precedence_group ( lchild , & copname ) ;
if ( cgroup > 0 )
{
if ( oldprecedence_l [ cgroup ] < oldprecedence_r [ opgroup ] & &
cgroup ! = PREC_GROUP_IN & &
cgroup ! = PREC_GROUP_NOT_IN & &
cgroup ! = PREC_GROUP_ANY_ALL & &
cgroup ! = PREC_GROUP_POSTFIX_IS )
ereport ( WARNING ,
( errmsg ( " operator precedence change: %s is now lower precedence than %s " ,
opname , copname ) ,
parser_errposition ( pstate , location ) ) ) ;
}
/*
* Complain if right child , which should be higher precedence according to
* current rules , used to be same or lower precedence .
*
* Exception to precedence rules : if right child is a prefix operator , the
* grouping is syntactically forced regardless of precedence .
*/
cgroup = operator_precedence_group ( rchild , & copname ) ;
if ( cgroup > 0 )
{
if ( oldprecedence_r [ cgroup ] < = oldprecedence_l [ opgroup ] & &
cgroup ! = PREC_GROUP_PREFIX_OP )
ereport ( WARNING ,
( errmsg ( " operator precedence change: %s is now lower precedence than %s " ,
opname , copname ) ,
parser_errposition ( pstate , location ) ) ) ;
}
}
/*
* Produce a string identifying an expression by kind .
*