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