Add ZOOM_query_cql2rpn()
[yaz-moved-to-github.git] / src / zoom-c.c
index 9ea9ffd..cd13a01 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1995-2005, Index Data ApS
  * See the file LICENSE for details.
  *
  * Copyright (C) 1995-2005, Index Data ApS
  * See the file LICENSE for details.
  *
- * $Id: zoom-c.c,v 1.57 2005-12-19 20:19:29 adam Exp $
+ * $Id: zoom-c.c,v 1.62 2005-12-21 16:41:36 mike Exp $
  */
 /**
  * \file zoom-c.c
  */
 /**
  * \file zoom-c.c
@@ -11,6 +11,7 @@
 
 #include <assert.h>
 #include <string.h>
 
 #include <assert.h>
 #include <string.h>
+#include <errno.h>
 #include "zoom-p.h"
 
 #include <yaz/yaz-util.h>
 #include "zoom-p.h"
 
 #include <yaz/yaz-util.h>
@@ -23,6 +24,7 @@
 #include <yaz/charneg.h>
 #include <yaz/ill.h>
 #include <yaz/srw.h>
 #include <yaz/charneg.h>
 #include <yaz/ill.h>
 #include <yaz/srw.h>
+#include <yaz/cql.h>
 
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -50,6 +52,8 @@ typedef enum {
 
 static zoom_ret ZOOM_connection_send_init (ZOOM_connection c);
 static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out);
 
 static zoom_ret ZOOM_connection_send_init (ZOOM_connection c);
 static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out);
+static char *cql2pqf(ZOOM_connection c, const char *cql);
+
 
 static void initlog()
 {
 
 static void initlog()
 {
@@ -163,7 +167,13 @@ static void set_ZOOM_error (ZOOM_connection c, int error,
 
 static void clear_error (ZOOM_connection c)
 {
 
 static void clear_error (ZOOM_connection c)
 {
-
+    /*
+     * If an error is tied to an operation then it's ok to clear: for
+     * example, a diagnostic returned from a search is cleared by a
+     * subsequent search.  However, problems such as Connection Lost
+     * or Init Refused are not cleared, because they are not
+     * recoverable: doing another search doesn't help.
+     */
     switch (c->error)
     {
     case ZOOM_ERROR_CONNECT:
     switch (c->error)
     {
     case ZOOM_ERROR_CONNECT:
@@ -520,6 +530,31 @@ ZOOM_query_cql(ZOOM_query s, const char *str)
     return 0;
 }
 
     return 0;
 }
 
+/*
+ * Translate the CQL string client-side into RPN which is passed to
+ * the server.  This is useful for server's that don't themselves
+ * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
+ * only as a place to stash diagnostics if compilation fails; if this
+ * information is not needed, a null pointer may be used.
+ */
+ZOOM_API(int)
+ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
+{
+    char *rpn;
+    int ret;
+
+    yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
+    if (conn == 0)
+        conn = ZOOM_connection_create(0);
+
+    if ((rpn = cql2pqf(conn, str)) == 0)
+        return -1;
+
+    ret = ZOOM_query_prefix(s, rpn);
+    xfree(rpn);
+    return ret;
+}
+
 ZOOM_API(int)
 ZOOM_query_sortby(ZOOM_query s, const char *criteria)
 {
 ZOOM_API(int)
 ZOOM_query_sortby(ZOOM_query s, const char *criteria)
 {
@@ -1094,7 +1129,7 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
         ZOOM_options_get(c->options, "implementationName"),
         odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
 
         ZOOM_options_get(c->options, "implementationName"),
         odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
 
-    version = odr_strdup(c->odr_out, "$Revision: 1.57 $");
+    version = odr_strdup(c->odr_out, "$Revision: 1.62 $");
     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
         version[strlen(version)-2] = '\0';
     ireq->implementationVersion = odr_prepend(c->odr_out,
     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
         version[strlen(version)-2] = '\0';
     ireq->implementationVersion = odr_prepend(c->odr_out,
@@ -2414,6 +2449,7 @@ ZOOM_connection_scan1 (ZOOM_connection c, ZOOM_query q)
 {
     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
     char *start;
 {
     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
     char *start;
+    char *freeme = 0;
 
     scan->connection = c;
     scan->odr = odr_createmem (ODR_DECODE);
 
     scan->connection = c;
     scan->odr = odr_createmem (ODR_DECODE);
@@ -2436,17 +2472,19 @@ ZOOM_connection_scan1 (ZOOM_connection c, ZOOM_query q)
     } else if (q->z_query->which == Z_Query_type_104) {
         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
                 c, q, q->query_string);
     } else if (q->z_query->which == Z_Query_type_104) {
         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
                 c, q, q->query_string);
-        /* Not yet supported */
-        abort();
+        start = freeme = cql2pqf(c, q->query_string);
+        if (start == 0)
+            return 0;
     } else {
         yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
                 c, q, q->query_string);
         abort();
     }
 
     } else {
         yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
                 c, q, q->query_string);
         abort();
     }
 
