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 */
rfd_create_base(RSET rs)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 
rset_close_int(RSET rs,RSFD rfd)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 
rset_set_hits_limit(RSET rs,zint l)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 */
rset_close(RSFD rfd)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 */
rset_create_base(const struct rset_control * sel,NMEM nmem,struct rset_key_control * kcontrol,int scope,TERMID term,int no_children,RSET * children)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 */
rset_delete(RSET rs)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 */
rfd_is_last(RSFD rfd)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 */
rset_dup(RSET rs)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 */
rset_count(RSET rs)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 */
rset_get_one_term(RSET ct,TERMID * terms,int maxterms,int * curterm)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 
ord_list_create(NMEM nmem)301 struct ord_list *ord_list_create(NMEM nmem)
302 {
303     return 0;
304 }
305 
ord_list_append(NMEM nmem,struct ord_list * list,int ord)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 
ord_list_dup(NMEM nmem,struct ord_list * list)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 
ord_list_print(struct ord_list * list)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 */
rset_term_create(const char * name,int length,const char * flags,int type,NMEM nmem,struct ord_list * ol,int reg_type,zint hits_limit,const char * ref_id)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 
rset_default_read(RSFD rfd,void * buf,TERMID * term)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 
rset_default_forward(RSFD rfd,void * buf,TERMID * term,const void * untilbuf)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 
rset_visit(RSET rset,int level)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 
442