Add re-entrant CQL/Solr/RPN functions
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 10 Apr 2014 12:19:32 +0000 (14:19 +0200)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 10 Apr 2014 12:19:32 +0000 (14:19 +0200)
The re-entrant functions are: cql_transform_r,
cql_transform_rpn2cql_stream_r, solr_transform_rpn2solr_stream_r.
These have same functionality as cql_transform,
cql_transform_rpn2cql_stream, solr_transform_rpn2solr_stream. They
return the additional infomration as WRBUF to make them re-entrant.

include/yaz/cql.h
include/yaz/rpn2cql.h
include/yaz/rpn2solr.h
src/cqltransform.c
src/rpn2cql.c
src/rpn2solr.c
test/test_rpn2cql.c

index f5b6ea9..f1cdd78 100644 (file)
@@ -33,6 +33,7 @@
 #define CQL_H_INCLUDED
 #include <stdio.h>
 #include <yaz/nmem.h>
 #define CQL_H_INCLUDED
 #include <stdio.h>
 #include <yaz/nmem.h>
+#include <yaz/wrbuf.h>
 
 YAZ_BEGIN_CDECL
 
 
 YAZ_BEGIN_CDECL
 
@@ -332,7 +333,7 @@ int cql_transform_define_pattern(cql_transform_t ct, const char *pattern,
 YAZ_EXPORT
 void cql_transform_close(cql_transform_t ct);
 
 YAZ_EXPORT
 void cql_transform_close(cql_transform_t ct);
 
-/** \brief tranforms PQF given a CQL tree
+/** \brief tranforms PQF given a CQL tree (NOT re-entrant)
     \param ct CQL transform handle
     \param cn CQL node tree
     \param pr print function
     \param ct CQL transform handle
     \param cn CQL node tree
     \param pr print function
@@ -348,7 +349,7 @@ int cql_transform(cql_transform_t ct,
                   void (*pr)(const char *buf, void *client_data),
                   void *client_data);
 
                   void (*pr)(const char *buf, void *client_data),
                   void *client_data);
 
-/** \brief tranforms PQF given a CQL tree
+/** \brief tranforms PQF given a CQL tree (re-entrant)
     \param ct CQL transform handle
     \param cn CQL node tree
     \param addinfo additional information (if error)
     \param ct CQL transform handle
     \param cn CQL node tree
     \param addinfo additional information (if error)
@@ -359,12 +360,12 @@ int cql_transform(cql_transform_t ct,
 
     The result is written to a user-defined stream.
 */
 
     The result is written to a user-defined stream.
 */
-int cql_transform_cql2rpn(cql_transform_t ct, struct cql_node *cn,
-                          char **addinfo,
-                          void (*pr)(const char *buf, void *client_data),
-                          void *client_data);
+int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
+                    WRBUF addinfo,
+                    void (*pr)(const char *buf, void *client_data),
+                    void *client_data);
 
 
-/** \brief transforms PQF given a CQL tree (from FILE)
+/** \brief transforms PQF given a CQL tree from FILE (not re-entrant)
     \param ct CQL transform handle
     \param cn CQL tree
     \param f FILE where output is written
     \param ct CQL transform handle
     \param cn CQL tree
     \param f FILE where output is written
@@ -378,7 +379,7 @@ YAZ_EXPORT
 int cql_transform_FILE(cql_transform_t ct,
                        struct cql_node *cn, FILE *f);
 
 int cql_transform_FILE(cql_transform_t ct,
                        struct cql_node *cn, FILE *f);
 
-/** \brief transforms PQF given a CQL tree (from FILE)
+/** \brief transforms PQF given a CQL tree from buffer (not re-entrant)
     \param ct CQL transform handle
     \param cn CQL tree
     \param out buffer for output
     \param ct CQL transform handle
     \param cn CQL tree
     \param out buffer for output
index 3c6691f..a7afd0b 100644 (file)
 
 YAZ_BEGIN_CDECL
 
 
 YAZ_BEGIN_CDECL
 
-/** \brief transforms RPN query to CQL output stream
+/** \brief transforms RPN query to CQL output stream (re-entrant)
+    \param ct CQL transform handle
+    \param addinfo for additional error info
+    \param pr print function
+    \param client_data opaque data to be passed to print handler
+    \param q RPN Query
+    \retval 0 success
+    \retval !=0 failure (error code)
+ */
+YAZ_EXPORT
+int cql_transform_rpn2cql_stream_r(cql_transform_t ct,
+                                   WRBUF addinfo,
+                                   void (*pr)(const char *buf,
+                                              void *client_data),
+                                   void *client_data,
+                                   Z_RPNQuery *q);
+
+
+/** \brief transforms RPN query to CQL output stream (NOT re-entrant)
     \param ct CQL transform handle
     \param pr print function
     \param client_data opaque data to be passed to print handler
     \param ct CQL transform handle
     \param pr print function
     \param client_data opaque data to be passed to print handler
@@ -52,7 +70,7 @@ int cql_transform_rpn2cql_stream(cql_transform_t ct,
                                  Z_RPNQuery *q);
 
 
                                  Z_RPNQuery *q);
 
 
-/** \brief transforms RPN query to CQL WRBUF
+/** \brief transforms RPN query to CQL WRBUF (NOT re-entrant)
     \param ct CQL transform handle
     \param w WRBUF handle for result
     \param q RPN Query
     \param ct CQL transform handle
     \param w WRBUF handle for result
     \param q RPN Query
@@ -60,9 +78,7 @@ int cql_transform_rpn2cql_stream(cql_transform_t ct,
     \retval !=0 failure (error code)
  */
 YAZ_EXPORT
     \retval !=0 failure (error code)
  */
 YAZ_EXPORT
