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