Fixed bug #815: Extended Services "delete" requires a dummy "record" to
[idzebra-moved-to-github.git] / rset / rset.c
1 /* $Id: rset.c,v 1.56 2006-08-16 13:14:45 adam Exp $
2    Copyright (C) 1995-2006
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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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
179     (*kcontrol->inc)(kcontrol);
180     rset->scope = scope;
181     rset->term = term;
182     if (term)
183     {
184         term->rset = rset;
185         rset->hits_limit = term->hits_limit;
186     }
187     rset->no_children = no_children;
188     rset->children = 0;
189     if (no_children)
190     {
191         rset->children = (RSET*)
192             nmem_malloc(rset->nmem, no_children*sizeof(RSET *));
193         memcpy(rset->children, children, no_children*sizeof(RSET *));
194     }
195     return rset;
196 }
197
198 /**
199    \brief Destructor RSETs
200    \param rs Handle for result set.
201    
202    Destroys a result set and all its children.
203    The f_delete method of control is called for the result set.
204 */
205 void rset_delete(RSET rs)
206 {
207     (rs->refcount)--;
208     yaz_log(log_level, "rs_delete(%s), rs=%p, refcount=%d",
209             rs->control->desc, rs, rs->refcount); 
210     if (!rs->refcount)
211     {
212         int i;
213         if (rs->use_list)
214             yaz_log(YLOG_WARN, "rs_delete(%s) still has RFDs in use",
215                     rs->control->desc);
216         for (i = 0; i<rs->no_children; i++)
217             rset_delete(rs->children[i]);
218         (*rs->control->f_delete)(rs);
219         (*rs->keycontrol->dec)(rs->keycontrol);
220     }
221 }
222
223 /**
224    \brief Test for last use of RFD
225    \param rfd RFD handle.
226    
227    Returns 1 if this RFD is the last reference to it; 0 otherwise.
228 */
229 int rfd_is_last(RSFD rfd)
230 {
231     if (rfd->rset->use_list == rfd && rfd->next == 0)
232         return 1;
233     return 0;
234 }
235
236 /**
237    \brief Duplicate an RSET
238    \param rs Handle for result set.
239    
240    Duplicates a result set by incrementing the reference count to it.
241 */
242 RSET rset_dup (RSET rs)
243 {
244     (rs->refcount)++;
245     yaz_log(log_level, "rs_dup(%s), rs=%p, refcount=%d",
246             rs->control->desc, rs, rs->refcount); 
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         int got_scope;
362         if (rfd->counted_items == 0)
363             got_scope = rset->scope+1;
364         else
365             got_scope = rset->keycontrol->cmp(buf, rfd->counted_buf);
366        
367 #if 0
368         key_logdump_txt(YLOG_LOG, buf, "rset_default_read");
369         yaz_log(YLOG_LOG, "rset_scope=%d got_scope=%d", rset->scope, got_scope);
370 #endif
371         if (got_scope > rset->scope)
372         {
373             memcpy(rfd->counted_buf, buf, rset->keycontrol->key_size);
374             rfd->counted_items++;
375         }
376     }
377     return rc;
378 }
379
380 int rset_default_forward(RSFD rfd, void *buf, TERMID *term,
381                          const void *untilbuf)
382 {
383     RSET rset = rfd->rset;
384     int more;
385
386     if (rset->control->f_forward &&
387         rfd->counted_items >= rset->hits_limit)
388     {
389         assert (rset->control->f_forward != rset_default_forward);
390         return rset->control->f_forward(rfd, buf, term, untilbuf);
391     }
392     
393     while ((more = rset_read(rfd, buf, term)) > 0)
394     {
395         if ((rfd->rset->keycontrol->cmp)(untilbuf, buf) <= 1)
396             break;
397     }
398     if (log_level)
399         yaz_log (log_level, "rset_default_forward exiting m=%d c=%d",
400                  more, rset->scope);
401     
402     return more;
403 }
404
405 void rset_visit(RSET rset, int level)
406 {
407     int i;
408     yaz_log(YLOG_LOG, "%*s%c " ZINT_FORMAT, level, "",
409             rset->hits_approx ? '~' : '=',
410             rset->hits_count);
411     for (i = 0; i<rset->no_children; i++)
412         rset_visit(rset->children[i], level+1);
413 }
414
415 /*
416  * Local variables:
417  * c-basic-offset: 4
418  * indent-tabs-mode: nil
419  * End:
420  * vim: shiftwidth=4 tabstop=8 expandtab
421  */
422