-int cql_transform_rpn2cql_wrbuf(cql_transform_t ct,
-                                WRBUF w,
-                                Z_RPNQuery *q);
+int cql_transform_rpn2cql_wrbuf(cql_transform_t ct, WRBUF w, Z_RPNQuery *q);
 
 /** \brief find a pattern that has a subset of attributes
     \param ct CQL transform handle
 
 /** \brief find a pattern that has a subset of attributes
     \param ct CQL transform handle
index 42f1004..630137e 100644 (file)
 
 YAZ_BEGIN_CDECL
 
 
 YAZ_BEGIN_CDECL
 
-/** \brief transforms RPN query to SOLR output stream
+/** \brief transforms RPN query to SOLR output stream (re-entrant)
+    \param ct SOLR transform handle
+    \param addinfo additional info on error
+    \param pr print function
+    \param client_data opaque data to be passed to print handler
+    \param q RPN Query
+    \retval 0 success
+    \retval !=0 failure (error code)
+ */
+YAZ_EXPORT
+int solr_transform_rpn2solr_stream_r(solr_transform_t ct,
+                                     WRBUF addinfo,
+                                     void (*pr)(const char *buf, void *client_data),
+                                     void *client_data,
+                                     Z_RPNQuery *q);
+
+/** \brief transforms RPN query to SOLR output stream (NOT re-entrant)
     \param ct SOLR transform handle
     \param pr print function
     \param client_data opaque data to be passed to print handler
     \param ct SOLR transform handle
     \param pr print function
     \param client_data opaque data to be passed to print handler
index f560445..0ab9079 100644 (file)
@@ -382,7 +382,7 @@ static const char *cql_lookup_property(cql_transform_t ct,
     return 0;
 }
 
     return 0;
 }
 
-int cql_pr_attr_uri(cql_transform_t ct, char **addinfo, const char *category,
+int cql_pr_attr_uri(cql_transform_t ct, WRBUF addinfo, const char *category,
                    const char *uri, const char *val, const char *default_val,
                    void (*pr)(const char *buf, void *client_data),
                    void *client_data,
                    const char *uri, const char *val, const char *default_val,
                    void (*pr)(const char *buf, void *client_data),
                    void *client_data,
@@ -464,11 +464,11 @@ int cql_pr_attr_uri(cql_transform_t ct, char **addinfo, const char *category,
     if (errcode == 0)
         return 1; /* signal error, but do not set addinfo */
     if (val)
     if (errcode == 0)
         return 1; /* signal error, but do not set addinfo */
     if (val)