-    if ((scan->termListAndStartPoint =
-         p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
-                      start)) != 0)
+    scan->termListAndStartPoint =
+        p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
+    xfree (freeme);
+    if (scan->termListAndStartPoint != 0)
     {
         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
         task->u.scan.scan = scan;
     {
         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
         task->u.scan.scan = scan;
@@ -3698,6 +3736,10 @@ ZOOM_diag_str (int error)
         return "Unsupported query type";
     case ZOOM_ERROR_INVALID_QUERY:
         return "Invalid query";
         return "Unsupported query type";
     case ZOOM_ERROR_INVALID_QUERY:
         return "Invalid query";
+    case ZOOM_ERROR_CQL_PARSE:
+        return "CQL parsing error";
+    case ZOOM_ERROR_CQL_TRANSFORM:
+        return "CQL transformation error";
     default:
         return diagbib1_str (error);
     }
     default:
         return diagbib1_str (error);
     }
@@ -3991,6 +4033,69 @@ ZOOM_event (int no, ZOOM_connection *cs)
     }
     return 0;
 }
     }
     return 0;
 }
+
+
+/*
+ * Returns an xmalloc()d string containing RPN that corresponds to the
+ * CQL passed in.  On error, sets the Connection object's error state
+ * and returns a null pointer.
+ * ### We could cache CQL parser and/or transformer in Connection.
+ */
+static char *cql2pqf(ZOOM_connection c, const char *cql)
+{
+    CQL_parser parser;
+    int error;
+    struct cql_node *node;
+    const char *cqlfile;
+    static cql_transform_t trans;
+    char pqfbuf[512];
+
+    parser = cql_parser_create();
+    if ((error = cql_parser_string(parser, cql)) != 0) {
+        cql_parser_destroy(parser);
+        set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
+        return 0;
+    }
+
+    node = cql_parser_result(parser);
+    /* ### Do not call cql_parser_destroy() yet: it destroys `node'! */
+
+    cqlfile = ZOOM_connection_option_get(c, "cqlfile");
+    if (cqlfile == 0) {
+        cql_parser_destroy(parser);
+        cql_node_destroy(node);
+        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
+        return 0;
+    }
+
+    if ((trans = cql_transform_open_fname(cqlfile)) == 0) {
+        char buf[512];        
+        cql_parser_destroy(parser);
+        cql_node_destroy(node);
+        sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
+                cqlfile, strerror(errno));
+        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+        return 0;
+    }
+
+    error = cql_transform_buf(trans, node, pqfbuf, sizeof pqfbuf);
+    cql_parser_destroy(parser);
+    cql_node_destroy(node);
+    if (error != 0) {
+        char buf[512];
+        const char *addinfo;
+        error = cql_transform_error(trans, &addinfo);
+        cql_transform_close(trans);
+        sprintf(buf, "%.200s (addinfo=%.200s)", cql_strerror(error), addinfo);
+        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+        return 0;
+    }
+
+    cql_transform_close(trans);
+    return xstrdup(pqfbuf);
+}
+
+
 /*
  * Local variables:
  * c-basic-offset: 4
 /*
  * Local variables:
  * c-basic-offset: 4