Deb: idzebra-2.0-utils includes init.d script
[idzebra-moved-to-github.git] / rset / rset.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 2004-2013 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;
347     yaz_log (log_level, "term_create '%s' %d f=%s type=%d nmem=%p",
348             name, length, flags, type, nmem);
349     t= (TERMID) nmem_malloc(nmem, sizeof(*t));
350     if (!name)
351         t->name = NULL;
352     else if (length == -1)
353         t->name = nmem_strdup(nmem, name);
354     else
355         t->name = nmem_strdupn(nmem, name, length);
356     if (!ref_id)
357         t->ref_id = 0;
358     else
359         t->ref_id = nmem_strdup(nmem, ref_id);
360     if (!flags)
361         t->flags = NULL;
362     else
363         t->flags = nmem_strdup(nmem, flags);
364     t->hits_limit = hits_limit;
365     t->type = type;
366     t->reg_type = reg_type;
367     t->rankpriv = 0;
368     t->rset = 0;
369     t->ol = ord_list_dup(nmem, ol);
370     return t;
371 }
372
373 int rset_default_read(RSFD rfd, void *buf, TERMID *term)
374 {
375     RSET rset = rfd->rset;
376     int rc = (*rset->control->f_read)(rfd, buf, term);
377     if (rc > 0)
378     {
379         int got_scope;
380         if (rfd->counted_items == 0)
381             got_scope = rset->scope+1;
382         else
383             got_scope = rset->keycontrol->cmp(buf, rfd->counted_buf);
384
385 #if 0
386         key_logdump_txt(YLOG_LOG, buf, "rset_default_read");
387         yaz_log(YLOG_LOG, "rset_scope=%d got_scope=%d", rset->scope, got_scope);
388 #endif
389         if (got_scope > rset->scope)
390         {
391             memcpy(rfd->counted_buf, buf, rset->keycontrol->key_size);
392             rfd->counted_items++;
393         }
394     }
395     return rc;
396 }
397
398 int rset_default_forward(RSFD rfd, void *buf, TERMID *term,
399                          const void *untilbuf)
400 {
401     RSET rset = rfd->rset;
402     int more;
403
404     if (rset->control->f_forward &&
405         rfd->counted_items >= rset->hits_limit)
406     {
407         assert (rset->control->f_forward != rset_default_forward);
408         return rset->control->f_forward(rfd, buf, term, untilbuf);
409     }
410
411     while ((more = rset_read(rfd, buf, term)) > 0)
412     {
413         if ((rfd->rset->keycontrol->cmp)(untilbuf, buf) < rset->scope)
414             break;
415     }
416     if (log_level)
417         yaz_log(log_level, "rset_default_forward exiting rfd=%p scope=%d m=%d c=%d",
418                 rfd, rset->scope, more, rset->scope);
419
420     return more;
421 }
422
423 void rset_visit(RSET rset, int level)
424 {
425     int i;
426     yaz_log(YLOG_LOG, "%*s%c " ZINT_FORMAT, level, "",
427             rset->hits_approx ? '~' : '=',
428             rset->hits_count);
429     for (i = 0; i<rset->no_children; i++)
430         rset_visit(rset->children[i], level+1);
431 }
432
433 /*
434  * Local variables:
435  * c-basic-offset: 4
436  * c-file-style: "Stroustrup"
437  * indent-tabs-mode: nil
438  * End:
439  * vim: shiftwidth=4 tabstop=8 expandtab
440  */
441