-        *addinfo = xstrdup(val);
+        wrbuf_puts(addinfo, val);
     return errcode;
 }
 
     return errcode;
 }
 
-int cql_pr_attr(cql_transform_t ct, char **addinfo, const char *category,
+int cql_pr_attr(cql_transform_t ct, WRBUF addinfo, const char *category,
                 const char *val, const char *default_val,
                 void (*pr)(const char *buf, void *client_data),
                 void *client_data,
                 const char *val, const char *default_val,
                 void (*pr)(const char *buf, void *client_data),
                 void *client_data,
@@ -491,7 +491,7 @@ static void cql_pr_int(int val,
 
 
 static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods,
 
 
 static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods,
-                       char **addinfo,
+                       WRBUF addinfo,
                        void (*pr)(const char *buf, void *client_data),
                        void *client_data)
 {
                        void (*pr)(const char *buf, void *client_data),
                        void *client_data)
 {
@@ -523,7 +523,7 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods,
                 proxrel = 6;
             else
             {
                 proxrel = 6;
             else
             {
-                *addinfo = xstrdup(relation);
+                wrbuf_puts(addinfo, relation);
                 return YAZ_SRW_UNSUPP_PROX_RELATION;
             }
         }
                 return YAZ_SRW_UNSUPP_PROX_RELATION;
             }
         }
@@ -543,13 +543,13 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods,
                 unit = 8;
             else
             {
                 unit = 8;
             else
             {
-                *addinfo = xstrdup(term);
+                wrbuf_puts(addinfo, term);
                 return YAZ_SRW_UNSUPP_PROX_UNIT;
             }
         }
         else
         {
                 return YAZ_SRW_UNSUPP_PROX_UNIT;
             }
         }
         else
         {
-            *addinfo = xstrdup(name);
+            wrbuf_puts(addinfo, name);
             return YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
         }
         mods = mods->u.st.modifiers;
             return YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
         }
         mods = mods->u.st.modifiers;
@@ -580,7 +580,7 @@ static int has_modifier(struct cql_node *cn, const char *name) {
 }
 
 static int emit_term(cql_transform_t ct,
 }
 
 static int emit_term(cql_transform_t ct,
-                     struct cql_node *cn, char **addinfo,
+                     struct cql_node *cn, WRBUF addinfo,
                      const char *term, int length,
                      void (*pr)(const char *buf, void *client_data),
                      void *client_data)
                      const char *term, int length,
                      void (*pr)(const char *buf, void *client_data),
                      void *client_data)
@@ -778,7 +778,7 @@ static int emit_term(cql_transform_t ct,
 }
 
 static int emit_terms(cql_transform_t ct, struct cql_node *cn,
 }
 
 static int emit_terms(cql_transform_t ct, struct cql_node *cn,
-                      char **addinfo,
+                      WRBUF addinfo,
                       void (*pr)(const char *buf, void *client_data),
                       void *client_data,
                       const char *op)
                       void (*pr)(const char *buf, void *client_data),
                       void *client_data,
                       const char *op)
@@ -808,7 +808,7 @@ static int emit_terms(cql_transform_t ct, struct cql_node *cn,
 }
 
 static int emit_wordlist(cql_transform_t ct, struct cql_node *cn,
 }
 
 static int emit_wordlist(cql_transform_t ct, struct cql_node *cn,
-                         char **addinfo,
+                         WRBUF addinfo,
                          void (*pr)(const char *buf, void *client_data),
                          void *client_data,
                          const char *op)
                          void (*pr)(const char *buf, void *client_data),
                          void *client_data,
                          const char *op)
