Merge commit 'origin/andrei/type_conversion'
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 4 May 2009 21:38:12 +0000 (23:38 +0200)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 4 May 2009 21:38:12 +0000 (23:38 +0200)
* commit 'origin/andrei/type_conversion':
  script parsing: fix bug in expression error checking
  core expr eval: various fixes
  news: update (new operators, expr. eval behaviour a.s.o.)
  core: new script operators: eq, ne, ieq, ine
  core expr eval: fix assoc., commut and 0 adding for +, ==
  core expr eval: minor ==/!= optimization
  core expr eval: internal == & != int and str only versions
  core expr eval: special handling for undef cmp expr
  core eval expr: cache undefined results too
  core expr eval:  str automatic conversion to int
  core expr eval: undef conversion to int and str
  core expr eval: defined @select

1  2 
cfg.lex
cfg.y
rvalue.c

diff --combined cfg.lex
+++ b/cfg.lex
@@@ -248,6 -248,10 +248,10 @@@ MINUS    "-
  STRLEN        "strlen"
  STREMPTY      "strempty"
  DEFINED               "defined"
+ STREQ eq
+ INTEQ ieq
+ STRDIFF       ne
+ INTDIFF       ine
  
  /* Attribute specification */
  ATTR_MARK   "%"
@@@ -273,7 -277,6 +277,7 @@@ LOGSTDERROR        log_stderro
  LOGFACILITY   log_facility
  LISTEN                listen
  ALIAS         alias
 +SR_AUTO_ALIASES       auto_aliases
  DNS            dns
  REV_DNS        rev_dns
  DNS_TRY_IPV6  dns_try_ipv6
