ZOOM: new setting apdufile
[yaz-moved-to-github.git] / src / zoom-query.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 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 const char *ZOOM_query_get_query_string(ZOOM_query s)
140 {
141     return wrbuf_cstr(s->full_query);
142 }
143
144 void ZOOM_query_get_hash(ZOOM_query s, WRBUF w)
145 {
146     wrbuf_printf(w, "%d;", s->query_type);
147     if (s->query_string)
148         wrbuf_puts(w, s->query_string);
149     wrbuf_printf(w, ";%d;", s->sort_strategy);
150     if (s->sort_spec)
151         yaz_sort_spec_to_type7(s->sort_spec, w);
152 }
153
154 /*
155  * Returns an xmalloc()d string containing RPN that corresponds to the
156  * CQL passed in.  On error, sets the Connection object's error state
157  * and returns a null pointer.
158  * ### We could cache CQL parser and/or transformer in Connection.
159  */
160 static char *cql2pqf(ZOOM_connection c, const char *cql)
161 {
162     CQL_parser parser;
163     int error;
164     const char *cqlfile;
165     cql_transform_t trans;
166     char *result = 0;
167
168     parser = cql_parser_create();
169     if ((error = cql_parser_string(parser, cql)) != 0) {
170         cql_parser_destroy(parser);
171         ZOOM_set_error(c, ZOOM_ERROR_CQL_PARSE, cql);
172         return 0;
173     }
174
175     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
176     if (cqlfile == 0)
177     {
178         ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
179     }
180     else if ((trans = cql_transform_open_fname(cqlfile)) == 0)
181     {
182         char buf[512];
183         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
184                 cqlfile, strerror(errno));
185         ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
186     }
187     else
188     {
189         WRBUF wrbuf_result = wrbuf_alloc();
190         error = cql_transform(trans, cql_parser_result(parser),
191                               wrbuf_vp_puts, wrbuf_result);
192         if (error != 0) {
193             char buf[512];
194             const char *addinfo;
195             error = cql_transform_error(trans, &addinfo);
196             sprintf(buf, "%.200s (addinfo=%.200s)",
197                     cql_strerror(error), addinfo);
198             ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
199         }
200         else
201         {
202             result = xstrdup(wrbuf_cstr(wrbuf_result));
203         }
204         cql_transform_close(trans);
205         wrbuf_destroy(wrbuf_result);
206     }
207     cql_parser_destroy(parser);
208     return result;
209 }
210
211
212 ZOOM_API(ZOOM_query)
213     ZOOM_query_create(void)
214 {
215     ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
216
217     s->refcount = 1;
218     s->z_query = 0;
219     s->sort_spec = 0;
220     s->odr_query = odr_createmem(ODR_ENCODE);
221     s->odr_sort_spec = odr_createmem(ODR_ENCODE);
222     s->query_string = 0;
223     s->full_query = wrbuf_alloc();
224     s->sort_strategy = SORT_STRATEGY_Z3950;
225     s->sru11_sort_spec = wrbuf_alloc();
226     return s;
227 }
228
229 ZOOM_API(void)
230     ZOOM_query_destroy(ZOOM_query s)
231 {
232     if (!s)
233         return;
234
235     (s->refcount)--;
236     if (s->refcount == 0)
237     {
238         odr_destroy(s->odr_query);
239         odr_destroy(s->odr_sort_spec);
240         xfree(s->query_string);
241         wrbuf_destroy(s->full_query);
242         wrbuf_destroy(s->sru11_sort_spec);
243         xfree(s);
244     }
245 }
246
247 ZOOM_API(void)
248     ZOOM_query_addref(ZOOM_query s)
249 {
250     s->refcount++;
251 }
252
253
254 ZOOM_API(int)
255     ZOOM_query_prefix(ZOOM_query s, const char *str)
256 {
257     xfree(s->query_string);
258     s->query_string = xstrdup(str);
259     s->query_type = Z_Query_type_1;
260     return generate(s);
261 }
262
263 ZOOM_API(int)
264     ZOOM_query_cql(ZOOM_query s, const char *str)
265 {
266     xfree(s->query_string);
267     s->query_string = xstrdup(str);
268     s->query_type = Z_Query_type_104;
269     return generate(s);
270 }
271
272 /*
273  * Translate the CQL string client-side into RPN which is passed to
274  * the server.  This is useful for server's that don't themselves
275  * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
276  * only as a place to stash diagnostics if compilation fails; if this
277  * information is not needed, a null pointer may be used.
278  */
279 ZOOM_API(int)
280     ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
281 {
282     char *rpn;
283     int ret;
284     ZOOM_connection freeme = 0;
285
286     if (conn == 0)
287         conn = freeme = ZOOM_connection_create(0);
288
289     rpn = cql2pqf(conn, str);
290     if (freeme != 0)
291         ZOOM_connection_destroy(freeme);
292     if (rpn == 0)
293         return -1;
294
295     ret = ZOOM_query_prefix(s, rpn);
296     xfree(rpn);
297     return ret;
298 }
299
300 /*
301  * Analogous in every way to ZOOM_query_cql2rpn(), except that there
302  * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
303  * to the server, as the YAZ GFS doesn't know how to handle this.
304  */
305 ZOOM_API(int)
306     ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
307                        int *ccl_error, const char **error_string,
308                        int *error_pos)
309 {
310     int ret;
311     struct ccl_rpn_node *rpn;
312     CCL_bibset bibset = ccl_qual_mk();
313
314     if (config)
315         ccl_qual_buf(bibset, config);
316
317     rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
318     if (!rpn)
319     {
320         *error_string = ccl_err_msg(*ccl_error);
321         ret = -1;
322     }
323     else
324     {
325         WRBUF wr = wrbuf_alloc();
326         ccl_pquery(wr, rpn);
327         ccl_rpn_delete(rpn);
328         ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
329         wrbuf_destroy(wr);
330     }
331     ccl_qual_rm(&bibset);
332     return ret;
333 }
334
335 ZOOM_API(int)
336     ZOOM_query_sortby(ZOOM_query s, const char *criteria)
337 {
338     return ZOOM_query_sortby2(s, "z3950", criteria);
339 }
340
341 ZOOM_API(int)
342 ZOOM_query_sortby2(ZOOM_query s, const char *strategy, const char *criteria)
343 {
344     if (!strcmp(strategy, "z3950"))
345     {
346         s->sort_strategy = SORT_STRATEGY_Z3950;
347     }
348     else if (!strcmp(strategy, "type7"))
349     {
350         s->sort_strategy = SORT_STRATEGY_TYPE7;
351     }
352     else if (!strcmp(strategy, "cql"))
353     {
354         s->sort_strategy = SORT_STRATEGY_CQL;
355     }
356     else if (!strcmp(strategy, "sru11"))
357     {
358         s->sort_strategy = SORT_STRATEGY_SRU11;
359     }
360     else if (!strcmp(strategy, "solr"))
361     {
362         s->sort_strategy = SORT_STRATEGY_SOLR;
363     }
364     else if (!strcmp(strategy, "embed"))
365     {
366         s->sort_strategy = SORT_STRATEGY_EMBED;
367     }
368     else
369         return -1;
370
371     odr_reset(s->odr_sort_spec);
372     s->sort_spec = yaz_sort_spec(s->odr_sort_spec, criteria);
373     if (!s->sort_spec)
374         return -2;
375     return generate(s);
376 }
377
378 /*
379  * Local variables:
380  * c-basic-offset: 4
381  * c-file-style: "Stroustrup"
382  * indent-tabs-mode: nil
383  * End:
384  * vim: shiftwidth=4 tabstop=8 expandtab
385  */
386