@@ -843,10 +843,10 @@ static int emit_wordlist(cql_transform_t ct, struct cql_node *cn,
     return r;
 }
 
     return r;
 }
 
-int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
-                    char **addinfo,
-                    void (*pr)(const char *buf, void *client_data),
-                    void *client_data)
+static int emit_node(cql_transform_t ct, struct cql_node *cn,
+                     WRBUF addinfo,
+                     void (*pr)(const char *buf, void *client_data),
+                     void *client_data)
 {
     const char *ns;
     int r = 0;
 {
     const char *ns;
     int r = 0;
@@ -871,7 +871,6 @@ int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
         }
         else
         {
         }
         else
         {
-            *addinfo = 0;
             return YAZ_SRW_UNSUPP_CONTEXT_SET;
         }
         cql_pr_attr(ct, addinfo, "always", 0, 0, pr, client_data, 0);
             return YAZ_SRW_UNSUPP_CONTEXT_SET;
         }
         cql_pr_attr(ct, addinfo, "always", 0, 0, pr, client_data, 0);
@@ -905,19 +904,19 @@ int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
         else if (mods)
         {
             /* Boolean modifiers other than on proximity not supported */
         else if (mods)
         {
             /* Boolean modifiers other than on proximity not supported */
-            *addinfo = xstrdup(mods->u.st.index);
+            wrbuf_puts(addinfo, mods->u.st.index);
             return YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
         }
 
             return YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
         }
 
-        r = cql_transform_r(ct, cn->u.boolean.left, addinfo, pr, client_data);
+        r = emit_node(ct, cn->u.boolean.left, addinfo, pr, client_data);
         if (r)
             return r;
         if (r)
             return r;
-        r = cql_transform_r(ct, cn->u.boolean.right, addinfo, pr, client_data);
+        r = emit_node(ct, cn->u.boolean.right, addinfo, pr, client_data);
         if (r)
             return r;
         break;
     case CQL_NODE_SORT:
         if (r)
             return r;
         break;
     case CQL_NODE_SORT:
-        r = cql_transform_r(ct, cn->u.sort.search, addinfo, pr, client_data);
+        r = emit_node(ct, cn->u.sort.search, addinfo, pr, client_data);
         break;
     default:
         fprintf(stderr, "Fatal: impossible CQL node-type %d\n", cn->which);
         break;
     default:
         fprintf(stderr, "Fatal: impossible CQL node-type %d\n", cn->which);
@@ -926,10 +925,10 @@ int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
     return r;
 }
 
     return r;
 }
 
-int cql_transform_cql2rpn(cql_transform_t ct, struct cql_node *cn,
-                          char **addinfo,
-                          void (*pr)(const char *buf, void *client_data),
-                          void *client_data)
+int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
+                    WRBUF addinfo,
+                    void (*pr)(const char *buf, void *client_data),
+                    void *client_data)
 {
     struct cql_prop_entry *e;
     NMEM nmem = nmem_create();
 {
     struct cql_prop_entry *e;
     NMEM nmem = nmem_create();
@@ -942,7 +941,7 @@ int cql_transform_cql2rpn(cql_transform_t ct, struct cql_node *cn,
         else if (!cql_strcmp(e->pattern, "set"))
             cql_apply_prefix(nmem, cn, 0, e->value);
     }
         else if (!cql_strcmp(e->pattern, "set"))
             cql_apply_prefix(nmem, cn, 0, e->value);
     }
-    r  = cql_transform_r(ct, cn, addinfo, pr, client_data);
+    r  = emit_node(ct, cn, addinfo, pr, client_data);
     nmem_destroy(nmem);
     return r;
 }
     nmem_destroy(nmem);
     return r;
 }
