Added debug logging to verify ZOOM_EVENT_RECV_SEARCH
[yaz-moved-to-github.git] / src / zoom-query.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file zoom-query.c
7  * \brief Implements ZOOM C query interface.
8  */
9
10 #include <assert.h>
11 #include <string.h>
12 #include <errno.h>
13 #include "zoom-p.h"
14
15 #include <yaz/yaz-util.h>
16 #include <yaz/xmalloc.h>
17 #include <yaz/log.h>
18 #include <yaz/pquery.h>
19 #include <yaz/cql.h>
20 #include <yaz/ccl.h>
21 #include <yaz/sortspec.h>
22
23 struct ZOOM_query_p {
24     Z_Query *z_query;
25     Z_SortKeySpecList *sort_spec;
26     int refcount;
27     ODR odr;
28     char *query_string;
29 };
30
31 Z_Query *ZOOM_query_get_Z_Query(ZOOM_query s)
32 {
33     return s->z_query;
34 }
35
36
37 Z_SortKeySpecList *ZOOM_query_get_sortspec(ZOOM_query s)
38 {
39     return s->sort_spec;
40 }
41
42 static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
43 {
44     WRBUF wrbuf = (WRBUF) client_data;
45     wrbuf_puts(wrbuf, buf);
46 }
47
48 char *ZOOM_query_get_query_string(ZOOM_query s)
49 {
50     return s->query_string;
51 }
52
53 /*
54  * Returns an xmalloc()d string containing RPN that corresponds to the
55  * CQL passed in.  On error, sets the Connection object's error state
56  * and returns a null pointer.
57  * ### We could cache CQL parser and/or transformer in Connection.
58  */
59 static char *cql2pqf(ZOOM_connection c, const char *cql)
60 {
61     CQL_parser parser;
62     int error;
63     const char *cqlfile;
64     cql_transform_t trans;
65     char *result = 0;
66
67     parser = cql_parser_create();
68     if ((error = cql_parser_string(parser, cql)) != 0) {
69         cql_parser_destroy(parser);
70         ZOOM_set_error(c, ZOOM_ERROR_CQL_PARSE, cql);
71         return 0;
72     }
73
74     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
75     if (cqlfile == 0) 
76     {
77         ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
78     }
79     else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
80     {
81         char buf[512];        
82         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
83                 cqlfile, strerror(errno));
84         ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
85     }
86     else 
87     {
88         WRBUF wrbuf_result = wrbuf_alloc();
89         error = cql_transform(trans, cql_parser_result(parser),
90                               cql2pqf_wrbuf_puts, wrbuf_result);
91         if (error != 0) {
92             char buf[512];
93             const char *addinfo;
94             error = cql_transform_error(trans, &addinfo);
95             sprintf(buf, "%.200s (addinfo=%.200s)", 
96                     cql_strerror(error), addinfo);
97             ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
98         }
99         else
100         {
101             result = xstrdup(wrbuf_cstr(wrbuf_result));
102         }
103         cql_transform_close(trans);
104         wrbuf_destroy(wrbuf_result);
105     }
106     cql_parser_destroy(parser);
107     return result;
108 }
109
110
111 ZOOM_API(ZOOM_query)
112     ZOOM_query_create(void)
113 {
114     ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
115
116     s->refcount = 1;
117     s->z_query = 0;
118     s->sort_spec = 0;
119     s->odr = odr_createmem(ODR_ENCODE);
120     s->query_string = 0;
121
122     return s;
123 }
124
125 ZOOM_API(void)
126     ZOOM_query_destroy(ZOOM_query s)
127 {
128     if (!s)
129         return;
130
131     (s->refcount)--;
132     if (s->refcount == 0)
133     {
134         odr_destroy(s->odr);
135         xfree(s);
136     }
137 }
138
139 ZOOM_API(void)
140     ZOOM_query_addref(ZOOM_query s)
141 {
142     s->refcount++;
143 }
144
145 ZOOM_API(int)
146     ZOOM_query_prefix(ZOOM_query s, const char *str)
147 {
148     s->query_string = odr_strdup(s->odr, str);
149     s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
150     s->z_query->which = Z_Query_type_1;
151     s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
152     if (!s->z_query->u.type_1)
153     {
154         s->z_query = 0;
155         return -1;
156     }
157     return 0;
158 }
159
160 ZOOM_API(int)
161     ZOOM_query_cql(ZOOM_query s, const char *str)
162 {
163     Z_External *ext;
164
165     s->query_string = odr_strdup(s->odr, str);
166
167     ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
168     ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
169     ext->indirect_reference = 0;
170     ext->descriptor = 0;
171     ext->which = Z_External_CQL;
172     ext->u.cql = s->query_string;
173     
174     s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
175     s->z_query->which = Z_Query_type_104;
176     s->z_query->u.type_104 =  ext;
177
178     return 0;
179 }
180
181 /*
182  * Translate the CQL string client-side into RPN which is passed to
183  * the server.  This is useful for server's that don't themselves
184  * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
185  * only as a place to stash diagnostics if compilation fails; if this
186  * information is not needed, a null pointer may be used.
187  */
188 ZOOM_API(int)
189     ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
190 {
191     char *rpn;
192     int ret;
193     ZOOM_connection freeme = 0;
194
195     if (conn == 0)
196         conn = freeme = ZOOM_connection_create(0);
197
198     rpn = cql2pqf(conn, str);
199     if (freeme != 0)
200         ZOOM_connection_destroy(freeme);
201     if (rpn == 0)
202         return -1;
203
204     ret = ZOOM_query_prefix(s, rpn);
205     xfree(rpn);
206     return ret;
207 }
208
209 /*
210  * Analogous in every way to ZOOM_query_cql2rpn(), except that there
211  * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
212  * to the server, as the YAZ GFS doesn't know how to handle this.
213  */
214 ZOOM_API(int)
215     ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
216                        int *ccl_error, const char **error_string,
217                        int *error_pos)
218 {
219     int ret;
220     struct ccl_rpn_node *rpn;
221     CCL_bibset bibset = ccl_qual_mk();
222
223     if (config)
224         ccl_qual_buf(bibset, config);
225
226     rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
227     if (!rpn)
228     {
229         *error_string = ccl_err_msg(*ccl_error);
230         ret = -1;
231     }
232     else
233     {
234         WRBUF wr = wrbuf_alloc();
235         ccl_pquery(wr, rpn);
236         ccl_rpn_delete(rpn);
237         ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
238         wrbuf_destroy(wr);
239     }
240     ccl_qual_rm(&bibset);
241     return ret;
242 }
243
244 ZOOM_API(int)
245     ZOOM_query_sortby(ZOOM_query s, const char *criteria)
246 {
247     s->sort_spec = yaz_sort_spec(s->odr, criteria);
248     if (!s->sort_spec)
249         return -1;
250     return 0;
251 }
252
253
254 /*
255  * Local variables:
256  * c-basic-offset: 4
257  * c-file-style: "Stroustrup"
258  * indent-tabs-mode: nil
259  * End:
260  * vim: shiftwidth=4 tabstop=8 expandtab
261  */
262