@@@ -551,8 -554,6 +555,8 @@@ EAT_ABLE   [\ \t\b\r
  <INITIAL>{LOGFACILITY}        { yylval.strval=yytext; return LOGFACILITY; }
  <INITIAL>{LISTEN}     { count(); yylval.strval=yytext; return LISTEN; }
  <INITIAL>{ALIAS}      { count(); yylval.strval=yytext; return ALIAS; }
 +<INITIAL>{SR_AUTO_ALIASES}    { count(); yylval.strval=yytext;
 +                                                                      return SR_AUTO_ALIASES; }
  <INITIAL>{DNS}        { count(); yylval.strval=yytext; return DNS; }
  <INITIAL>{REV_DNS}    { count(); yylval.strval=yytext; return REV_DNS; }
  <INITIAL>{DNS_TRY_IPV6}       { count(); yylval.strval=yytext;
  <INITIAL>{STRLEN}     { count(); return STRLEN; }
  <INITIAL>{STREMPTY}   { count(); return STREMPTY; }
  <INITIAL>{DEFINED}    { count(); return DEFINED; }
+ <INITIAL>{STREQ}      { count(); return STREQ; }
+ <INITIAL>{INTEQ}      { count(); return INTEQ; }
+ <INITIAL>{STRDIFF}    { count(); return STRDIFF; }
+ <INITIAL>{INTDIFF}    { count(); return INTDIFF; }
  
  <INITIAL>{SELECT_MARK}  { count(); state = SELECT_S; BEGIN(SELECT); return SELECT_MARK; }
  <SELECT>{ID}          { count(); addstr(&s_buf, yytext, yyleng);
diff --combined cfg.y
--- 1/cfg.y
--- 2/cfg.y
+++ b/cfg.y
@@@ -333,7 -333,6 +333,7 @@@ static int case_check_default(struct ca
  %token LOGFACILITY
  %token LISTEN
  %token ALIAS
 +%token SR_AUTO_ALIASES
  %token DNS
  %token REV_DNS
  %token DNS_TRY_IPV6
  %left LOG_AND
  %left BIN_OR
  %left BIN_AND
- %left EQUAL_T DIFF MATCH
+ %left EQUAL_T DIFF MATCH INTEQ INTDIFF STREQ STRDIFF
  %left GT LT GTE LTE
  %left PLUS MINUS
  %left STAR SLASH
@@@ -1273,8 -1272,6 +1273,8 @@@ assign_stm
                free_socket_id_lst($3);
        }
        | ALIAS  EQUAL error  { yyerror(" hostname expected"); }
 +      | SR_AUTO_ALIASES EQUAL NUMBER { sr_auto_aliases=$3; }
 +      | SR_AUTO_ALIASES EQUAL error  { yyerror("boolean value expected"); }
        | ADVERTISED_ADDRESS EQUAL listen_id {
                default_global_address.s=$3;
                default_global_address.len=strlen($3);
@@@ -1568,7 -1565,10 +1568,10 @@@ send_route_stm: ROUTE_SEND LBRACE actio
  
  exp:  rval_expr
                {
-                       if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
+                       if ($1==0){
+                               yyerror("invalid expression");
+                               $$=0;
+                       }else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
                                yyerror("invalid expression");
                                $$=0;
                        }else if (i_tmp!=RV_INT && i_tmp!=RV_NONE){
  equalop:
        EQUAL_T {$$=EQUAL_OP; }
        | DIFF  {$$=DIFF_OP; }
+       | STREQ {$$=EQUAL_OP; }  /* for expr. elems equiv. to EQUAL_T*/
+       | STRDIFF {$$=DIFF_OP; } /* for expr. elems. equiv. to DIFF */
        ;
  cmpop:
          GT    {$$=GT_OP; }
@@@ -1600,6 -1602,10 +1605,10 @@@ strop
  rve_equalop:
        EQUAL_T {$$=RVE_EQ_OP; }
        | DIFF  {$$=RVE_DIFF_OP; }
+       | INTEQ {$$=RVE_IEQ_OP; }
+       | INTDIFF {$$=RVE_IDIFF_OP; }
+       | STREQ {$$=RVE_STREQ_OP; }
+       | STRDIFF {$$=RVE_STRDIFF_OP; }
        ;
  rve_cmpop:
          GT    {$$=RVE_GT_OP; }
@@@ -2217,9 -2223,9 +2226,9 @@@ rval: intno                     {$$=mk_rve_rval(RV_INT, (
        | fcmd                          {$$=mk_rve_rval(RV_ACTION_ST, $1); }
        | exp_elem { $$=mk_rve_rval(RV_BEXPR, $1); }
        | LBRACE actions RBRACE {$$=mk_rve_rval(RV_ACTION_ST, $2); }
-       | LBRACE error RBRACE   { yyerror("bad command block"); }
+       | LBRACE error RBRACE   { $$=0; yyerror("bad command block"); }
        | LPAREN assign_action RPAREN   {$$=mk_rve_rval(RV_ACTION_ST, $2); }
-       | LPAREN error RPAREN   { yyerror("bad expression"); }
+       | LPAREN error RPAREN   { $$=0; yyerror("bad expression"); }
        ;
  
  
@@@ -2237,10 -2243,11 +2246,11 @@@ rve_op:              PLUS            { $$=RVE_PLUS_OP; 
  */
  
  rval_expr: rval                                               { $$=$1;
-                                                                                       if ($$==0){
+                                                                               /*      if ($$==0){
                                                                                                yyerror("out of memory\n");
                                                                                                YYABORT;
                                                                                        }
+                                                                                       */
                                                                        }
                | rve_un_op %prec NOT rval_expr {$$=mk_rve1($1, $2); }
                | rval_expr PLUS rval_expr              {$$=mk_rve2(RVE_PLUS_OP, $1, $3); }
                | STRLEN LPAREN rval_expr RPAREN { $$=mk_rve1(RVE_STRLEN_OP, $3);}
                | STREMPTY LPAREN rval_expr RPAREN {$$=mk_rve1(RVE_STREMPTY_OP, $3);}
                | DEFINED rval_expr                             { $$=mk_rve1(RVE_DEFINED_OP, $2);}
-               | rve_un_op %prec NOT error             { yyerror("bad expression"); }
+               | rve_un_op %prec NOT error             { $$=0; yyerror("bad expression"); }
                | rval_expr PLUS error                  { yyerror("bad expression"); }
                | rval_expr MINUS error                 { yyerror("bad expression"); }
                | rval_expr STAR error                  { yyerror("bad expression"); }
                        { yyerror("bad expression"); }
                | rval_expr LOG_AND error               { yyerror("bad expression"); }
                | rval_expr LOG_OR error                { yyerror("bad expression"); }
-               | STRLEN LPAREN error RPAREN    { yyerror("bad expression"); }
-               | STREMPTY LPAREN error RPAREN  { yyerror("bad expression"); }
-               | DEFINED error                                 { yyerror("bad expression"); }
+               | STRLEN LPAREN error RPAREN    { $$=0; yyerror("bad expression"); }
+               | STREMPTY LPAREN error RPAREN  { $$=0; yyerror("bad expression"); }
+               | DEFINED error                                 { $$=0; yyerror("bad expression"); }
                ;
  
  assign_action: lval assign_op  rval_expr      { $$=mk_action($2, 2, LVAL_ST, $1, 
@@@ -2901,13 -2908,19 +2911,19 @@@ static struct rval_expr* mk_rve2(enum r
        
        if ((rve1==0) || (rve2==0))
                return 0;
+       bad_rve=0;
+       bad_t=0;
+       exp_t=0;
        cfg_pos_join(&pos, &rve1->fpos, &rve2->fpos);
        ret=mk_rval_expr2(op, rve1, rve2, &pos);
        if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
-               yyerror_at(&pos, "bad expression: type mismatch:"
+               if (bad_rve)
+                       yyerror_at(&pos, "bad expression: type mismatch:"
                                                " %s instead of %s at (%d,%d)",
                                                rval_type_name(bad_t), rval_type_name(exp_t),
                                                bad_rve->fpos.s_line, bad_rve->fpos.s_col);
+               else
+                       yyerror("BUG: unexpected null \"bad\" expression\n");
        }
        return ret;
  }
diff --combined rvalue.c
+++ b/rvalue.c
   * --------
   *  2008-12-01  initial version (andrei)
   *  2009-04-24  added support for defined, strempty, strlen (andrei)
+  *  2009-04-28  int and str automatic conversions: (int)undef=0,
+  *               (str)undef="", (int)""=0, (int)"123"=123, (int)"abc"=0
+  *              handle undef == expr, in function of the UNDEF_EQ_* defines.
+  *              (andrei)
+  */
+ /* special defines:
+  *
+  *  UNDEF_EQ_* - how to behave when undef is on the right side of a generic
+  *               compare operator
+  *  UNDEF_EQ_ALWAYS_FALSE:  undef  == something  is always false
+  *  UNDEF_EQ_UNDEF_TRUE  :  undef == something false except for undef==undef
+  *                          which is true
+  *  no UNDEF_EQ* define  :  undef == expr => convert undef to typeof(expr)
+  *                          and perform normal ==. undef == undef will be
+  *                          converted to string and it will be true
+  *                          ("" == "")
+  * NOTE: expr == undef, with defined(expr) is always evaluated this way:
+          expr == (type_of(expr))undef
   */
  
  #include "rvalue.h"
@@@ -111,7 -130,7 +130,7 @@@ void rve_destroy(struct rval_expr* rve
  
  void rval_cache_clean(struct rval_cache* rvc)
  {
-       if (rvc->cache_type==RV_CACHE_PVAR){
+       if ((rvc->cache_type==RV_CACHE_PVAR) && (rvc->val_type!=RV_NONE)){
                pv_value_destroy(&rvc->c.pval);
        }
        rvc->cache_type=RV_CACHE_EMPTY;
@@@ -261,7 -280,7 +280,7 @@@ struct rvalue* rval_new(enum rval_type 
    * rval_cache_clean()'en when no longer needed.
    *
    * @param rv - target rvalue
-   * @param val_cache - value cache, might be filled if non-null, 
+   * @param val_cache - write-only: value cache, might be filled if non-null,
    *                    it _must_ be rval_cache_clean()'en when done.
    * @return - basic type or RV_NONE on error
    */
@@@ -288,36 -307,39 +307,39 @@@ inline static enum rval_type rval_get_b
                case RV_PVAR:
                        if (likely(val_cache && val_cache->cache_type==RV_CACHE_EMPTY)){
                                pv=&val_cache->c.pval;
+                               val_cache->cache_type=RV_CACHE_PVAR;
                        }else{
                                val_cache=0;
                                pv=&tmp_pval;
                        }
                        memset(pv, 0, sizeof(tmp_pval));
                        if (likely(pv_get_spec_value(msg, &rv->v.pvs, pv)==0)){
-                               if (pv->flags & PV_VAL_STR){
-                                       if (unlikely(val_cache==0)) pv_value_destroy(pv);
-                                       else{
-                                               val_cache->cache_type=RV_CACHE_PVAR;
-                                               val_cache->val_type=RV_STR;
-                                       }
-                                       return RV_STR;
-                               }else if (pv->flags & PV_TYPE_INT){
-                                       if (unlikely(val_cache==0)) pv_value_destroy(pv);
-                                       else{
-                                               val_cache->cache_type=RV_CACHE_PVAR;
+                               if (pv->flags & PV_TYPE_INT){
+                                       if (likely(val_cache!=0))
                                                val_cache->val_type=RV_INT;
-                                       }
+                                       else
+                                               pv_value_destroy(pv);
                                        return RV_INT;
+                               }else if (pv->flags & PV_VAL_STR){
+                                       if (likely(val_cache!=0))
+                                               val_cache->val_type=RV_STR;
+                                       else
+                                               pv_value_destroy(pv);
+                                       return RV_STR;
                                }else{
                                        pv_value_destroy(pv);
+                                       if (likely(val_cache!=0))
+                                               val_cache->val_type=RV_NONE; /* undefined */
                                        goto error;
                                }
                        }else{
+                               if (likely(val_cache!=0))
+                                       val_cache->val_type=RV_NONE; /* undefined */
                                goto error;
                        }
                        break;
                case RV_AVP:
-                       if (likely(val_cache && val_cache==RV_CACHE_EMPTY)){
+                       if (likely(val_cache && val_cache->cache_type==RV_CACHE_EMPTY)){
                                ptype=&val_cache->val_type;
                                avpv=&val_cache->c.avp_val;
                                val_cache->cache_type=RV_CACHE_AVP;
                                }
                        }else{
                                *ptype=RV_NONE;
-                               if (val_cache) val_cache->cache_type=RV_CACHE_EMPTY;
                                goto error;
                        }
                        break;
@@@ -390,6 -411,10 +411,10 @@@ enum rval_type rve_guess_type( struct r
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_IPLUS_OP:
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
@@@ -450,6 -475,10 +475,10 @@@ int rve_is_constant(struct rval_expr* r
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_PLUS_OP:
                case RVE_IPLUS_OP:
                case RVE_CONCAT_OP:
@@@ -502,6 -531,10 +531,10 @@@ static int rve_op_unary(enum rval_expr_
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_PLUS_OP:
                case RVE_IPLUS_OP:
                case RVE_CONCAT_OP:
@@@ -564,6 -597,8 +597,8 @@@ int rve_check_type(enum rval_type* type
                case RVE_GTE_OP:
                case RVE_LT_OP:
                case RVE_LTE_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                case RVE_IPLUS_OP:
                        *type=RV_INT;
                        if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
                                }
                        }
                        break;
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
+                       *type=RV_INT;
+                       if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+                               if (rve_check_type(&type2, rve->right.rve, bad_rve, bad_t,
+                                                                       exp_t)){
+                                       if ((type2!=type1) && (type1!=RV_NONE) &&
+                                                       (type2!=RV_NONE) &&
+                                                       !(type1==RV_STR && type2==RV_INT)){
+                                               if (bad_rve) *bad_rve=rve->right.rve;
+                                               if (bad_t) *bad_t=type2;
+                                               if (exp_t) *exp_t=type1;
+                                               return 0;
+                                       }
+                                       if (type1==RV_INT){
+                                               if (bad_rve) *bad_rve=rve->left.rve;
+                                               if (bad_t) *bad_t=type1;
+                                               if (exp_t) *exp_t=RV_STR;
+                                               return 0;
+                                       }
+                                       return 1;
+                               }
+                       }
+                       break;
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                case RVE_DEFINED_OP:
                        }
                        break;
                case RVE_NONE_OP:
+               default:
+                       BUG("unexpected rve op %d\n", rve->op);
+                       if (bad_rve) *bad_rve=rve;
+                       if (bad_t) *bad_t=RV_NONE;
+                       if (exp_t) *exp_t=RV_STR;
                        break;
        }
        return 0;
  
  /** get the integer value of an rvalue.
    * *i=(int)rv
+   * if rv == undefined select, avp or pvar, return 0.
+   * if an error occurs while evaluating a select, avp or pvar, behave as
+   * for the undefined case (and return success).
+   * @param h - script context handle
+   * @param msg - sip msg
+   * @param i   - pointer to int, where the conversion result will be stored
+   * @param rv   - rvalue to be converted
+   * @param cache - cached rv value (read-only), can be 0
+   *
    * @return 0 on success, \<0 on error and EXPR_DROP on drop
   */
  int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
        avp_t* r_avp;
        int_str avp_val;
        pv_value_t pval;
+       str tmp;
+       str* s;
+       int r;
+       int destroy_pval;
        
+       destroy_pval=0;
+       s=0;
        switch(rv->type){
                case RV_INT:
                        *i=rv->v.l;
                        break;
                case RV_STR:
+                       s=&rv->v.s;
                        goto rv_str;
                case RV_BEXPR:
                        *i=eval_expr(h, rv->v.bexpr, msg);
                        break;
                case RV_ACTION_ST:
                        if (rv->v.action)
 -                              *i=run_actions(h, rv->v.action, msg);
 -                      else 
 +                              *i=(run_actions(h, rv->v.action, msg)>0);
 +                      else
                                *i=0;
                        break;
                case RV_SEL:
+                       r=run_select(&tmp, &rv->v.sel, msg);
+                       if (unlikely(r!=0)){
+                               if (r<0)
+                                       goto eval_error;
+                               else /* i>0  => undefined */
+                                       goto undef;
+                       }
+                       s=&tmp;
                        goto rv_str;
                case RV_AVP:
                        if (unlikely(cache && cache->cache_type==RV_CACHE_AVP)){
                                if (likely(cache->val_type==RV_INT)){
                                        *i=cache->c.avp_val.n;
-                               }else if (cache->val_type==RV_STR)
+                               }else if (cache->val_type==RV_STR){
+                                       s=&cache->c.avp_val.s;
                                        goto rv_str;
-                               else goto error;
+                               }else if (cache->val_type==RV_NONE)
+                                       goto undef;
+                               else goto error_cache;
                        }else{
                                r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
                                                                                        &avp_val, rv->v.avps.index);
                                if (likely(r_avp)){
                                        if (unlikely(r_avp->flags & AVP_VAL_STR)){
+                                               s=&avp_val.s;
                                                goto rv_str;
                                        }else{
                                                *i=avp_val.n;
                                        }
                                }else{
-                                       goto error;
+                                       goto undef;
                                }
                        }
                        break;
                case RV_PVAR:
                        if (unlikely(cache && cache->cache_type==RV_CACHE_PVAR)){
-                               if (likely((cache->val_type==RV_INT) || 
+                               if (likely((cache->val_type==RV_INT) ||
                                                                (cache->c.pval.flags & PV_VAL_INT))){
                                        *i=cache->c.pval.ri;
-                               }else if (cache->val_type==RV_STR)
+                               }else if (cache->val_type==RV_STR){
+                                       s=&cache->c.pval.rs;
                                        goto rv_str;
-                               else goto error;
+                               }else if (cache->val_type==RV_NONE)
+                                       goto undef;
+                               else goto error_cache;
                        }else{
                                memset(&pval, 0, sizeof(pval));
                                if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
                                                *i=pval.ri;
                                                pv_value_destroy(&pval);
                                        }else if (likely(pval.flags & PV_VAL_STR)){
-                                               pv_value_destroy(&pval);
+                                               destroy_pval=1; /* we must pv_value_destroy() later*/
+                                               s=&pval.rs;
                                                goto rv_str;
                                        }else{
+                                               /* no PV_VAL_STR and no PV_VAL_INT => undef
+                                                  (PV_VAL_NULL) */
                                                pv_value_destroy(&pval);
-                                               goto error;
+                                               goto undef;
                                        }
                                }else{
-                                       goto error;
+                                       goto eval_error;
                                }
                        }
                        break;
                        goto error;
        }
        return 0;
+ undef:
+ eval_error: /* same as undefined */
+       /* handle undefined => result 0, return success */
+       *i=0;
+       return 0;
+ error_cache:
+       BUG("invalid cached value:cache type %d, value type %d\n",
+                       cache?cache->cache_type:0, cache?cache->val_type:0);
  rv_str:
-       /* rv is of string type => error */
-       /* ERR("string in int expression\n"); */
+       /* rv is of string type => try to convert it to int */
+       /* if "" => 0 (most likely case) */
+       if (likely(s->len==0)) *i=0;
+       else if (unlikely(str2sint(s, i)!=0)){
+               /* error converting to int => non numeric => 0 */
+               *i=0;
+       }
+       if (destroy_pval)
+               pv_value_destroy(&pval);
+       return 0;
  error:
+       *i=0;
        return -1;
  }
  
  
  /** get the string value of an rv in a tmp variable
    * *s=(str)rv
+   * if rv == undefined select, avp or pvar, return "".
+   * if an error occurs while evaluating a select, avp or pvar, behave as
+   * for the undefined case (and return success).
    * The result points either to a temporary string or inside
    * new_cache. new_cache must be non zero, initialized previously,
    * and it _must_ be rval_cache_clean(...)'ed when done.
    * @param h - script context handle
    * @param msg - sip msg
    * @param tmpv - str return value (pointer to a str struct that will be
-   *               be filled.
+   *               be filled with the conversion result)
    * @param rv   - rvalue to be converted
-   * @param cache - cached rv value (read-only)
+   * @param cache - cached rv value (read-only), can be 0
    * @param tmp_cache - used for temporary storage (so that tmpv will not
    *                 point to possible freed data), it must be non-null,
    *                 initialized and cleaned afterwards.
@@@ -796,8 -914,8 +914,8 @@@ int rval_get_tmp_str(struct run_act_ctx
                        break;
                case RV_ACTION_ST:
                        if (rv->v.action)
 -                              i=run_actions(h, rv->v.action, msg);
 -                      else 
 +                              i=(run_actions(h, rv->v.action, msg)>0);
 +                      else
                                i=0;
                        tmpv->s=int2str(i, &tmpv->len);
                        break;
                        i=run_select(tmpv, &rv->v.sel, msg);
                        if (unlikely(i!=0)){
                                if (i<0){
-                                       goto error;
-                               }else { /* i>0 */
-                                       tmpv->s="";
-                                       tmpv->len=0;
+                                       goto eval_error;
+                               }else { /* i>0  => undefined */
+                                       goto undef;
                                }
                        }
                        break;
                                }else if (cache->val_type==RV_INT){
                                        i=cache->c.avp_val.n;
                                        tmpv->s=int2str(i, &tmpv->len);
-                               }else goto error;
+                               }else if (cache->val_type==RV_NONE){
+                                       goto undef;
+                               }else goto error_cache;
                        }else{
                                r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
                                                                                        &tmp_cache->c.avp_val,
                                                i=tmp_cache->c.avp_val.n;
                                                tmpv->s=int2str(i, &tmpv->len);
                                        }
-                               }else{
-                                       goto error;
-                               }
+                               }else goto undef;
                        }
                        break;
                case RV_PVAR:
                                }else if (cache->val_type==RV_INT){
                                        i=cache->c.pval.ri;
                                        tmpv->s=int2str(i, &tmpv->len);
-                               }else goto error;
+                               }else if (cache->val_type==RV_NONE){
+                                       goto undef;
+                               }else goto error_cache;
                        }else{
                                memset(&tmp_cache->c.pval, 0, sizeof(tmp_cache->c.pval));
                                if (likely(pv_get_spec_value(msg, &rv->v.pvs,
                                                pv_value_destroy(&tmp_cache->c.pval);
                                                tmpv->s=int2str(i, &tmpv->len);
                                        }else{
+                                               /* no PV_VAL_STR and no PV_VAL_INT => undef
+                                                  (PV_VAL_NULL) */
                                                pv_value_destroy(&tmp_cache->c.pval);
-                                               goto error;
+                                               goto undef;
                                        }
                                }else{
-                                       goto error;
+                                       goto eval_error;
                                }
                        }
                        break;
                        goto error;
        }
        return 0;
+ undef:
+ eval_error: /* same as undefined */
+       /* handle undefined => result "", return success */
+       tmpv->s="";
+       tmpv->len=0;
+       return 0;
+ error_cache:
+       BUG("invalid cached value:cache type %d, value type %d\n",
+                       cache?cache->cache_type:0, cache?cache->val_type:0);
  error:
+       tmpv->s="";
+       tmpv->len=0;
        return -1;
  }
  