@@ -951,10 +950,10 @@ int cql_transform(cql_transform_t ct, struct cql_node *cn,
                   void (*pr)(const char *buf, void *client_data),
                   void *client_data)
 {
                   void (*pr)(const char *buf, void *client_data),
                   void *client_data)
 {
-    char *addinfo = 0;
-    int r = cql_transform_cql2rpn(ct, cn, &addinfo, pr, client_data);
-    cql_transform_set_error(ct, r, addinfo);
-    xfree(addinfo);
+    WRBUF addinfo = wrbuf_alloc();
+    int r = cql_transform_r(ct, cn, addinfo, pr, client_data);
+    cql_transform_set_error(ct, r, wrbuf_cstr(addinfo));
+    wrbuf_destroy(addinfo);
     return r;
 }
 
     return r;
 }
 
index 7763db3..842d1a9 100644 (file)
@@ -116,7 +116,10 @@ static int rpn2cql_attr(cql_transform_t ct,
         relation = lookup_relation_index_from_attr(attributes);
 
     if (!index)
         relation = lookup_relation_index_from_attr(attributes);
 
     if (!index)
+    {
+        wrbuf_rewind(w);
         return YAZ_BIB1_UNSUPP_USE_ATTRIBUTE;
         return YAZ_BIB1_UNSUPP_USE_ATTRIBUTE;
+    }
     /* for serverChoice we omit index+relation+structure */
     if (strcmp(index, "cql.serverChoice"))
     {
     /* for serverChoice we omit index+relation+structure */
     if (strcmp(index, "cql.serverChoice"))
     {
@@ -172,7 +175,10 @@ static int rpn2cql_simple(cql_transform_t ct,
                           Z_Operand *q, WRBUF w)
 {
     if (q->which != Z_Operand_APT)
                           Z_Operand *q, WRBUF w)
 {
     if (q->which != Z_Operand_APT)
+    {
+        wrbuf_rewind(w);
         return YAZ_BIB1_RESULT_SET_UNSUPP_AS_A_SEARCH_TERM;
         return YAZ_BIB1_RESULT_SET_UNSUPP_AS_A_SEARCH_TERM;
+    }
     else
     {
         Z_AttributesPlusTerm *apt = q->u.attributesPlusTerm;
     else
     {
         Z_AttributesPlusTerm *apt = q->u.attributesPlusTerm;
@@ -202,6 +208,8 @@ static int rpn2cql_simple(cql_transform_t ct,
             lterm = strlen(sterm);
             break;
         default:
             lterm = strlen(sterm);
             break;
         default:
+            wrbuf_rewind(w);
+            wrbuf_printf(w, "%d", term->which);
             return YAZ_BIB1_TERM_TYPE_UNSUPP;
         }
 
             return YAZ_BIB1_TERM_TYPE_UNSUPP;
         }
 
@@ -255,6 +263,8 @@ static int rpn2cql_simple(cql_transform_t ct,
         }
         else
         {
         }
         else
         {
+            wrbuf_rewind(w);
+            wrbuf_printf(w, ODR_INT_PRINTF, trunc);
             return YAZ_BIB1_UNSUPP_TRUNCATION_ATTRIBUTE;
         }
         pr(wrbuf_cstr(w), client_data);
             return YAZ_BIB1_UNSUPP_TRUNCATION_ATTRIBUTE;
         }
         pr(wrbuf_cstr(w), client_data);
@@ -306,6 +316,7 @@ static int rpn2cql_structure(cql_transform_t ct,
                     *prox->relationType < Z_ProximityOperator_Prox_lessThan ||
                     *prox->relationType > Z_ProximityOperator_Prox_notEqual)
                 {
                     *prox->relationType < Z_ProximityOperator_Prox_lessThan ||
                     *prox->relationType > Z_ProximityOperator_Prox_notEqual)
                 {
+                    wrbuf_rewind(w);
                     return YAZ_BIB1_UNSUPP_SEARCH;
                 }
                 pr(op2name[*prox->relationType-1], client_data);
                     return YAZ_BIB1_UNSUPP_SEARCH;
                 }
                 pr(op2name[*prox->relationType-1], client_data);
@@ -335,16 +346,29 @@ static int rpn2cql_structure(cql_transform_t ct,
     }
 }
 
     }
 }
 
