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