@@@ -1047,9 -1179,11 +1179,11 @@@ inline static int int_intop2(int* res, 
                        *res=v1 <= v2;
                        break;
                case RVE_EQ_OP:
+               case RVE_IEQ_OP:
                        *res=v1 == v2;
                        break;
                case RVE_DIFF_OP:
+               case RVE_IDIFF_OP:
                        *res=v1 != v2;
                        break;
                case RVE_CONCAT_OP:
  inline static int bool_strop2( enum rval_expr_op op, int* res,
                                                                str* s1, str* s2)
  {
-       if (s1->len!=s2->len)
-               *res= op==RVE_DIFF_OP;
-       else if (memcmp(s1->s, s2->s, s1->len)==0)
-               *res= op==RVE_EQ_OP;
-       else
-               *res= op==RVE_DIFF_OP;
+       switch(op){
+               case RVE_EQ_OP:
+               case RVE_STREQ_OP:
+                       *res= (s1->len==s2->len) && (memcmp(s1->s, s2->s, s1->len)==0);
+                       break;
+               case RVE_DIFF_OP:
+               case RVE_STRDIFF_OP:
+                       *res= (s1->len!=s2->len) || (memcmp(s1->s, s2->s, s1->len)!=0);
+                       break;
+               default:
+                       BUG("rv unsupported intop %d\n", op);
+                       return -1;
+       }
        return 0;
  }
  
@@@ -1386,9 -1527,11 +1527,11 @@@ error
  /** checks if rv is defined.
   * @param res - set to the result 1 - defined, 0 not defined
   * @return 0 on success, -1 on error
-  * Can use cached rvalues (c1).
-  * Note: a rv can be undefined if it's an undefined avp or pvar or
+  * Can use cached rvalues (cache).
+  * Note: a rv can be undefined if it's an undefined avp or pvar or select or
   * if it's NONE
+  * Note2: an error in the avp, pvar or select search is equivalent to 
+  *  undefined (and it's not reported)
   */
  inline static int rv_defined(struct run_act_ctx* h,
                                                 struct sip_msg* msg, int* res,
        avp_t* r_avp;
        int_str avp_val;
        pv_value_t pval;
+       str tmp;
        
        *res=1;
        switch(rv->type){
+               case RV_SEL:
+                       if (unlikely(cache && cache->cache_type==RV_CACHE_SELECT)){
+                               *res=(cache->val_type!=RV_NONE);
+                       }else
+                               /* run select returns 0 on success, -1 on error and >0 on 
+                                  undefined. error is considered undefined */
+                               *res=(run_select(&tmp, &rv->v.sel, msg)==0);
+                       break;
                case RV_AVP:
                        if (unlikely(cache && cache->cache_type==RV_CACHE_AVP)){
-                               if (cache->val_type==RV_NONE)
-                                       *res=0;
+                               *res=(cache->val_type!=RV_NONE);
                        }else{
                                r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
                                                                                        &avp_val, rv->v.avps.index);
                case RV_PVAR:
                        /* PV_VAL_NULL or pv_get_spec_value error => undef */
                        if (unlikely(cache && cache->cache_type==RV_CACHE_PVAR)){
-                               if (cache->val_type==RV_NONE)
-                                       *res=0;
+                               *res=(cache->val_type!=RV_NONE);
                        }else{
                                memset(&pval, 0, sizeof(pval));
                                if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
@@@ -1467,7 -1617,7 +1617,7 @@@ int rval_expr_eval_int( struct run_act_
                                                int* res, struct rval_expr* rve)
  {
        int i1, i2, ret;
-       struct rval_cache c1;
+       struct rval_cache c1, c2;
        struct rvalue* rv1;
        struct rvalue* rv2;
        
                case RVE_GTE_OP:
                case RVE_LT_OP:
                case RVE_LTE_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                        if (unlikely(
                                        (ret=rval_expr_eval_int(h, msg, &i1, rve->left.rve)) <0) )
                                break;
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
                        /* if left is string, eval left & right as string and
-                          use string diff, else eval as int */
+                        *   use string diff.
+                        * if left is int eval as int using int diff
+                        * if left is undef, look at right and convert to right type
+                        */
                        rval_cache_init(&c1);
                        if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv1, &i1,
                                                                                                        rve->left.rve, &c1))<0)){
+                               /* error */
                                rval_cache_clean(&c1);
                                break;
                        }
                                rval_cache_clean(&c1);
                                if (unlikely( (ret=rval_expr_eval_int(h, msg, &i2,
                                                                                                                rve->right.rve)) <0) )
-                                       break;
+                                       break;  /* error */
                                ret=int_intop2(res, rve->op, i1, i2);
                        }else{
-                               if (unlikely((rv2=rval_expr_eval(h, msg,
-                                                                                                       rve->right.rve))==0)){
+                               /* not int => str or undef */
+                               /* check for undefined left operand */
+                               if (unlikely( c1.cache_type!=RV_CACHE_EMPTY &&
+                                                               c1.val_type==RV_NONE)){
+ #ifdef UNDEF_EQ_ALWAYS_FALSE
+                                       /* undef == something  always false
+                                          undef != something  always true*/
+                                       ret=(rve->op==RVE_DIFF_OP);
+ #elif defined UNDEF_EQ_UNDEF_TRUE
+                                       /* undef == something defined always false
+                                          undef == undef true */
+                                       if (int_rve_defined(h, msg, &i2, rve->right.rve)<0){
+                                               /* error */
+                                               rval_cache_clean(&c1);
+                                               rval_destroy(rv1);
+                                               break;
+                                       }
+                                       ret=(!i2) ^ (rve->op==RVE_DIFF_OP);
+ #else  /* ! UNDEF_EQ_* */
+                                       /*  undef == val
+                                        *  => convert to (type_of(val)) (undef) == val */
+                                       rval_cache_init(&c2);
+                                       if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv2, &i2,
+                                                                                                       rve->right.rve, &c2))<0)){
+                                               /* error */
+                                               rval_cache_clean(&c1);
+                                               rval_cache_clean(&c2);
+                                               rval_destroy(rv1);
+                                               break;
+                                       }
+                                       if (rv2==0){
+                                               /* int */
+                                               ret=int_intop2(res, rve->op, 0 /* undef */, i2);
+                                       }else{
+                                               /* str or undef */
+                                               ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1,
+                                                                                       rv2, &c2);
+                                               rval_cache_clean(&c2);
+                                               rval_destroy(rv2);
+                                       }
+ #endif /* UNDEF_EQ_* */
+                                       rval_cache_clean(&c1);
                                        rval_destroy(rv1);
