Omit CVS Id. Update copyright year.
[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 /**
319    \brief Creates a TERMID entry.
320    \param name Term/Name buffer with given length
321    \param length of term
322    \param flags for term
323    \param type Term Type, Z_Term_general, Z_Term_characterString,..
324    \param nmem memory for term.
325    \param ol ord list
326    \param reg_type register type
327    \param hits_limit limit before counting stops and gets approximate
328    \param ref_id supplied ID for term that can be used to identify this
329 */
330 TERMID rset_term_create(const char *name, int length, const char *flags,
331                         int type, NMEM nmem, struct ord_list *ol,
332                         int reg_type,
333                         zint hits_limit, const char *ref_id)
334
335 {
336     TERMID t;
337     yaz_log (log_level, "term_create '%s' %d f=%s type=%d nmem=%p",
338             name, length, flags, type, nmem);
339     t= (TERMID) nmem_malloc(nmem, sizeof(*t));
340     if (!name)
341         t->name = NULL;
342     else if (length == -1)
343         t->name = nmem_strdup(nmem, name);
344     else
345         t->name = nmem_strdupn(nmem, name, length);
346     if (!ref_id)
347         t->ref_id = 0;
348     else
349         t->ref_id = nmem_strdup(nmem, ref_id);
350     if (!flags)
351         t->flags = NULL;
352     else
353         t->flags = nmem_strdup(nmem, flags);
354     t->hits_limit = hits_limit;
355     t->type = type;
356     t->reg_type = reg_type;
357     t->rankpriv = 0;
358     t->rset = 0;
359     t->ol = ord_list_dup(nmem, ol);
360     return t;
361 }
362
363 int rset_default_read(RSFD rfd, void *buf, TERMID *term)
364 {
365     RSET rset = rfd->rset;
366     int rc = (*rset->control->f_read)(rfd, buf, term);
367     if (rc > 0)
368     {
369         int got_scope;
370         if (rfd->counted_items == 0)
371             got_scope = rset->scope+1;
372         else
373             got_scope = rset->keycontrol->cmp(buf, rfd->counted_buf);
374        
375 #if 0
376         key_logdump_txt(YLOG_LOG, buf, "rset_default_read");
377         yaz_log(YLOG_LOG, "rset_scope=%d got_scope=%d", rset->scope, got_scope);
378 #endif
379         if (got_scope > rset->scope)
380         {
381             memcpy(rfd->counted_buf, buf, rset->keycontrol->key_size);
382             rfd->counted_items++;
383         }
384     }
385     return rc;
386 }
387
388 int rset_default_forward(RSFD rfd, void *buf, TERMID *term,
389                          const void *untilbuf)
390 {
391     RSET rset = rfd->rset;
392     int more;
393
394     if (rset->control->f_forward &&
395         rfd->counted_items >= rset->hits_limit)
396     {
397         assert (rset->control->f_forward != rset_default_forward);
398         return rset->control->f_forward(rfd, buf, term, untilbuf);
399     }
400     
401     while ((more = rset_read(rfd, buf, term)) > 0)
402     {
403         if ((rfd->rset->keycontrol->cmp)(untilbuf, buf) <= 1)
404             break;
405     }
406     if (log_level)
407         yaz_log (log_level, "rset_default_forward exiting m=%d c=%d",
408                  more, rset->scope);
409     
410     return more;
411 }
412
413 void rset_visit(RSET rset, int level)
414 {
415     int i;
416     yaz_log(YLOG_LOG, "%*s%c " ZINT_FORMAT, level, "",
417             rset->hits_approx ? '~' : '=',
418             rset->hits_count);
419     for (i = 0; i<rset->no_children; i++)
420         rset_visit(rset->children[i], level+1);
421 }
422
423 /*
424  * Local variables:
425  * c-basic-offset: 4
426  * indent-tabs-mode: nil
427  * End:
428  * vim: shiftwidth=4 tabstop=8 expandtab
429  */
430