+int cql_transform_rpn2cql_stream_r(cql_transform_t ct,
+                                   WRBUF addinfo,
+                                   void (*pr)(const char *buf, void *client_data),
+                                   void *client_data,
+                                   Z_RPNQuery *q)
+{
+    /* addinfo (w) is used for both addinfo and house-keeping ! */
+    int r = rpn2cql_structure(ct, pr, client_data, q->RPNStructure, 0, addinfo);
+    if (!r)
+        wrbuf_rewind(addinfo); /* no additional info if no error */
+    return r;
+}
+
+
 int cql_transform_rpn2cql_stream(cql_transform_t ct,
                                  void (*pr)(const char *buf, void *client_data),
                                  void *client_data,
                                  Z_RPNQuery *q)
 {
 int cql_transform_rpn2cql_stream(cql_transform_t ct,
                                  void (*pr)(const char *buf, void *client_data),
                                  void *client_data,
                                  Z_RPNQuery *q)
 {
-    int r;
     WRBUF w = wrbuf_alloc();
     WRBUF w = wrbuf_alloc();
-    r = rpn2cql_structure(ct, pr, client_data, q->RPNStructure, 0, w);
+    int r = cql_transform_rpn2cql_stream_r(ct, w, pr, client_data, q);
     if (r)
     if (r)
-        cql_transform_set_error(ct, r, 0);
+        cql_transform_set_error(ct, r, wrbuf_len(w) ? wrbuf_cstr(w) : 0);
     wrbuf_destroy(w);
     return r;
 }
     wrbuf_destroy(w);
     return r;
 }
index 332ae88..f790ea2 100644 (file)
@@ -393,21 +393,32 @@ static int rpn2solr_structure(solr_transform_t ct,
     }
 }
 
     }
 }
 
+int solr_transform_rpn2solr_stream_r(solr_transform_t ct,
+                                     WRBUF addinfo,
+                                     void (*pr)(const char *buf, void *client_data),
+                                     void *client_data,
+                                     Z_RPNQuery *q)
+{
+    int r = rpn2solr_structure(ct, pr, client_data, q->RPNStructure,
+                               /* nested*/ 0, addinfo);
+    if (!r)
+        wrbuf_rewind(addinfo);
+    return r;
+}
+
 int solr_transform_rpn2solr_stream(solr_transform_t ct,
                                    void (*pr)(const char *buf, void *client_data),
                                    void *client_data,
                                    Z_RPNQuery *q)
 {
 int solr_transform_rpn2solr_stream(solr_transform_t ct,
                                    void (*pr)(const char *buf, void *client_data),
                                    void *client_data,
                                    Z_RPNQuery *q)
 {
-    int r;
     WRBUF w = wrbuf_alloc();
     WRBUF w = wrbuf_alloc();
-    r = rpn2solr_structure(ct, pr, client_data, q->RPNStructure, 0, w);
+    int r = solr_transform_rpn2solr_stream_r(ct, w, pr, client_data, q);
     if (r)
     if (r)
-        solr_transform_set_error(ct, r, 0);
+        solr_transform_set_error(ct, r, wrbuf_len(w) ? wrbuf_cstr(w) : 0);
     wrbuf_destroy(w);
     return r;
 }
 
     wrbuf_destroy(w);
     return r;
 }
 
-
 int solr_transform_rpn2solr_wrbuf(solr_transform_t ct,
                                   WRBUF w,
                                   Z_RPNQuery *q)
 int solr_transform_rpn2solr_wrbuf(solr_transform_t ct,
                                   WRBUF w,
                                   Z_RPNQuery *q)
index 0d929e5..0d05fab 100644 (file)
@@ -15,7 +15,8 @@
 #include <yaz/wrbuf.h>
 #include <yaz/pquery.h>
 
 #include <yaz/wrbuf.h>
 #include <yaz/pquery.h>
 
