Fix 0 ptr reference in zsets.c ZEB-661
[idzebra-moved-to-github.git] / rset / rset.c
1 /* This file is part of the Zebra server.
2    Copyright (C) Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #include <string.h>
25 #include <idzebra/util.h>
26 #include <assert.h>
27 #include <yaz/nmem.h>
28 #include <rset.h>
29
30 static int log_level = 0;
31 static int log_level_initialized = 0;
32
33
34 /**
35    \brief Common constuctor for RFDs
36    \param rs Result set handle.
37
38    Creates an rfd. Either allocates a new one, in which case the priv
39    pointer is null, and will have to be filled in, or picks up one
40    from the freelist, in which case the priv is already allocated,
41    and presumably everything that hangs from it as well
42 */
43 RSFD rfd_create_base(RSET rs)
44 {
45     RSFD rnew = rs->free_list;
46
47     if (rnew)
48     {
49         rs->free_list = rnew->next;
50         assert(rnew->rset==rs);
51         yaz_log(log_level, "rfd_create_base (fl): rfd=%p rs=%p fl=%p priv=%p",
52                 rnew, rs, rs->free_list, rnew->priv);
53     }
54     else
55     {
56         rnew = nmem_malloc(rs->nmem, sizeof(*rnew));
57         rnew->counted_buf = nmem_malloc(rs->nmem, rs->keycontrol->key_size);
58         rnew->priv = 0;
59         rnew->rset = rs;
60         yaz_log(log_level, "rfd_create_base (new): rfd=%p rs=%p fl=%p priv=%p",
61                 rnew, rs, rs->free_list, rnew->priv);
62     }
63     rnew->next = rs->use_list;
64     rs->use_list = rnew;
65     rnew->counted_items = 0;
66     return rnew;
67 }
68
69 static void rset_close_int(RSET rs, RSFD rfd)
70 {
71     RSFD *pfd;
72     (*rs->control->f_close)(rfd);
73
74     yaz_log(log_level, "rfd_delete_base: rfd=%p rs=%p priv=%p fl=%p",
75             rfd, rs, rfd->priv, rs->free_list);
76     for (pfd = &rs->use_list; *pfd; pfd = &(*pfd)->next)
77         if (*pfd == rfd)
78         {
79             *pfd = (*pfd)->next;
80             rfd->next = rs->free_list;
81             rs->free_list = rfd;
82             return;
83         }
84     yaz_log(YLOG_WARN, "rset_close handle not found. type=%s",
85             rs->control->desc);
86 }
87
88 void rset_set_hits_limit(RSET rs, zint l)
89 {
90     yaz_log(log_level, "rset_set_hits_limit %p l=" ZINT_FORMAT, rs, l);
91     rs->hits_limit = l;
92 }
93
94 /**
95    \brief Closes a result set RFD handle
96    \param rfd the RFD handle.
97 */
98 void rset_close(RSFD rfd)
99 {
100     RSET rs = rfd->rset;
101
102     if (rs->hits_count == 0)
103     {
104         TERMID termid;
105         char buf[100];
106
107         while (rfd->counted_items <= rs->hits_limit
108               && rset_default_read(rfd, buf, &termid))
109             ;
110
111         rs->hits_count = rfd->counted_items;
112         yaz_log(log_level, "rset_close rset=%p hits_count=" ZINT_FORMAT
113                 " hits_limit=" ZINT_FORMAT,
114                 rs, rs->hits_count, rs->hits_limit);
115         rs->hits_approx = 0;
116         if (rs->hits_count > rs->hits_limit && rs->hits_limit > 0)
117         {
118             double cur, tot;
119             zint est;
120             rset_pos(rfd, &cur, &tot);
121             if (tot > 0) {
122                 int i;
123                 double ratio = cur/tot;
124                 est = (zint)(0.5 + rs->hits_count / ratio);
125                 yaz_log(log_level, "Estimating hits (%s) "
126                         "%0.1f->" ZINT_FORMAT
127                         "; %0.1f->" ZINT_FORMAT,
128                         rs->control->desc,
129                         cur, rs->hits_count,
130                         tot, est);
131                 i = 0; /* round to significant digits */
132                 while (est > rs->hits_round) {
133                     est /= 10;
134                     i++;
135                 }
136                 while (i--)
137                     est *= 10;
138                 rs->hits_count = est;
139                 rs->hits_approx = 1;
140             }
141         }
142         yaz_log(log_level, "rset_close(%s) p=%p count=" ZINT_FORMAT,
143                 rs->control->desc, rs,
144                 rs->hits_count);
145     }
146     rset_close_int(rs, rfd);
147 }
148
149 /**
150    \brief Common constuctor for RSETs
151    \param sel The interface control handle
152    \param nmem The memory handle for it.
153    \param kcontrol Key control info (decode, encode, comparison etc)
154    \param scope scope for set
155    \param term Information about term for it (NULL for none).
156    \param no_children number of child rsets (0 for none)
157    \param children child rsets (NULL for none).
158
159    Creates an rfd. Either allocates a new one, in which case the priv
160    pointer is null, and will have to be filled in, or picks up one
161    from the freelist, in which case the priv is already allocated,
162    and presumably everything that hangs from it as well
163 */
164 RSET rset_create_base(const struct rset_control *sel,
165                       NMEM nmem, struct rset_key_control *kcontrol,
166                       int scope, TERMID term,
167                       int no_children, RSET *children)
168 {
169     RSET rset;
170     assert(nmem);
171     if (!log_level_initialized)
172     {
173         log_level = yaz_log_module_level("rset");
174         log_level_initialized = 1;
175     }
176
177     rset = (RSET) nmem_malloc(nmem, sizeof(*rset));
178     yaz_log(log_level, "rs_create(%s) rs=%p (nm=%p)", sel->desc, rset, nmem);
179     yaz_log(log_level, " ref_id=%s",
180             (term && term->ref_id ? term->ref_id : "null"));
181     rset->nmem = nmem;
182     rset->control = sel;
183     rset->refcount = 1;
184     rset->priv = 0;
185     rset->free_list = NULL;
186     rset->use_list = NULL;
187     rset->hits_count = 0;
188     rset->hits_limit = 0;
189     rset->hits_round = 1000;
190     rset->keycontrol = kcontrol;
191
192     (*kcontrol->inc)(kcontrol);
193     rset->scope = scope;
194     rset->term = term;
195     if (term)
196     {
197         term->rset = rset;
198         rset->hits_limit = term->hits_limit;
199     }
200     rset->no_children = no_children;
201     rset->children = 0;
202     if (no_children)
203     {
204         rset->children = (RSET*)
205             nmem_malloc(rset->nmem, no_children*sizeof(RSET *));
206         memcpy(rset->children, children, no_children*sizeof(RSET *));
207     }
208     return rset;
209 }
210
211 /**
212    \brief Destructor RSETs
213    \param rs Handle for result set.
214
215    Destroys a result set and all its children.
216    The f_delete method of control is called for the result set.
217 */
218 void rset_delete(RSET rs)
219 {
220     (rs->refcount)--;
221     yaz_log(log_level, "rs_delete(%s), rs=%p, refcount=%d",
222             rs->control->desc, rs, rs->refcount);
223     if (!rs->refcount)
224     {
225         int i;
226         if (rs->use_list)
227             yaz_log(YLOG_WARN, "rs_delete(%s) still has RFDs in use",
228                     rs->control->desc);
229         for (i = 0; i<rs->no_children; i++)
230             rset_delete(rs->children[i]);
231         (*rs->control->f_delete)(rs);
232         (*rs->keycontrol->dec)(rs->keycontrol);
233     }
234 }
235
236 /**
237    \brief Test for last use of RFD
238    \param rfd RFD handle.
239
240    Returns 1 if this RFD is the last reference to it; 0 otherwise.
241 */
242 int rfd_is_last(RSFD rfd)
243 {
244     if (rfd->rset->use_list == rfd && rfd->next == 0)
245         return 1;
246     return 0;
247 }
248
249 /**
250    \brief Duplicate an RSET
251    \param rs Handle for result set.
252
253    Duplicates a result set by incrementing the reference count to it.
254 */
255 RSET rset_dup (RSET rs)
256 {
257     (rs->refcount)++;
258     yaz_log(log_level, "rs_dup(%s), rs=%p, refcount=%d",
259             rs->control->desc, rs, rs->refcount);
260     return rs;
261 }
262
263 /**
264     \brief Estimates hit count for result set.
265     \param rs Result Set.
266
267     rset_count uses rset_pos to get the total and returns that.
268     This is ok for rsisamb/c/s, and for some other rsets, but in case of
269     booleans etc it will give bad estimate, as nothing has been read
270     from that rset
271 */
272 zint rset_count(RSET rs)
273 {
274     double cur, tot;
275     RSFD rfd = rset_open(rs, 0);
276     rset_pos(rfd, &cur, &tot);
277     rset_close_int(rs, rfd);
278     return (zint) tot;
279 }
280
281 /**
282    \brief is a getterms function for those that don't have any
283    \param ct result set handle
284    \param terms array of terms (0..maxterms-1)
285    \param maxterms length of terms array
286    \param curterm current size of terms array
287
288    If there is a term associated with rset the term is appeneded; otherwise
289    the terms array is untouched but curterm is incremented anyway.
290 */
291 void rset_get_one_term(RSET ct, TERMID *terms, int maxterms, int *curterm)
292 {
293     if (ct->term)
294     {
295         if (*curterm < maxterms)
296             terms[*curterm] = ct->term;
297         (*curterm)++;
298     }
299 }
300
301 struct ord_list *ord_list_create(NMEM nmem)
302 {
303     return 0;
304 }
305
306 struct ord_list *ord_list_append(NMEM nmem, struct ord_list *list,
307                                         int ord)
308 {
309     struct ord_list *n = nmem_malloc(nmem, sizeof(*n));
310     n->ord = ord;
311     n->next = list;
312     return n;
313 }
314
315 struct ord_list *ord_list_dup(NMEM nmem, struct ord_list *list)
316 {
317     struct ord_list *n = ord_list_create(nmem);
318     for (; list; list = list->next)
319         n = ord_list_append(nmem, n, list->ord);
320     return n;
321 }
322
323 void ord_list_print(struct ord_list *list)
324 {
325     for (; list; list = list->next)
326         yaz_log(YLOG_LOG, "ord_list %d", list->ord);
327 }
328 /**
329    \brief Creates a TERMID entry.
330    \param name Term/Name buffer with given length
331    \param length of term
332    \param flags for term
333    \param type Term Type, Z_Term_general, Z_Term_characterString,..
334    \param nmem memory for term.
335    \param ol ord list
336    \param reg_type register type
337    \param hits_limit limit before counting stops and gets approximate
338    \param ref_id supplied ID for term that can be used to identify this
339 */
340 TERMID rset_term_create(const char *name, int length, const char *flags,
341                         int type, NMEM nmem, struct ord_list *ol,
342                         int reg_type,
343                         zint hits_limit, const char *ref_id)
344
345 {
346     TERMID t = (TERMID) nmem_malloc(nmem, sizeof(*t));
347
348     if (!name)
349         name = "";
350     if (length == -1)
351         t->name = nmem_strdup(nmem, name);
352     else
353         t->name = nmem_strdupn(nmem, name, length);
354     if (!ref_id)
355         t->ref_id = 0;
356     else
357         t->ref_id = nmem_strdup(nmem, ref_id);
358     if (!flags)
359         t->flags = NULL;
360     else
361         t->flags = nmem_strdup(nmem, flags);
362     t->hits_limit = hits_limit;
363     t->type = type;
364     t->reg_type = reg_type;
365     t->rankpriv = 0;
366     t->rset = 0;
367     t->ol = ord_list_dup(nmem, ol);
368     return t;
369 }
370
371 int rset_default_read(RSFD rfd, void *buf, TERMID *term)
372 {
373     RSET rset = rfd->rset;
374     int rc = (*rset->control->f_read)(rfd, buf, term);
375     if (rc > 0)
376     {
377         int got_scope;
378         if (rfd->counted_items == 0)
379             got_scope = rset->scope+1;
380         else
381             got_scope = rset->keycontrol->cmp(buf, rfd->counted_buf);
382
383 #if 0
384         key_logdump_txt(YLOG_LOG, buf, "rset_default_read");
385         yaz_log(YLOG_LOG, "rset_scope=%d got_scope=%d", rset->scope, got_scope);
386 #endif
387         if (got_scope > rset->scope)
388         {
389             memcpy(rfd->counted_buf, buf, rset->keycontrol->key_size);
390             rfd->counted_items++;
391         }
392     }
393     return rc;
394 }
395
396 int rset_default_forward(RSFD rfd, void *buf, TERMID *term,
397                          const void *untilbuf)
398 {
399     RSET rset = rfd->rset;
400     int more;
401
402     if (rset->control->f_forward &&
403         rfd->counted_items >= rset->hits_limit)
404     {
405         assert (rset->control->f_forward != rset_default_forward);
406         return rset->control->f_forward(rfd, buf, term, untilbuf);
407     }
408
409     while ((more = rset_read(rfd, buf, term)) > 0)
410     {
411         if ((rfd->rset->keycontrol->cmp)(untilbuf, buf) < rset->scope)
412             break;
413     }
414     if (log_level)
415         yaz_log(log_level, "rset_default_forward exiting rfd=%p scope=%d m=%d c=%d",
416                 rfd, rset->scope, more, rset->scope);
417
418     return more;
419 }
420
421 void rset_visit(RSET rset, int level)
422 {
423     int i;
424     yaz_log(YLOG_LOG, "%*s%c " ZINT_FORMAT, level, "",
425             rset->hits_approx ? '~' : '=',
426             rset->hits_count);
427     for (i = 0; i<rset->no_children; i++)
428         rset_visit(rset->children[i], level+1);
429 }
430
431 int rset_no_write(RSFD rfd, const void *buf)
432 {
433     yaz_log(YLOG_FATAL, "%s set type is read-only", rfd->rset->control->desc);
434     return -1;
435 }
436
437 /*
438  * Local variables:
439  * c-basic-offset: 4
440  * c-file-style: "Stroustrup"
441  * indent-tabs-mode: nil
442  * End:
443  * vim: shiftwidth=4 tabstop=8 expandtab
444  */
445