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