Merge branch 'master' into sru_2_0
[yaz-moved-to-github.git] / src / zoom-query.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 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 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <assert.h>
14 #include <string.h>
15 #include <errno.h>
16 #include "zoom-p.h"
17
18 #include <yaz/yaz-util.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/log.h>
21 #include <yaz/pquery.h>
22 #include <yaz/cql.h>
23 #include <yaz/ccl.h>
24 #include <yaz/sortspec.h>
25
26 #define SORT_STRATEGY_Z3950 0
27 #define SORT_STRATEGY_TYPE7 1
28 #define SORT_STRATEGY_CQL   2
29 #define SORT_STRATEGY_SRU11 3
30 #define SORT_STRATEGY_EMBED 4
31 #define SORT_STRATEGY_SOLR  5
32
33 struct ZOOM_query_p {
34     Z_Query *z_query;
35     int sort_strategy;
36     Z_SortKeySpecList *sort_spec;
37     int refcount;
38     ODR odr_sort_spec;
39     ODR odr_query;
40     int query_type;
41     char *query_string;
42     WRBUF full_query;
43     WRBUF sru11_sort_spec;
44 };
45
46 static int generate(ZOOM_query s)
47 {
48     if (s->query_string)
49     {
50         Z_External *ext;
51
52         wrbuf_rewind(s->full_query);
53         wrbuf_puts(s->full_query, s->query_string);
54         odr_reset(s->odr_query);
55
56         if (s->sort_spec && (s->sort_strategy == SORT_STRATEGY_SRU11 || s->sort_strategy == SORT_STRATEGY_SOLR))
57         {
58             int r = 0;
59             wrbuf_rewind(s->sru11_sort_spec);
60
61             switch (s->sort_strategy)
62             {
63             case SORT_STRATEGY_SRU11:
64                 r = yaz_sort_spec_to_srw_sortkeys(s->sort_spec, s->sru11_sort_spec);
65                 break;
66             case SORT_STRATEGY_SOLR:
67                 r = yaz_sort_spec_to_solr_sortkeys(s->sort_spec, s->sru11_sort_spec);
68                 break;
69             }
70             if (r)
71                 return r;
72         }
73         switch (s->query_type)
74         {
75         case Z_Query_type_1: /* RPN */
76             if (s->sort_spec &&
77                 (s->sort_strategy == SORT_STRATEGY_TYPE7 ||
78                  s->sort_strategy == SORT_STRATEGY_EMBED))
79             {
80                 int r = yaz_sort_spec_to_type7(s->sort_spec, s->full_query);
81                 if (r)
82                     return r;
83             }
84             s->z_query = (Z_Query *) odr_malloc(s->odr_query,
85                                                 sizeof(*s->z_query));
86             s->z_query->which = Z_Query_type_1;
87             s->z_query->u.type_1 =
88                 p_query_rpn(s->odr_query, wrbuf_cstr(s->full_query));
89             if (!s->z_query->u.type_1)
90             {
91                 s->z_query = 0;
92                 return -1;
93             }
94             break;
95         case Z_Query_type_104: /* CQL */
96             if (s->sort_spec &&
97                 (s->sort_strategy == SORT_STRATEGY_CQL ||
98                  s->sort_strategy == SORT_STRATEGY_EMBED))
99             {
100                 int r = yaz_sort_spec_to_cql(s->sort_spec, s->full_query);
101                 if (r)
102                     return r;
103             }
104             ext = (Z_External *) odr_malloc(s->odr_query, sizeof(*ext));
105             ext->direct_reference = odr_oiddup(s->odr_query,
106                                                yaz_oid_userinfo_cql);
107             ext->indirect_reference = 0;
108             ext->descriptor = 0;
109             ext->which = Z_External_CQL;
110             ext->u.cql = odr_strdup(s->odr_query, wrbuf_cstr(s->full_query));
111
112             s->z_query = (Z_Query *) odr_malloc(s->odr_query, sizeof(*s->z_query));
113             s->z_query->which = Z_Query_type_104;
114             s->z_query->u.type_104 =  ext;
115
116             break;
117         }
118     }
119     return 0;
120 }
121
122 const char *ZOOM_query_get_sru11(ZOOM_query s)
123 {
124     if (wrbuf_len(s->sru11_sort_spec))
125         return wrbuf_cstr(s->sru11_sort_spec);
126     return 0;
127 }
128
129 Z_Query *ZOOM_query_get_Z_Query(ZOOM_query s)
130 {
131     return s->z_query;
132 }
133
134 Z_SortKeySpecList *ZOOM_query_get_sortspec(ZOOM_query s)
135 {
136     return s->sort_strategy == SORT_STRATEGY_Z3950 ? s->sort_spec : 0;
137 }
138
139 static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
140 {
141     WRBUF wrbuf = (WRBUF) client_data;
142     wrbuf_puts(wrbuf, buf);
143 }
144
145 const char *ZOOM_query_get_query_string(ZOOM_query s)
146 {
147     return wrbuf_cstr(s->full_query);
148 }
149
150 /*
151  * Returns an xmalloc()d string containing RPN that corresponds to the
152  * CQL passed in.  On error, sets the Connection object's error state
153  * and returns a null pointer.
154  * ### We could cache CQL parser and/or transformer in Connection.
155  */
156 static char *cql2pqf(ZOOM_connection c, const char *cql)
157 {
158     CQL_parser parser;
159     int error;
160     const char *cqlfile;
161     cql_transform_t trans;
162     char *result = 0;
163
164     parser = cql_parser_create();
165     if ((error = cql_parser_string(parser, cql)) != 0) {
166         cql_parser_destroy(parser);
167         ZOOM_set_error(c, ZOOM_ERROR_CQL_PARSE, cql);
168         return 0;
169     }
170
171     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
172     if (cqlfile == 0)
173     {
174         ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
175     }
176     else if ((trans = cql_transform_open_fname(cqlfile)) == 0)
177     {
178         char buf[512];
179         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
180                 cqlfile, strerror(errno));
181         ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
182     }
183     else
184     {
185         WRBUF wrbuf_result = wrbuf_alloc();
186         error = cql_transform(trans, cql_parser_result(parser),
187                               cql2pqf_wrbuf_puts, wrbuf_result);
188         if (error != 0) {
189             char buf[512];
190             const char *addinfo;
191             error = cql_transform_error(trans, &addinfo);
192             sprintf(buf, "%.200s (addinfo=%.200s)",
193                     cql_strerror(error), addinfo);
194             ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
195         }
196         else
197         {
198             result = xstrdup(wrbuf_cstr(wrbuf_result));
199         }
200         cql_transform_close(trans);
201         wrbuf_destroy(wrbuf_result);
202     }
203     cql_parser_destroy(parser);
204     return result;
205 }
206
207
208 ZOOM_API(ZOOM_query)
209     ZOOM_query_create(void)
210 {
211     ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
212
213     s->refcount = 1;
214     s->z_query = 0;
215     s->sort_spec = 0;
216     s->odr_query = odr_createmem(ODR_ENCODE);
217     s->odr_sort_spec = odr_createmem(ODR_ENCODE);
218     s->query_string = 0;
219     s->full_query = wrbuf_alloc();
220     s->sort_strategy = SORT_STRATEGY_Z3950;
221     s->sru11_sort_spec = wrbuf_alloc();
222     return s;
223 }
224
225 ZOOM_API(void)
226     ZOOM_query_destroy(ZOOM_query s)
227 {
228     if (!s)
229         return;
230
231     (s->refcount)--;
232     if (s->refcount == 0)
233     {
234         odr_destroy(s->odr_query);
235         odr_destroy(s->odr_sort_spec);
236         xfree(s->query_string);
237         wrbuf_destroy(s->full_query);
238         wrbuf_destroy(s->sru11_sort_spec);
239         xfree(s);
240     }
241 }
242
243 ZOOM_API(void)
244     ZOOM_query_addref(ZOOM_query s)
245 {
246     s->refcount++;
247 }
248
249
250 ZOOM_API(int)
251     ZOOM_query_prefix(ZOOM_query s, const char *str)
252 {
253     xfree(s->query_string);
254     s->query_string = xstrdup(str);
255     s->query_type = Z_Query_type_1;
256     return generate(s);
257 }
258
259 ZOOM_API(int)
260     ZOOM_query_cql(ZOOM_query s, const char *str)
261 {
262     xfree(s->query_string);
263     s->query_string = xstrdup(str);
264     s->query_type = Z_Query_type_104;
265     return generate(s);
266 }
267
268 /*
269  * Translate the CQL string client-side into RPN which is passed to
270  * the server.  This is useful for server's that don't themselves
271  * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
272  * only as a place to stash diagnostics if compilation fails; if this
273  * information is not needed, a null pointer may be used.
274  */
275 ZOOM_API(int)
276     ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
277 {
278     char *rpn;
279     int ret;
280     ZOOM_connection freeme = 0;
281
282     if (conn == 0)
283         conn = freeme = ZOOM_connection_create(0);
284
285     rpn = cql2pqf(conn, str);
286     if (freeme != 0)
287         ZOOM_connection_destroy(freeme);
288     if (rpn == 0)
289         return -1;
290
291     ret = ZOOM_query_prefix(s, rpn);
292     xfree(rpn);
293     return ret;
294 }
295
296 /*
297  * Analogous in every way to ZOOM_query_cql2rpn(), except that there
298  * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
299  * to the server, as the YAZ GFS doesn't know how to handle this.
300  */
301 ZOOM_API(int)
302     ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
303                        int *ccl_error, const char **error_string,
304                        int *error_pos)
305 {
306     int ret;
307     struct ccl_rpn_node *rpn;
308     CCL_bibset bibset = ccl_qual_mk();
309
310     if (config)
311         ccl_qual_buf(bibset, config);
312
313     rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
314     if (!rpn)
315     {
316         *error_string = ccl_err_msg(*ccl_error);
317         ret = -1;
318     }
319     else
320     {
321         WRBUF wr = wrbuf_alloc();
322         ccl_pquery(wr, rpn);
323         ccl_rpn_delete(rpn);
324         ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
325         wrbuf_destroy(wr);
326     }
327     ccl_qual_rm(&bibset);
328     return ret;
329 }
330
331 ZOOM_API(int)
332     ZOOM_query_sortby(ZOOM_query s, const char *criteria)
333 {
334     return ZOOM_query_sortby2(s, "z3950", criteria);
335 }
336
337 ZOOM_API(int)
338 ZOOM_query_sortby2(ZOOM_query s, const char *strategy, const char *criteria)
339 {
340     if (!strcmp(strategy, "z3950"))
341     {
342         s->sort_strategy = SORT_STRATEGY_Z3950;
343     }
344     else if (!strcmp(strategy, "type7"))
345     {
346         s->sort_strategy = SORT_STRATEGY_TYPE7;
347     }
348     else if (!strcmp(strategy, "cql"))
349     {
350         s->sort_strategy = SORT_STRATEGY_CQL;
351     }
352     else if (!strcmp(strategy, "sru11"))
353     {
354         s->sort_strategy = SORT_STRATEGY_SRU11;
355     }
356     else if (!strcmp(strategy, "solr"))
357     {
358         s->sort_strategy = SORT_STRATEGY_SOLR;
359     }
360     else if (!strcmp(strategy, "embed"))
361     {
362         s->sort_strategy = SORT_STRATEGY_EMBED;
363     }
364     else
365         return -1;
366
367     odr_reset(s->odr_sort_spec);
368     s->sort_spec = yaz_sort_spec(s->odr_sort_spec, criteria);
369     if (!s->sort_spec)
370         return -1;
371     return generate(s);
372 }
373
374 /*
375  * Local variables:
376  * c-basic-offset: 4
377  * c-file-style: "Stroustrup"
378  * indent-tabs-mode: nil
379  * End:
380  * vim: shiftwidth=4 tabstop=8 expandtab
381  */
382