-static int compare(cql_transform_t ct, const char *pqf, const char *cql)
+static int compare2(cql_transform_t ct, const char *pqf, const char *cql,
+                    int expected_error)
 {
     int ret = 0;
     ODR odr = odr_createmem(ODR_ENCODE);
 {
     int ret = 0;
     ODR odr = odr_createmem(ODR_ENCODE);
@@ -28,15 +29,26 @@ static int compare(cql_transform_t ct, const char *pqf, const char *cql)
 
         if (r != 0)
         {
 
         if (r != 0)
         {
+            const char *addinfo = 0;
+            int err = cql_transform_error(ct, &addinfo);
             /* transform error */
             yaz_log(YLOG_LOG, "%s -> Error %d", pqf, r);
             /* transform error */
             yaz_log(YLOG_LOG, "%s -> Error %d", pqf, r);
-            if (!cql) /* also expected error? */
-                ret = 1;
+            if (err == 0)
+                ;
+            else if (err == expected_error)
+            {
+                if (addinfo && cql && !strcmp(addinfo, cql))
+                    ret = 1;
+                else if (!addinfo && !cql)
+                    ret = 1;
+            }
         }
         else if (r == 0)
         {
             yaz_log(YLOG_LOG, "%s -> %s", pqf, wrbuf_cstr(w));
         }
         else if (r == 0)
         {
             yaz_log(YLOG_LOG, "%s -> %s", pqf, wrbuf_cstr(w));
-            if (cql && !strcmp(wrbuf_cstr(w), cql))
+            if (!expected_error)
+                ret = 1;
+            else if (cql && !strcmp(wrbuf_cstr(w), cql))
             {
                 ret = 1;
             }
             {
                 ret = 1;
             }
@@ -52,6 +64,11 @@ static int compare(cql_transform_t ct, const char *pqf, const char *cql)
     return ret;
 }
 
     return ret;
 }
 
+static int compare(cql_transform_t ct, const char *pqf, const char *cql)
+{
+    return compare2(ct, pqf, cql, 0);
+}
+
 static void tst1(void)
 {
     cql_transform_t ct = cql_transform_create();
 static void tst1(void)
 {
     cql_transform_t ct = cql_transform_create();
@@ -65,7 +82,7 @@ static void tst1(void)
     YAZ_CHECK(compare(ct, "@and @and a b @and c d", "(a and b) and (c and d)"));
 
     YAZ_CHECK(compare(ct, "@attr 1=field abc", "field=abc"));
     YAZ_CHECK(compare(ct, "@and @and a b @and c d", "(a and b) and (c and d)"));
 
     YAZ_CHECK(compare(ct, "@attr 1=field abc", "field=abc"));
-    YAZ_CHECK(compare(ct, "@attr 1=4 abc", 0)); /* should fail */
+    YAZ_CHECK(compare2(ct, "@attr 1=4 abc", 0, 114)); /* should fail */
 
     cql_transform_define_pattern(ct, "index.title", "1=4");
     YAZ_CHECK(compare(ct, "@attr 1=4 abc", "title=abc"));
 
     cql_transform_define_pattern(ct, "index.title", "1=4");
     YAZ_CHECK(compare(ct, "@attr 1=4 abc", "title=abc"));
@@ -151,7 +168,8 @@ static void tst2(void)
 
     /* Other */
     YAZ_CHECK(compare(ct, "@attr 2=103 @attr 1=_ALLRECORDS 1", "cql.allRecords=1"));
 
     /* Other */
     YAZ_CHECK(compare(ct, "@attr 2=103 @attr 1=_ALLRECORDS 1", "cql.allRecords=1"));
-    YAZ_CHECK(compare(ct, "@attr 1=500 abc", 0));
+    YAZ_CHECK(compare2(ct, "@attr 1=500 abc", 0, 114));
+    YAZ_CHECK(compare2(ct, "@attr 5=99 x", "99", 120));
     cql_transform_close(ct);
     wrbuf_destroy(w);
 }
     cql_transform_close(ct);
     wrbuf_destroy(w);
 }