+                               }else{ 
+                                       /* left value == defined and != int => str
+                                        * => lval == (str) val */
+                                       if (unlikely((rv2=rval_expr_eval(h, msg,
+                                                                                                               rve->right.rve))==0)){
+                                               /* error */
+                                               rval_destroy(rv1);
+                                               rval_cache_clean(&c1);
+                                               break;
+                                       }
+                                       ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1, rv2, 0);
                                        rval_cache_clean(&c1);
-                                       ret=-1;
-                                       break;
+                                       rval_destroy(rv1);
+                                       rval_destroy(rv2);
                                }
-                               ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1, rv2, 0);
-                               rval_cache_clean(&c1);
-                               rval_destroy(rv1);
-                               rval_destroy(rv2);
                        }
                        break;
  #if 0
                case RVE_DEFINED_OP:
                        ret=int_rve_defined(h, msg, res, rve->left.rve);
                        break;
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
+                       if (unlikely((rv1=rval_expr_eval(h, msg, rve->left.rve))==0)){
+                               ret=-1;
+                               break;
+                       }
+                       if (unlikely((rv2=rval_expr_eval(h, msg, rve->right.rve))==0)){
+                               rval_destroy(rv1);
+                               ret=-1;
+                               break;
+                       }
+                       ret=rval_str_lop2(h, msg, res, rve->op, rv1, 0, rv2, 0);
+                       rval_destroy(rv1);
+                       rval_destroy(rv2);
+                       break;
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                        if (unlikely((rv1=rval_expr_eval(h, msg, rve->left.rve))==0)){
@@@ -1666,7 -1884,11 +1884,11 @@@ int rval_expr_eval_rvint(                        struct r
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                case RVE_IPLUS_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                case RVE_DEFINED_OP:
@@@ -1764,7 -1986,11 +1986,11 @@@ struct rvalue* rval_expr_eval(struct ru
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
                case RVE_IPLUS_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_STRLEN_OP:
                case RVE_STREMPTY_OP:
                case RVE_DEFINED_OP:
                                        }
                                        break;
                                case RV_STR:
+                               case RV_NONE:
                                        rv2=rval_expr_eval(h, msg, rve->right.rve);
                                        if (unlikely(rv2==0)){
                                                ERR("rval expression evaluation failed\n");
                                        break;
                                default:
                                        BUG("rv unsupported basic type %d\n", type);
-                               case RV_NONE:
-                                       rval_cache_clean(&c1);
-                                       goto error;
                        }
                        rval_cache_clean(&c1);
                        break;
@@@ -2019,6 -2243,10 +2243,10 @@@ struct rval_expr* mk_rval_expr2(enum rv
                case RVE_IPLUS_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_CONCAT_OP:
                        break;
                default:
@@@ -2056,6 -2284,9 +2284,9 @@@ static int rve_op_is_assoc(enum rval_ex
                case RVE_MINUS_OP:
                        return 0;
                case RVE_PLUS_OP:
+                       /* the generic plus is not assoc, e.g.
+                          "a" + 1 + "2" => "a12" in one case and "a3" in the other */
+                       return 0;
                case RVE_IPLUS_OP:
                case RVE_CONCAT_OP:
                case RVE_MUL_OP:
                case RVE_LTE_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                        return 0;
        }
        return 0;
  
  
  /** returns true if the operator is commutative. */
- static int rve_op_is_commutative(enum rval_expr_op op, enum rval_type type)
+ static int rve_op_is_commutative(enum rval_expr_op op)
  {
        switch(op){
                case RVE_NONE_OP:
                case RVE_MINUS_OP:
                        return 0;
                case RVE_PLUS_OP:
-                       return type==RV_INT; /* commutative only for INT*/
+                       /* non commut. when diff. type 
+                          (e.g 1 + "2" != "2" + 1 ) => non commut. in general
+                          (specific same type versions are covered by IPLUS & CONCAT) */
+                       return 0;
                case RVE_IPLUS_OP:
                case RVE_MUL_OP:
                case RVE_BAND_OP:
                case RVE_BOR_OP:
-                       return 1;
                case RVE_LAND_OP:
                case RVE_LOR_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                        return 1;
                case RVE_GT_OP:
                case RVE_GTE_OP:
                case RVE_LT_OP:
                case RVE_LTE_OP:
-               case RVE_EQ_OP:
-               case RVE_DIFF_OP:
                case RVE_CONCAT_OP:
                        return 0;
+               case RVE_DIFF_OP:
+               case RVE_EQ_OP:
+                       /* non. commut. in general, only for same type e.g.:
+                          "" == 0  diff. 0 == "" ( "" == "0" and 0 == 0)
+                          same type versions are covered by IEQ, IDIFF, STREQ, STRDIFF
+                        */
+                       return 0 /* asymmetrical undef handling */;
        }
        return 0;
  }
@@@ -2421,8 -2667,19 +2667,19 @@@ static int rve_opt_01(struct rval_expr
                        case RVE_PLUS_OP:
                        case RVE_IPLUS_OP:
                                /* we must make sure that this is an int PLUS
-                                  (because "foo"+0 is valid => "foo0") */
-                               if ((i==0) && ((op==RVE_IPLUS_OP)||(rve_type==RV_INT))){
+                                  (because "foo"+0 is valid => "foo0")
+                                 Even if overall type is RV_INT, it's still not safe
+                                 to optimize a generic PLUS: 0 + $v is not always equivalent
+                                 to $v (e.g. 0+"1" ==1 != "1") => optimize only if
+                                 IPLUS (always safe since it converts to int first) or
+                                 if generic PLUS and result is integer (rve_type) and
+                                 expression is of the form $v+ct (and not ct+$v).
+                                 TODO: dropping PLUS_OP all together and relying on the
+                                  optimizer replacing safe PLUS_OP with IPLUS_OP or CONCAT_OP
+                                  will simplify things.
+                                */
+                               if ((i==0) && ((op==RVE_IPLUS_OP)||
+                                                       (rve_type==RV_INT && ct_rve==rve->right.rve))){
                                        /* $v +  0 -> $v
                                         *  0 + $v -> $v */
                                        rve_destroy(ct_rve);
                                }
                                break;
                        case RVE_EQ_OP:
+                       case RVE_STREQ_OP:
                                if (rv->v.s.len==0){
                                        /* $v == "" -> strempty($v) 
                                           "" == $v -> strempty ($v) */
@@@ -2600,6 -2858,25 +2858,25 @@@ static int rve_optimize(struct rval_exp
                                                rve->fpos.e_line, rve->fpos.e_col);
                        }
                }
+               /* e1 EQ_OP e2 -> change op if we know e1 basic type
+                  e1 DIFF_OP e2 -> change op if we know e2 basic type */
+               if (rve->op==RVE_EQ_OP || rve->op==RVE_DIFF_OP){
+                       l_type=rve_guess_type(rve->left.rve);
+                       if (l_type==RV_INT){
+                               rve->op=(rve->op==RVE_EQ_OP)?RVE_IEQ_OP:RVE_IDIFF_OP;
+                               DBG("FIXUP RVE (%d,%d-%d,%d): changed ==/!= into interger"
+                                               " ==/!=\n",
+                                               rve->fpos.s_line, rve->fpos.s_col,
+                                               rve->fpos.e_line, rve->fpos.e_col);
+                       }else if (l_type==RV_STR){
+                               rve->op=RVE_CONCAT_OP;
+                               rve->op=(rve->op==RVE_EQ_OP)?RVE_STREQ_OP:RVE_STRDIFF_OP;
+                               DBG("FIXUP RVE (%d,%d-%d,%d): changed ==/!= into string"
+                                               " ==/!=\n",
+                                               rve->fpos.s_line, rve->fpos.s_col,
+                                               rve->fpos.e_line, rve->fpos.e_col);
+                       }
+               }
                
                /* $v * 0 => 0; $v * 1 => $v (for *, /, &, |, &&, ||, +, -) */
                if (rve_opt_01(rve, type)==1){
                                                                trv->v.s.len, trv->v.s.s, rve->op);
                                        ret=1;
                                }else if (rve_is_constant(rve->left.rve->left.rve) &&
-                                                       rve_op_is_commutative(rve->op, type)){
+                                                       rve_op_is_commutative(rve->op)){
                                        /* op(op(a, $v), b) => op(op(a, b), $v) */
                                        /* rv= op(a, b) */
                                        tmp_rve.op=rve->op;
                        if ((rve->op==rve->right.rve->op) && rve_op_is_assoc(rve->op)){
                                /* op(a, op(...)) */
                                if (rve_is_constant(rve->right.rve->right.rve) &&
-                                               rve_op_is_commutative(rve->op, type)){
+                                               rve_op_is_commutative(rve->op)){
                                        /* op(a, op($v, b)) => op(op(a, b), $v) */
                                        /* rv= op(a, b) */
                                        tmp_rve.op=rve->op;
@@@ -2824,6 -3101,10 +3101,10 @@@ int fix_rval_expr(void** p
                case RVE_IPLUS_OP:
                case RVE_EQ_OP:
                case RVE_DIFF_OP:
+               case RVE_IEQ_OP:
+               case RVE_IDIFF_OP:
+               case RVE_STREQ_OP:
+               case RVE_STRDIFF_OP:
                case RVE_CONCAT_OP:
                        ret=fix_rval_expr((void**)&rve->left.rve);
                        if (ret<0) return ret;