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