Make two functions to get term/doc occurrences rather than one.
[idzebra-moved-to-github.git] / index / zinfo.c
1 /* $Id: zinfo.c,v 1.62 2006-05-11 10:15:33 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 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 <sys/types.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28
29 #include <idzebra/version.h>
30 #include "zinfo.h"
31
32 #define ZINFO_DEBUG 0
33
34 struct zebSUInfo {
35     int index_type;
36 #define ZEB_SU_SET_USE 1
37 #define ZEB_SU_STR 2
38     int which;
39     union {
40         char *str;
41         struct {
42             int set;
43             int use;
44         } su;
45     } u;
46     int ordinal;
47     zint doc_occurrences;
48     zint term_occurrences;
49 };
50
51 struct zebSUInfoB {
52     struct zebSUInfo info;
53     struct zebSUInfoB *next;
54 };
55
56 typedef struct zebAccessObjectB *zebAccessObject;
57 struct zebAccessObjectB {
58     void *handle;
59     SYSNO sysno;
60     Odr_oid *oid;
61     zebAccessObject next;
62 };
63
64 typedef struct zebAccessInfoB *zebAccessInfo;
65 struct zebAccessInfoB {
66     zebAccessObject attributeSetIds;
67     zebAccessObject schemas;
68 };
69
70 typedef struct {
71     struct zebSUInfoB *SUInfo;
72     SYSNO sysno;
73     int dirty;
74     int readFlag;
75     data1_node *data1_tree;
76 } *zebAttributeDetails;
77
78 struct zebDatabaseInfoB {
79     zebAttributeDetails attributeDetails;
80     int ordinalDatabase;
81     char *databaseName;
82     data1_node *data1_database;
83     zint recordCount;    /* records in db */
84     zint recordBytes;    /* size of records */
85     SYSNO sysno;         /* sysno of database info */
86     int readFlag;        /* 1: read is needed when referenced; 0 if not */
87     int dirty;           /* 1: database is dirty: write is needed */
88     struct zebDatabaseInfoB *next;
89     zebAccessInfo accessInfo;
90 };
91
92 struct zebraExplainAttset {
93     char *name;
94     int ordinal;
95     struct zebraExplainAttset *next;
96 };
97
98 struct zebraCategoryListInfo {
99     int dirty;
100     SYSNO sysno;
101     data1_node *data1_categoryList;
102 };
103
104 struct zebraExplainInfo {
105     int ordinalSU;
106     int ordinalDatabase;
107     zint runNumber;
108     int dirty;
109     int write_flag;
110     Records records;
111     data1_handle dh;
112     Res res;
113     struct zebraExplainAttset *attsets;
114     NMEM nmem;
115     data1_node *data1_target;
116     struct zebraCategoryListInfo *categoryList;
117     struct zebDatabaseInfoB *databaseInfo;
118     struct zebDatabaseInfoB *curDatabaseInfo;
119     zebAccessInfo accessInfo;
120     char date[15]; /* YYYY MMDD HH MM SS */
121     int (*updateFunc)(void *handle, Record drec, data1_node *n);
122     void *updateHandle;
123 };
124
125 static void zebraExplain_initCommonInfo(ZebraExplainInfo zei, data1_node *n);
126 static void zebraExplain_initAccessInfo(ZebraExplainInfo zei, data1_node *n);
127
128 static data1_node *read_sgml_rec(data1_handle dh, NMEM nmem, Record rec)
129 {
130     return data1_read_sgml(dh, nmem, rec->info[recInfo_storeData]);
131 }
132
133 static void zebraExplain_writeDatabase(ZebraExplainInfo zei,
134                                         struct zebDatabaseInfoB *zdi,
135                                         int key_flush);
136 static void zebraExplain_writeAttributeDetails(ZebraExplainInfo zei,
137                                                 zebAttributeDetails zad,
138                                                 const char *databaseName,
139                                                 int key_flush);
140 static void zebraExplain_writeTarget(ZebraExplainInfo zei, int key_flush);
141 static void zebraExplain_writeAttributeSet(ZebraExplainInfo zei,
142                                             zebAccessObject o,
143                                             int key_flush);
144 static void zebraExplain_writeCategoryList(ZebraExplainInfo zei,
145                                             struct zebraCategoryListInfo *zcl,
146                                             int key_flush);
147
148
149 static Record createRecord(Records records, SYSNO *sysno)
150 {
151     Record rec;
152     if (*sysno)
153     {
154         rec = rec_get(records, *sysno);
155         if (!rec)
156             return 0;
157         xfree(rec->info[recInfo_storeData]);
158     }
159     else
160     {
161         rec = rec_new(records);
162         if (!rec)
163             return 0;
164         *sysno = rec->sysno;
165         
166         rec->info[recInfo_fileType] =
167             rec_strdup("grs.sgml", &rec->size[recInfo_fileType]);
168         rec->info[recInfo_databaseName] =
169             rec_strdup("IR-Explain-1",
170                         &rec->size[recInfo_databaseName]); 
171     }
172     return rec;
173 }
174
175 void zebraExplain_flush(ZebraExplainInfo zei, void *handle)
176 {
177     if (!zei)
178         return;
179     zei->updateHandle = handle;
180     if (zei->write_flag)
181     {
182         struct zebDatabaseInfoB *zdi;
183         zebAccessObject o;
184
185         /* write each database info record */
186         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
187         {
188             zebraExplain_writeDatabase(zei, zdi, 1);
189             zebraExplain_writeAttributeDetails(zei, zdi->attributeDetails,
190                                                 zdi->databaseName, 1);
191         }
192         zebraExplain_writeTarget(zei, 1);
193         zebraExplain_writeCategoryList(zei,
194                                         zei->categoryList,
195                                         1);
196         assert(zei->accessInfo);
197         for (o = zei->accessInfo->attributeSetIds; o; o = o->next)
198             if (!o->sysno)
199                 zebraExplain_writeAttributeSet(zei, o, 1);
200         for (o = zei->accessInfo->schemas; o; o = o->next)
201             if (!o->sysno)
202             {
203 /*              zebraExplain_writeSchema(zei, o, 1); */
204             }
205
206         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
207         {
208             zebraExplain_writeDatabase(zei, zdi, 0);
209             zebraExplain_writeAttributeDetails(zei, zdi->attributeDetails,
210                                                 zdi->databaseName, 0);
211         }
212         zebraExplain_writeTarget(zei, 0);
213     }
214 }
215
216 void zebraExplain_close(ZebraExplainInfo zei)
217 {
218 #if ZINFO_DEBUG
219     yaz_log(YLOG_LOG, "zebraExplain_close");
220 #endif
221     if (!zei)
222         return;
223     zebraExplain_flush(zei, zei->updateHandle);
224     nmem_destroy(zei->nmem);
225 }
226
227 void zebraExplain_mergeOids (ZebraExplainInfo zei, data1_node *n,
228                              zebAccessObject *op)
229 {
230     data1_node *np;
231
232     for (np = n->child; np; np = np->next)
233     {
234         char str[64];
235         int len;
236         Odr_oid *oid;
237         zebAccessObject ao;
238
239         if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "oid"))
240             continue;
241         len = np->child->u.data.len;
242         if (len > 63)
243             len = 63;
244         memcpy(str, np->child->u.data.data, len);
245         str[len] = '\0';
246         
247         oid = odr_getoidbystr_nmem(zei->nmem, str);
248
249         for (ao = *op; ao; ao = ao->next)
250             if (!oid_oidcmp(oid, ao->oid))
251             {
252                 ao->sysno = 1;
253                 break;
254             }
255         if (!ao)
256         {
257             ao = (zebAccessObject) nmem_malloc(zei->nmem, sizeof(*ao));
258             ao->handle = NULL;
259             ao->sysno = 1;
260             ao->oid = oid;
261             ao->next = *op;
262             *op = ao;
263         }
264     }
265 }
266
267 void zebraExplain_mergeAccessInfo(ZebraExplainInfo zei, data1_node *n,
268                                    zebAccessInfo *accessInfo)
269 {
270     data1_node *np;
271     
272     if (!n)
273     {
274         *accessInfo = (zebAccessInfo)
275             nmem_malloc(zei->nmem, sizeof(**accessInfo));
276         (*accessInfo)->attributeSetIds = NULL;
277         (*accessInfo)->schemas = NULL;
278     }
279     else
280     {
281         if (!(n = data1_search_tag(zei->dh, n->child, "accessInfo")))
282             return;
283         if ((np = data1_search_tag(zei->dh, n->child, "attributeSetIds")))
284             zebraExplain_mergeOids(zei, np,
285                                     &(*accessInfo)->attributeSetIds);
286         if ((np = data1_search_tag(zei->dh, n->child, "schemas")))
287             zebraExplain_mergeOids(zei, np,
288                                     &(*accessInfo)->schemas);
289     }
290 }
291
292 /* Explain structure
293     root record
294       of type targetInfo
295       and has sysno = 1
296
297     databaseList (list of databases)
298 */
299 /*
300 Example root:
301 explain:
302   targetInfo: TargetInfo
303     name: Zebra
304     namedResultSets: 1
305     multipleDbSearch: 1
306     nicknames:
307       name: Zebra
308     commonInfo:
309       dateAdded: 20030630190601
310       dateChanged: 20030630190601
311       languageCode: EN
312     accessinfo:
313       unitSystems:
314         string: ISO
315       attributeSetIds:
316         oid: 1.2.840.10003.3.2
317         oid: 1.2.840.10003.3.5
318         oid: 1.2.840.10003.3.1
319       schemas:
320         oid: 1.2.840.10003.13.1000.81.2
321         oid: 1.2.840.10003.13.2
322     zebraInfo:
323       version: 1.3.12
324       databaseList:
325         database:
326           name: Default
327           id: 50
328           attributeDetailsId: 51
329         database:
330           name: IR-Explain-1
331           id: 52
332           attributeDetailsId: 53
333       ordinalSU: 38
334       runNumber: 1
335 nextResultSetPosition = 2
336 */
337
338 ZebraExplainInfo zebraExplain_open(
339     Records records, data1_handle dh,
340     Res res,
341     int writeFlag,
342     void *updateHandle,
343     int (*updateFunc)(void *handle, Record drec, data1_node *n))
344 {
345     Record trec;
346     ZebraExplainInfo zei;
347     struct zebDatabaseInfoB **zdip;
348     time_t our_time;
349     struct tm *tm;
350     NMEM nmem = nmem_create();
351
352 #if ZINFO_DEBUG
353     yaz_log(YLOG_LOG, "zebraExplain_open wr=%d", writeFlag);
354 #endif
355     zei = (ZebraExplainInfo) nmem_malloc(nmem, sizeof(*zei));
356     zei->databaseInfo = 0;
357     zei->write_flag = writeFlag;
358     zei->updateHandle = updateHandle;
359     zei->updateFunc = updateFunc;
360     zei->dirty = 0;
361     zei->ordinalDatabase = 1;
362     zei->curDatabaseInfo = NULL;
363     zei->records = records;
364     zei->nmem = nmem;
365     zei->dh = dh;
366     zei->attsets = NULL;
367     zei->res = res;
368     zei->categoryList = (struct zebraCategoryListInfo *)
369         nmem_malloc(zei->nmem, sizeof(*zei->categoryList));
370     zei->categoryList->sysno = 0;
371     zei->categoryList->dirty = 0;
372     zei->categoryList->data1_categoryList = NULL;
373
374     if ( atoi(res_get_def(res, "notimestamps", "0") )== 0)
375     {
376         time(&our_time);
377         tm = localtime(&our_time);
378         sprintf(zei->date, "%04d%02d%02d%02d%02d%02d",
379                  tm->tm_year+1900, tm->tm_mon+1,  tm->tm_mday,
380                  tm->tm_hour, tm->tm_min, tm->tm_sec);
381     } else {
382         sprintf(zei->date, "%04d%02d%02d%02d%02d%02d",
383                  0, 0, 0,  0, 0, 0);
384     }
385     zdip = &zei->databaseInfo;
386     trec = rec_get_root(records);      /* get "root" record */
387
388     zei->ordinalSU = 1;
389     zei->runNumber = 0;
390
391     zebraExplain_mergeAccessInfo(zei, 0, &zei->accessInfo);
392     if (trec)    /* targetInfo already exists ... */
393     {
394         data1_node *node_tgtinfo, *node_zebra, *node_list, *np;
395
396         zei->data1_target = read_sgml_rec(zei->dh, zei->nmem, trec);
397 #if 0
398         if (!zei->data1_target || !zei->data1_target->u.root.absyn)
399 #else
400         if (!zei->data1_target)
401 #endif
402         {
403             yaz_log(YLOG_FATAL, "Explain schema missing. Check profilePath");
404             nmem_destroy(zei->nmem);
405             return 0;
406         }
407 #if ZINFO_DEBUG
408         data1_pr_tree(zei->dh, zei->data1_target, stderr);
409 #endif
410         node_tgtinfo = data1_search_tag(zei->dh, zei->data1_target,
411                                          "/targetInfo");
412         zebraExplain_mergeAccessInfo(zei, node_tgtinfo,
413                                       &zei->accessInfo);
414
415         node_zebra = data1_search_tag(zei->dh, node_tgtinfo->child,
416                                        "zebraInfo");
417         np = 0;
418         if (node_zebra)
419         {
420             node_list = data1_search_tag(zei->dh, node_zebra->child,
421                                           "databaseList");
422             if (node_list)
423                 np = node_list->child;
424         }
425         for(; np; np = np->next)
426         {
427             data1_node *node_name = NULL;
428             data1_node *node_id = NULL;
429             data1_node *node_aid = NULL;
430             data1_node *np2;
431             if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "database"))
432                 continue;
433             for(np2 = np->child; np2; np2 = np2->next)
434             {
435                 if (np2->which != DATA1N_tag)
436                     continue;
437                 if (!strcmp(np2->u.tag.tag, "name"))
438                     node_name = np2->child;
439                 else if (!strcmp(np2->u.tag.tag, "id"))
440                     node_id = np2->child;
441                 else if (!strcmp(np2->u.tag.tag, "attributeDetailsId"))
442                     node_aid = np2->child;
443             }
444             assert(node_id && node_name && node_aid);
445             
446             *zdip =(struct zebDatabaseInfoB *) 
447                 nmem_malloc(zei->nmem, sizeof(**zdip));
448             (*zdip)->readFlag = 1;
449             (*zdip)->dirty = 0;
450             (*zdip)->data1_database = NULL;
451             (*zdip)->recordCount = 0;
452             (*zdip)->recordBytes = 0;
453             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
454
455             (*zdip)->databaseName = (char *)
456                 nmem_malloc (zei->nmem, 1+node_name->u.data.len);
457             memcpy((*zdip)->databaseName, node_name->u.data.data,
458                    node_name->u.data.len);
459             (*zdip)->databaseName[node_name->u.data.len] = '\0';
460             (*zdip)->sysno = atoi_zn (node_id->u.data.data,
461                                       node_id->u.data.len);
462             (*zdip)->attributeDetails = (zebAttributeDetails)
463                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
464             (*zdip)->attributeDetails->sysno = atoi_zn (node_aid->u.data.data,
465                                                         node_aid->u.data.len);
466             (*zdip)->attributeDetails->readFlag = 1;
467             (*zdip)->attributeDetails->dirty = 0;
468             (*zdip)->attributeDetails->SUInfo = NULL;
469
470             zdip = &(*zdip)->next;
471         }
472         if (node_zebra)
473         {
474             np = data1_search_tag(zei->dh, node_zebra->child,
475                                   "ordinalSU");
476             np = np->child;
477             assert (np && np->which == DATA1N_data);
478             zei->ordinalSU = atoi_n(np->u.data.data, np->u.data.len);
479             
480             np = data1_search_tag(zei->dh, node_zebra->child,
481                                   "ordinalDatabase");
482             np = np->child;
483             assert (np && np->which == DATA1N_data);
484             zei->ordinalDatabase = atoi_n(np->u.data.data, np->u.data.len);
485
486             np = data1_search_tag(zei->dh, node_zebra->child,
487                                    "runNumber");
488             np = np->child;
489             assert (np && np->which == DATA1N_data);
490             zei->runNumber = atoi_zn(np->u.data.data, np->u.data.len);
491             yaz_log(YLOG_DEBUG, "read runnumber=" ZINT_FORMAT, zei->runNumber);
492             *zdip = NULL;
493         }
494         rec_rm(&trec);
495     }
496     else  /* create initial targetInfo */
497     {
498         data1_node *node_tgtinfo;
499
500         *zdip = NULL;
501         if (writeFlag)
502         {
503             char *sgml_buf;
504             int sgml_len;
505
506             zei->data1_target =
507                 data1_read_sgml(zei->dh, zei->nmem,
508                                  "<explain><targetInfo>TargetInfo\n"
509                                  "<name>Zebra</>\n"
510                                  "<namedResultSets>1</>\n"
511                                  "<multipleDBSearch>1</>\n"
512                                  "<nicknames><name>Zebra</></>\n"
513                                  "</></>\n" );
514             if (!zei->data1_target)
515             {
516                 yaz_log(YLOG_FATAL, "Explain schema missing. Check profilePath");
517                 nmem_destroy(zei->nmem);
518                 return 0;
519             }
520             node_tgtinfo = data1_search_tag(zei->dh, zei->data1_target,
521                                              "/targetInfo");
522             assert(node_tgtinfo);
523
524             zebraExplain_initCommonInfo(zei, node_tgtinfo);
525             zebraExplain_initAccessInfo(zei, node_tgtinfo);
526
527             /* write now because we want to be sure about the sysno */
528             trec = rec_new(records);
529             if (!trec)
530             {
531                 yaz_log(YLOG_FATAL, "Cannot create root Explain record");
532                 nmem_destroy(zei->nmem);
533                 return 0;
534             }
535             trec->info[recInfo_fileType] =
536                 rec_strdup("grs.sgml", &trec->size[recInfo_fileType]);
537             trec->info[recInfo_databaseName] =
538                 rec_strdup("IR-Explain-1", &trec->size[recInfo_databaseName]);
539             
540             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
541             trec->info[recInfo_storeData] = (char *) xmalloc(sgml_len);
542             memcpy(trec->info[recInfo_storeData], sgml_buf, sgml_len);
543             trec->size[recInfo_storeData] = sgml_len;
544                 
545             rec_put(records, &trec);
546             rec_rm(&trec);
547         }
548         
549         zebraExplain_newDatabase(zei, "IR-Explain-1", 0);
550             
551         if (!zei->categoryList->dirty)
552         {
553             struct zebraCategoryListInfo *zcl = zei->categoryList;
554             data1_node *node_cl;
555             
556             zcl->dirty = 1;
557             zcl->data1_categoryList =
558                 data1_read_sgml(zei->dh, zei->nmem,
559                                  "<explain><categoryList>CategoryList\n"
560                                  "</></>\n");
561         
562             if (zcl->data1_categoryList)
563             {
564                 node_cl = data1_search_tag(zei->dh, zcl->data1_categoryList,
565                                             "/categoryList");
566                 assert(node_cl);
567                 zebraExplain_initCommonInfo(zei, node_cl);
568             }
569         }
570     }
571     return zei;
572 }
573
574 static void zebraExplain_readAttributeDetails(ZebraExplainInfo zei,
575                                                zebAttributeDetails zad)
576 {
577     Record rec;
578     struct zebSUInfoB **zsuip = &zad->SUInfo;
579     data1_node *node_adinfo, *node_zebra, *node_list, *np;
580
581     assert (zad->sysno);
582     rec = rec_get(zei->records, zad->sysno);
583
584     zad->data1_tree = read_sgml_rec(zei->dh, zei->nmem, rec);
585
586     node_adinfo = data1_search_tag(zei->dh, zad->data1_tree,
587                                     "/attributeDetails");
588     node_zebra = data1_search_tag(zei->dh, node_adinfo->child,
589                                  "zebraInfo");
590     node_list = data1_search_tag(zei->dh, node_zebra->child,
591                                   "attrlist");
592     for (np = node_list->child; np; np = np->next)
593     {
594         data1_node *node_set = NULL;
595         data1_node *node_use = NULL;
596         data1_node *node_str = NULL;
597         data1_node *node_ordinal = NULL;
598         data1_node *node_type = NULL;
599         data1_node *node_doc_occurrences = NULL;
600         data1_node *node_term_occurrences = NULL;
601         data1_node *np2;
602         char oid_str[128];
603         int oid_str_len;
604
605         if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "attr"))
606             continue;
607         for (np2 = np->child; np2; np2 = np2->next)
608         {
609             if (np2->which != DATA1N_tag || !np2->child ||
610                 np2->child->which != DATA1N_data)
611                 continue;
612             if (!strcmp(np2->u.tag.tag, "set"))
613                 node_set = np2->child;
614             else if (!strcmp(np2->u.tag.tag, "use"))
615                 node_use = np2->child;
616             else if (!strcmp(np2->u.tag.tag, "str"))
617                 node_str = np2->child;
618             else if (!strcmp(np2->u.tag.tag, "ordinal"))
619                 node_ordinal = np2->child;
620             else if (!strcmp(np2->u.tag.tag, "type"))
621                 node_type = np2->child;
622             else if (!strcmp(np2->u.tag.tag, "dococcurrences"))
623                 node_doc_occurrences = np2->child;
624             else if (!strcmp(np2->u.tag.tag, "termoccurrences"))
625                 node_term_occurrences = np2->child;
626             else
627             {
628                 yaz_log(YLOG_LOG, "Unknown tag '%s' in attributeDetails",
629                         np2->u.tag.tag);
630             }
631         }
632         assert(node_ordinal);
633
634         *zsuip = (struct zebSUInfoB *)
635             nmem_malloc(zei->nmem, sizeof(**zsuip));
636
637         if (node_type && node_type->u.data.len > 0)
638             (*zsuip)->info.index_type =  node_type->u.data.data[0];
639         else
640         {
641             yaz_log(YLOG_WARN, "Missing attribute 'type' in attribute info");
642             (*zsuip)->info.index_type = 'w';
643         }
644
645         if (node_doc_occurrences)
646         {
647             data1_node *np = node_doc_occurrences;
648             (*zsuip)->info.doc_occurrences = atoi_zn(np->u.data.data,
649                                                      np->u.data.len);
650         }
651         if (node_term_occurrences)
652         {
653             data1_node *np = node_term_occurrences;
654             (*zsuip)->info.term_occurrences = atoi_zn(np->u.data.data,
655                                                       np->u.data.len);
656         }
657         if (node_set && node_use)
658         {
659             (*zsuip)->info.which = ZEB_SU_SET_USE;
660             
661             oid_str_len = node_set->u.data.len;
662             if (oid_str_len >= (int) sizeof(oid_str))
663                 oid_str_len = sizeof(oid_str)-1;
664             memcpy(oid_str, node_set->u.data.data, oid_str_len);
665             oid_str[oid_str_len] = '\0';
666
667             (*zsuip)->info.u.su.set = oid_getvalbyname(oid_str);
668             
669             (*zsuip)->info.u.su.use = atoi_n(node_use->u.data.data,
670                                          node_use->u.data.len);
671             yaz_log(YLOG_DEBUG, "set=%d use=%d ordinal=%d",
672                      (*zsuip)->info.u.su.set, (*zsuip)->info.u.su.use,
673                      (*zsuip)->info.ordinal);
674         }
675         else if (node_str)
676         {
677             (*zsuip)->info.which = ZEB_SU_STR;
678             
679             (*zsuip)->info.u.str = nmem_strdupn(zei->nmem,
680                                                 node_str->u.data.data,
681                                                 node_str->u.data.len);
682         }
683         else
684         {
685             yaz_log(YLOG_WARN, "Missing set/use/str in attribute info");
686             continue;
687         }
688         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
689                                          node_ordinal->u.data.len);
690         zsuip = &(*zsuip)->next;
691     }
692     *zsuip = NULL;
693     zad->readFlag = 0;
694     rec_rm (&rec);
695 }
696
697 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
698                                        struct zebDatabaseInfoB *zdi)
699 {
700     Record rec;
701     data1_node *node_dbinfo, *node_zebra, *np;
702
703     assert (zdi->sysno);
704     rec = rec_get (zei->records, zdi->sysno);
705
706     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
707     
708     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
709                                     "/databaseInfo");
710     assert (node_dbinfo);
711     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
712
713     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
714                                  "zebraInfo");
715     if (node_zebra
716         && (np = data1_search_tag (zei->dh, node_zebra->child,
717                                    "recordBytes")) 
718         && np->child && np->child->which == DATA1N_data)
719         zdi->recordBytes = atoi_zn (np->child->u.data.data,
720                                     np->child->u.data.len);
721
722     if (node_zebra
723         && (np = data1_search_tag (zei->dh, node_zebra->child,
724                                    "ordinalDatabase")) 
725         && np->child && np->child->which == DATA1N_data)
726         zdi->ordinalDatabase = atoi_n(np->child->u.data.data,
727                                       np->child->u.data.len);
728
729     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
730                                 "recordCount")) &&
731         (np = data1_search_tag (zei->dh, np->child,
732                                 "recordCountActual")) &&
733         np->child->which == DATA1N_data)
734     {
735         zdi->recordCount = atoi_zn (np->child->u.data.data,
736                                     np->child->u.data.len);
737     }
738     zdi->readFlag = 0;
739     rec_rm (&rec);
740 }
741
742 int zebraExplain_removeDatabase(ZebraExplainInfo zei, void *update_handle)
743 {
744     struct zebDatabaseInfoB **zdip = &zei->databaseInfo;
745
746     while (*zdip)
747     {
748         if (*zdip == zei->curDatabaseInfo)
749         {
750             struct zebDatabaseInfoB *zdi = *zdip;
751             Record rec;
752
753             zei->dirty = 1;
754             zei->updateHandle = update_handle;
755
756             if (zdi->attributeDetails)
757             {
758                 /* remove attribute details keys and delete it */
759                 zebAttributeDetails zad = zdi->attributeDetails;
760                 
761                 rec = rec_get(zei->records, zad->sysno);
762                 (*zei->updateFunc)(zei->updateHandle, rec, 0);
763                 rec_rm(&rec);
764             }
765             /* remove database record keys and delete it */
766             rec = rec_get (zei->records, zdi->sysno);
767             (*zei->updateFunc)(zei->updateHandle, rec, 0);
768             rec_rm(&rec);
769
770             /* remove from list */
771             *zdip = zdi->next;
772
773             /* current database is IR-Explain-1 */
774             return 0;
775         }
776         zdip = &(*zdip)->next;
777     }
778     return -1;
779 }
780
781 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
782 {
783     struct zebDatabaseInfoB *zdi;
784     const char *database_n = strrchr (database, '/');
785
786     if (database_n)
787         database_n++;
788     else
789         database_n = database;
790     
791     assert (zei);
792     if (zei->curDatabaseInfo &&
793         !STRCASECMP (zei->curDatabaseInfo->databaseName, database))
794         return 0;
795     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
796     {
797         if (!STRCASECMP (zdi->databaseName, database_n))
798             break;
799     }
800     if (!zdi)
801         return -1;
802 #if ZINFO_DEBUG
803     yaz_log(YLOG_LOG, "zebraExplain_curDatabase: %s", database);
804 #endif
805     if (zdi->readFlag)
806     {
807 #if ZINFO_DEBUG
808         yaz_log(YLOG_LOG, "zebraExplain_readDatabase: %s", database);
809 #endif
810         zebraExplain_readDatabase (zei, zdi);
811     }
812     if (zdi->attributeDetails->readFlag)
813     {
814 #if ZINFO_DEBUG
815         yaz_log(YLOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
816 #endif
817         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
818     }
819     zei->curDatabaseInfo = zdi;
820     return 0;
821 }
822
823 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
824 {
825     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "commonInfo", 0, n);
826     data1_mk_tag_data_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
827     data1_mk_tag_data_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
828     data1_mk_tag_data_text (zei->dh, c, "languageCode", "EN", zei->nmem);
829 }
830
831 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
832 {
833     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
834     assert (c);
835     data1_mk_tag_data_text_uni (zei->dh, c, "dateChanged", zei->date,
836                                 zei->nmem);
837 }
838
839 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
840 {
841     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "accessInfo", 0, n);
842     data1_node *d = data1_mk_tag (zei->dh, zei->nmem, "unitSystems", 0, c);
843     data1_mk_tag_data_text (zei->dh, d, "string", "ISO", zei->nmem);
844 }
845
846 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
847                                            zebAccessInfo accessInfo)
848 {
849     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
850     data1_node *d;
851     zebAccessObject p;
852     
853     if (!c)
854     {
855         data1_pr_tree (zei->dh, n, stdout);
856         exit (0);
857         assert (c);
858     }
859
860     if ((p = accessInfo->attributeSetIds))
861     {
862         d = data1_mk_tag_uni (zei->dh, zei->nmem, "attributeSetIds", c);
863         for (; p; p = p->next)
864             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
865     }
866     if ((p = accessInfo->schemas))
867     {
868         d = data1_mk_tag_uni (zei->dh, zei->nmem, "schemas", c);
869         for (; p; p = p->next)
870             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
871     }
872 }
873
874 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
875                               int explain_database)
876 {
877     struct zebDatabaseInfoB *zdi;
878     data1_node *node_dbinfo, *node_adinfo;
879     const char *database_n = strrchr (database, '/');
880
881     if (database_n)
882         database_n++;
883     else
884         database_n = database;
885
886 #if ZINFO_DEBUG
887     yaz_log(YLOG_LOG, "zebraExplain_newDatabase: %s", database);
888 #endif
889     assert (zei);
890     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
891     {
892         if (!STRCASECMP (zdi->databaseName, database_n))
893             break;
894     }
895     if (zdi)
896         return -1;
897     /* it's new really. make it */
898     zdi = (struct zebDatabaseInfoB *) nmem_malloc (zei->nmem, sizeof(*zdi));
899     zdi->next = zei->databaseInfo;
900     zei->databaseInfo = zdi;
901     zdi->sysno = 0;
902     zdi->recordCount = 0;
903     zdi->recordBytes = 0;
904     zdi->readFlag = 0;
905     zdi->databaseName = nmem_strdup (zei->nmem, database_n);
906
907     zdi->ordinalDatabase = zei->ordinalDatabase++;
908
909     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
910     
911     assert (zei->dh);
912     assert (zei->nmem);
913
914     zdi->data1_database =
915         data1_read_sgml (zei->dh, zei->nmem, 
916                          "<explain><databaseInfo>DatabaseInfo\n"
917                          "</></>\n");
918     if (!zdi->data1_database)
919         return -2;
920
921     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
922                                     "/databaseInfo");
923     assert (node_dbinfo);
924
925     zebraExplain_initCommonInfo (zei, node_dbinfo);
926     zebraExplain_initAccessInfo (zei, node_dbinfo);
927
928     data1_mk_tag_data_text (zei->dh, node_dbinfo, "name",
929                                database, zei->nmem);
930     
931     if (explain_database)
932         data1_mk_tag_data_text (zei->dh, node_dbinfo, "explainDatabase",
933                                 "", zei->nmem);
934     
935     data1_mk_tag_data_text (zei->dh, node_dbinfo, "userFee",
936                             "0", zei->nmem);
937     
938     data1_mk_tag_data_text (zei->dh, node_dbinfo, "available",
939                             "1", zei->nmem);
940     
941 #if ZINFO_DEBUG
942     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
943 #endif
944     zdi->dirty = 1;
945     zei->dirty = 1;
946     zei->curDatabaseInfo = zdi;
947
948     zdi->attributeDetails = (zebAttributeDetails)
949         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
950     zdi->attributeDetails->readFlag = 0;
951     zdi->attributeDetails->sysno = 0;
952     zdi->attributeDetails->dirty = 1;
953     zdi->attributeDetails->SUInfo = NULL;
954     zdi->attributeDetails->data1_tree =
955         data1_read_sgml (zei->dh, zei->nmem,
956                          "<explain><attributeDetails>AttributeDetails\n"
957                          "</></>\n");
958
959     node_adinfo = data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree,
960                                     "/attributeDetails");
961     assert (node_adinfo);
962
963     zebraExplain_initCommonInfo (zei, node_adinfo);
964
965     return 0;
966 }
967
968 static void writeAttributeValueDetails (ZebraExplainInfo zei,
969                                   zebAttributeDetails zad,
970                                   data1_node *node_atvs, data1_attset *attset)
971
972 {
973     struct zebSUInfoB *zsui;
974     int set_ordinal = attset->reference;
975     data1_attset_child *c;
976
977     for (c = attset->children; c; c = c->next)
978         writeAttributeValueDetails (zei, zad, node_atvs, c->child);
979     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
980     {
981         if (zsui->info.which == ZEB_SU_SET_USE && 
982             set_ordinal == zsui->info.u.su.set)
983         {
984             data1_node *node_attvalue, *node_value;
985             node_attvalue = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
986                                           0 /* attr */, node_atvs);
987             node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
988                                        0 /* attr */, node_attvalue);
989             data1_mk_tag_data_int (zei->dh, node_value, "numeric",
990                                    zsui->info.u.su.use, zei->nmem);
991         }
992     }
993 }
994
995 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
996                                             struct zebraCategoryListInfo *zcl,
997                                             int key_flush)
998 {
999     char *sgml_buf;
1000     int sgml_len;
1001     int i;
1002     Record drec;
1003     data1_node *node_ci, *node_categoryList;
1004     SYSNO sysno = 0;
1005     static char *category[] = {
1006         "CategoryList",
1007         "TargetInfo",
1008         "DatabaseInfo",
1009         "AttributeDetails",
1010         NULL
1011     };
1012
1013     assert (zcl);
1014     if (!zcl->dirty)
1015         return ;
1016     zcl->dirty = 1;
1017     node_categoryList = zcl->data1_categoryList;
1018
1019 #if ZINFO_DEBUG
1020     yaz_log(YLOG_LOG, "zebraExplain_writeCategoryList");
1021 #endif
1022
1023     drec = createRecord (zei->records, &sysno);
1024     if (!drec)
1025         return;
1026     
1027     node_ci = data1_search_tag (zei->dh, node_categoryList,
1028                                 "/categoryList");
1029     assert (node_ci);
1030     node_ci = data1_mk_tag (zei->dh, zei->nmem, "categories", 0 /* attr */,
1031                             node_ci);
1032     assert (node_ci);
1033     
1034     for (i = 0; category[i]; i++)
1035     {
1036         data1_node *node_cat = data1_mk_tag (zei->dh, zei->nmem,  "category",
1037                                              0 /* attr */, node_ci);
1038
1039         data1_mk_tag_data_text (zei->dh, node_cat, "name",
1040                                 category[i], zei->nmem);
1041     }
1042     /* extract *searchable* keys from it. We do this here, because
1043        record count, etc. is affected */
1044     if (key_flush)
1045         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
1046
1047     /* convert to "SGML" and write it */
1048 #if ZINFO_DEBUG
1049     data1_pr_tree (zei->dh, node_categoryList, stderr);
1050 #endif
1051     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList, 0, &sgml_len);
1052     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1053     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1054     drec->size[recInfo_storeData] = sgml_len;
1055     
1056     rec_put (zei->records, &drec);
1057 }
1058
1059 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
1060                                                 zebAttributeDetails zad,
1061                                                 const char *databaseName,
1062                                                 int key_flush)
1063 {
1064     char *sgml_buf;
1065     int sgml_len;
1066     Record drec;
1067     data1_node *node_adinfo, *node_list, *node_zebra, *node_attributesBySet;
1068     struct zebSUInfoB *zsui;
1069     int set_min;
1070     
1071     if (!zad->dirty)
1072         return;
1073     
1074     zad->dirty = 0;
1075 #if ZINFO_DEBUG
1076     yaz_log(YLOG_LOG, "zebraExplain_writeAttributeDetails");    
1077 #endif
1078
1079     drec = createRecord (zei->records, &zad->sysno);
1080     if (!drec)
1081         return;
1082     assert (zad->data1_tree);
1083
1084     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
1085                                    "/attributeDetails");
1086     zebraExplain_updateCommonInfo (zei, node_adinfo);
1087
1088     data1_mk_tag_data_text (zei->dh, node_adinfo, "name",
1089                             databaseName, zei->nmem);
1090
1091     /* extract *searchable* keys from it. We do this here, because
1092        record count, etc. is affected */
1093     if (key_flush)
1094         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
1095
1096     node_attributesBySet = data1_mk_tag_uni (zei->dh, zei->nmem,
1097                                            "attributesBySet", node_adinfo);
1098     set_min = -1;
1099     while (1)
1100     {
1101         data1_node *node_asd;
1102         data1_attset *attset;
1103         int set_ordinal = -1;
1104         for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1105         {
1106             if (zsui->info.which == ZEB_SU_SET_USE &&
1107                 (set_ordinal < 0 || set_ordinal > zsui->info.u.su.set)
1108                 && zsui->info.u.su.set > set_min)
1109                 set_ordinal = zsui->info.u.su.set;
1110         }
1111         if (set_ordinal < 0)
1112             break;
1113         set_min = set_ordinal;
1114         node_asd = data1_mk_tag (zei->dh, zei->nmem,
1115                                  "attributeSetDetails",
1116                                  0 /* attr */, node_attributesBySet);
1117
1118         attset = data1_attset_search_id (zei->dh, set_ordinal);
1119         if (!attset)
1120         {
1121             zebraExplain_loadAttsets (zei->dh, zei->res);
1122             attset = data1_attset_search_id (zei->dh, set_ordinal);
1123         }
1124         if (attset)
1125         {
1126             int oid[OID_SIZE];
1127             oident oe;
1128             
1129             oe.proto = PROTO_Z3950;
1130             oe.oclass = CLASS_ATTSET;
1131             oe.value = (enum oid_value) set_ordinal;
1132             
1133             if (oid_ent_to_oid (&oe, oid))
1134             {
1135                 data1_node *node_abt, *node_atd, *node_atvs;
1136                 data1_mk_tag_data_oid (zei->dh, node_asd, "oid",
1137                                        oid, zei->nmem);
1138                 
1139                 node_abt = data1_mk_tag (zei->dh, zei->nmem,
1140                                          "attributesByType",
1141                                          0 /*attr */, node_asd);
1142                 node_atd = data1_mk_tag (zei->dh, zei->nmem,
1143                                          "attributeTypeDetails", 
1144                                          0 /* attr */, node_abt);
1145                 data1_mk_tag_data_int (zei->dh, node_atd,
1146                                        "type", 1, zei->nmem);
1147                 node_atvs = data1_mk_tag (zei->dh, zei->nmem, 
1148                                           "attributeValues",
1149                                           0 /* attr */, node_atd);
1150                 writeAttributeValueDetails (zei, zad, node_atvs, attset);
1151             }
1152         }
1153     }
1154     /* zebra info (private) */
1155     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1156                                  "zebraInfo", node_adinfo);
1157     node_list = data1_mk_tag_uni (zei->dh, zei->nmem,
1158                                  "attrlist", node_zebra);
1159     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1160     {
1161         struct oident oident;
1162         int oid[OID_SIZE];
1163         data1_node *node_attr;
1164         char index_type_str[2];
1165
1166         
1167         node_attr = data1_mk_tag (zei->dh, zei->nmem, "attr", 0 /* attr */,
1168                                   node_list);
1169
1170         index_type_str[0] = zsui->info.index_type;
1171         index_type_str[1] = '\0';
1172         data1_mk_tag_data_text (zei->dh, node_attr, "type",
1173                                 index_type_str, zei->nmem);
1174         if (zsui->info.which == ZEB_SU_SET_USE)
1175         {
1176             oident.proto = PROTO_Z3950;
1177             oident.oclass = CLASS_ATTSET;
1178             oident.value = (enum oid_value) zsui->info.u.su.set;
1179             oid_ent_to_oid (&oident, oid);
1180             
1181             data1_mk_tag_data_text (zei->dh, node_attr, "set",
1182                                     oident.desc, zei->nmem);
1183             data1_mk_tag_data_int (zei->dh, node_attr, "use",
1184                                    zsui->info.u.su.use, zei->nmem);
1185         }
1186         else if (zsui->info.which == ZEB_SU_STR)
1187         {
1188             data1_mk_tag_data_text (zei->dh, node_attr, "str",
1189                                     zsui->info.u.str, zei->nmem);
1190         }
1191         data1_mk_tag_data_int (zei->dh, node_attr, "ordinal",
1192                                zsui->info.ordinal, zei->nmem);
1193
1194         data1_mk_tag_data_zint (zei->dh, node_attr, "dococcurrences",
1195                                 zsui->info.doc_occurrences, zei->nmem);
1196         data1_mk_tag_data_zint (zei->dh, node_attr, "termoccurrences",
1197                                 zsui->info.term_occurrences, zei->nmem);
1198     }
1199     /* convert to "SGML" and write it */
1200 #if ZINFO_DEBUG
1201     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
1202 #endif
1203     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
1204                                   0, &sgml_len);
1205     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1206     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1207     drec->size[recInfo_storeData] = sgml_len;
1208     
1209     rec_put (zei->records, &drec);
1210 }
1211
1212 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
1213                                         struct zebDatabaseInfoB *zdi,
1214                                         int key_flush)
1215 {
1216     char *sgml_buf;
1217     int sgml_len;
1218     Record drec;
1219     data1_node *node_dbinfo, *node_count, *node_zebra;
1220     
1221     if (!zdi->dirty)
1222         return;
1223
1224     zdi->dirty = 0;
1225 #if ZINFO_DEBUG
1226     yaz_log(YLOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1227 #endif
1228     drec = createRecord (zei->records, &zdi->sysno);
1229     if (!drec)
1230         return;
1231     assert (zdi->data1_database);
1232
1233     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
1234                                     "/databaseInfo");
1235
1236     assert (node_dbinfo);
1237     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1238     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1239
1240     /* extract *searchable* keys from it. We do this here, because
1241        record count, etc. is affected */
1242     if (key_flush)
1243         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1244     /* record count */
1245     node_count = data1_mk_tag_uni (zei->dh, zei->nmem,
1246                                  "recordCount", node_dbinfo);
1247     data1_mk_tag_data_zint (zei->dh, node_count, "recordCountActual",
1248                             zdi->recordCount, zei->nmem);
1249
1250     /* zebra info (private) */
1251     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1252                                  "zebraInfo", node_dbinfo);
1253     data1_mk_tag_data_zint (zei->dh, node_zebra,
1254                            "recordBytes", zdi->recordBytes, zei->nmem);
1255
1256     data1_mk_tag_data_zint(zei->dh, node_zebra,
1257                            "ordinalDatabase", zdi->ordinalDatabase, zei->nmem);
1258
1259     /* convert to "SGML" and write it */
1260 #if ZINFO_DEBUG
1261     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1262 #endif
1263     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1264                                   0, &sgml_len);
1265     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1266     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1267     drec->size[recInfo_storeData] = sgml_len;
1268     
1269     rec_put (zei->records, &drec);
1270 }
1271
1272 static void writeAttributeValues (ZebraExplainInfo zei,
1273                                   data1_node *node_values,
1274                                   data1_attset *attset)
1275 {
1276     data1_att *atts;
1277     data1_attset_child *c;
1278
1279     if (!attset)
1280         return;
1281
1282     for (c = attset->children; c; c = c->next)
1283         writeAttributeValues (zei, node_values, c->child);
1284     for (atts = attset->atts; atts; atts = atts->next)
1285     {
1286         data1_node *node_value;
1287         
1288         node_value = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
1289                                    0 /* attr */, node_values);
1290         data1_mk_tag_data_text (zei->dh, node_value, "name",
1291                                 atts->name, zei->nmem);
1292         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
1293                                    0 /* attr */, node_value);
1294         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
1295                                atts->value, zei->nmem);
1296     }
1297 }
1298
1299
1300 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1301                                             zebAccessObject o,
1302                                             int key_flush)
1303 {
1304     char *sgml_buf;
1305     int sgml_len;
1306     Record drec;
1307     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1308     data1_node *node_values;
1309     struct oident *entp;
1310     struct data1_attset *attset = NULL;
1311     
1312     if ((entp = oid_getentbyoid (o->oid)))
1313         attset = data1_attset_search_id (zei->dh, entp->value);
1314             
1315 #if ZINFO_DEBUG
1316     yaz_log(YLOG_LOG, "zebraExplain_writeAttributeSet %s",
1317           attset ? attset->name : "<unknown>");    
1318 #endif
1319
1320     drec = createRecord (zei->records, &o->sysno);
1321     if (!drec)
1322         return;
1323     node_root =
1324         data1_read_sgml (zei->dh, zei->nmem,
1325                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1326                          "</></>\n" );
1327
1328     node_attinfo = data1_search_tag (zei->dh, node_root,
1329                                    "/attributeSetInfo");
1330
1331     assert (node_attinfo);
1332     zebraExplain_initCommonInfo (zei, node_attinfo);
1333     zebraExplain_updateCommonInfo (zei, node_attinfo);
1334
1335     data1_mk_tag_data_oid (zei->dh, node_attinfo,
1336                             "oid", o->oid, zei->nmem);
1337     if (attset && attset->name)
1338         data1_mk_tag_data_text (zei->dh, node_attinfo,
1339                                 "name", attset->name, zei->nmem);
1340     
1341     node_attributes = data1_mk_tag_uni (zei->dh, zei->nmem,
1342                                       "attributes", node_attinfo);
1343     node_atttype = data1_mk_tag_uni (zei->dh, zei->nmem,
1344                                    "attributeType", node_attributes);
1345     data1_mk_tag_data_text (zei->dh, node_atttype,
1346                             "name", "Use", zei->nmem);
1347     data1_mk_tag_data_text (zei->dh, node_atttype,
1348                             "description", "Use Attribute", zei->nmem);
1349     data1_mk_tag_data_int (zei->dh, node_atttype,
1350                            "type", 1, zei->nmem);
1351     node_values = data1_mk_tag (zei->dh, zei->nmem,
1352                                 "attributeValues", 0 /* attr */, node_atttype);
1353     if (attset)
1354         writeAttributeValues (zei, node_values, attset);
1355
1356     /* extract *searchable* keys from it. We do this here, because
1357        record count, etc. is affected */
1358     if (key_flush)
1359         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1360     /* convert to "SGML" and write it */
1361 #if ZINFO_DEBUG
1362     data1_pr_tree (zei->dh, node_root, stderr);
1363 #endif
1364     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 0, &sgml_len);
1365     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1366     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1367     drec->size[recInfo_storeData] = sgml_len;
1368     
1369     rec_put (zei->records, &drec);
1370 }
1371
1372 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush)
1373 {
1374     struct zebDatabaseInfoB *zdi;
1375     data1_node *node_tgtinfo, *node_list, *node_zebra;
1376     Record trec;
1377     int sgml_len;
1378     char *sgml_buf;
1379
1380     if (!zei->dirty)
1381         return;
1382     zei->dirty = 0;
1383
1384     trec = rec_get_root(zei->records);
1385     xfree (trec->info[recInfo_storeData]);
1386
1387     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
1388                                      "/targetInfo");
1389     assert (node_tgtinfo);
1390
1391     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1392     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1393
1394     /* convert to "SGML" and write it */
1395     if (key_flush)
1396         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1397
1398     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1399                                  "zebraInfo", node_tgtinfo);
1400     data1_mk_tag_data_text (zei->dh, node_zebra, "version",
1401                                ZEBRAVER, zei->nmem);
1402     node_list = data1_mk_tag (zei->dh, zei->nmem,
1403                               "databaseList", 0 /* attr */, node_zebra);
1404     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1405     {
1406         data1_node *node_db;
1407         node_db = data1_mk_tag (zei->dh, zei->nmem,
1408                                 "database", 0 /* attr */, node_list);
1409         data1_mk_tag_data_text (zei->dh, node_db, "name",
1410                                 zdi->databaseName, zei->nmem);
1411         data1_mk_tag_data_zint (zei->dh, node_db, "id",
1412                                 zdi->sysno, zei->nmem);
1413         data1_mk_tag_data_zint (zei->dh, node_db, "attributeDetailsId",
1414                                 zdi->attributeDetails->sysno, zei->nmem);
1415     }
1416     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalSU",
1417                            zei->ordinalSU, zei->nmem);
1418
1419     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalDatabase",
1420                            zei->ordinalDatabase, zei->nmem);
1421
1422     data1_mk_tag_data_zint (zei->dh, node_zebra, "runNumber",
1423                             zei->runNumber, zei->nmem);
1424
1425 #if ZINFO_DEBUG
1426     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1427 #endif
1428     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1429                                   0, &sgml_len);
1430     trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1431     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1432     trec->size[recInfo_storeData] = sgml_len;
1433     
1434     rec_put (zei->records, &trec);
1435 }
1436
1437 int zebraExplain_lookup_attr_su_any_index(ZebraExplainInfo zei,
1438                                           int set, int use)
1439 {
1440     struct zebSUInfoB *zsui;
1441
1442     assert (zei->curDatabaseInfo);
1443     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1444          zsui; zsui=zsui->next)
1445         if (zsui->info.which == ZEB_SU_SET_USE &&
1446             zsui->info.u.su.use == use && zsui->info.u.su.set == set)
1447             return zsui->info.ordinal;
1448     return -1;
1449 }
1450
1451 int zebraExplain_lookup_attr_su(ZebraExplainInfo zei, int index_type,
1452                                 int set, int use)
1453 {
1454     struct zebSUInfoB **zsui;
1455
1456 #if 0
1457     yaz_log(YLOG_LOG, "lookup_attr_su index_type=%d set=%d use=%d",
1458             index_type, set, use);
1459 #endif
1460     assert (zei->curDatabaseInfo);
1461     for (zsui = &zei->curDatabaseInfo->attributeDetails->SUInfo;
1462          *zsui; zsui = &(*zsui)->next)
1463         if ((*zsui)->info.index_type == index_type &&
1464             (*zsui)->info.which == ZEB_SU_SET_USE &&
1465             (*zsui)->info.u.su.use == use && (*zsui)->info.u.su.set == set)
1466         {
1467             struct zebSUInfoB *zsui_this = *zsui;
1468
1469             /* take it out of the list and move to front */
1470             *zsui = (*zsui)->next;
1471             zsui_this->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1472             zei->curDatabaseInfo->attributeDetails->SUInfo = zsui_this;
1473
1474             return zsui_this->info.ordinal;
1475         }
1476     return -1;
1477 }
1478
1479 int zebraExplain_lookup_attr_str(ZebraExplainInfo zei, int index_type,
1480                                  const char *str)
1481 {
1482     struct zebSUInfoB **zsui;
1483
1484     assert (zei->curDatabaseInfo);
1485     for (zsui = &zei->curDatabaseInfo->attributeDetails->SUInfo;
1486          *zsui; zsui = &(*zsui)->next)
1487         if ((*zsui)->info.index_type == index_type
1488             && (*zsui)->info.which == ZEB_SU_STR 
1489             && !strcmp((*zsui)->info.u.str, str))
1490         {
1491             struct zebSUInfoB *zsui_this = *zsui;
1492
1493             /* take it out of the list and move to front */
1494             *zsui = (*zsui)->next;
1495             zsui_this->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1496             zei->curDatabaseInfo->attributeDetails->SUInfo = zsui_this;
1497
1498             return zsui_this->info.ordinal;
1499         }
1500     return -1;
1501 }
1502
1503 int zebraExplain_trav_ord(ZebraExplainInfo zei, void *handle,
1504                           int (*f)(void *handle, int ord))
1505 {
1506     struct zebDatabaseInfoB *zdb = zei->curDatabaseInfo;
1507     if (zdb)
1508     {
1509         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1510         for ( ;zsui; zsui = zsui->next)
1511             (*f)(handle,  zsui->info.ordinal);
1512     }
1513     return 0;
1514 }
1515
1516
1517 struct zebSUInfoB *zebraExplain_get_sui_info (ZebraExplainInfo zei, int ord,
1518                                               int dirty_mark,
1519                                               const char **db)
1520 {
1521     struct zebDatabaseInfoB *zdb;
1522
1523     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1524     {
1525         struct zebSUInfoB **zsui;
1526
1527         if (zdb->attributeDetails->readFlag)
1528             zebraExplain_readAttributeDetails (zei, zdb->attributeDetails);
1529
1530         for (zsui = &zdb->attributeDetails->SUInfo; *zsui;
1531              zsui = &(*zsui)->next)
1532             if ((*zsui)->info.ordinal == ord)
1533             {
1534                 struct zebSUInfoB *zsui_this = *zsui;
1535                 
1536                 /* take it out of the list and move to front */
1537                 *zsui = (*zsui)->next;
1538                 zsui_this->next = zdb->attributeDetails->SUInfo;
1539                 zdb->attributeDetails->SUInfo = zsui_this;
1540
1541                 if (dirty_mark)
1542                     zdb->attributeDetails->dirty = 1;
1543                 if (db)
1544                     *db = zdb->databaseName;
1545                 return zsui_this;
1546             }
1547     }
1548     return 0;
1549 }
1550
1551
1552
1553 int zebraExplain_ord_adjust_occurrences(ZebraExplainInfo zei, int ord,
1554                                         int term_delta, int doc_delta)
1555 {
1556     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 1, 0);
1557     if (zsui)
1558     {
1559         zsui->info.term_occurrences += term_delta;
1560         zsui->info.doc_occurrences += doc_delta;
1561         return 0;
1562     }
1563     return -1;
1564 }
1565
1566 int zebraExplain_ord_get_occurrences(ZebraExplainInfo zei, int ord,
1567                                      zint *term_occurrences,
1568                                      zint *doc_occurrences)
1569 {
1570     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1571     if (zsui)
1572     {
1573         *term_occurrences = zsui->info.term_occurrences;
1574         *doc_occurrences = zsui->info.doc_occurrences;
1575         return 0;
1576     }
1577     return -1;
1578 }
1579
1580 zint zebraExplain_ord_get_doc_occurrences(ZebraExplainInfo zei, int ord)
1581 {
1582     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1583     if (zsui)
1584         return zsui->info.doc_occurrences;
1585     return 0;
1586 }
1587
1588 zint zebraExplain_ord_get_term_occurrences(ZebraExplainInfo zei, int ord)
1589 {
1590     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1591     if (zsui)
1592         return zsui->info.term_occurrences;
1593     return 0;
1594 }
1595
1596 int zebraExplain_lookup_ord(ZebraExplainInfo zei, int ord,
1597                             int *index_type, 
1598                             const char **db,
1599                             int *set, int *use,
1600                             const char **string_index)
1601 {
1602     struct zebSUInfoB *zsui;
1603
1604     if (set)
1605         *set = -1;
1606     if (use)
1607         *use = -1;
1608     if (index_type)
1609         *index_type = 0;
1610     if (string_index)
1611         *string_index = 0;
1612
1613     zsui = zebraExplain_get_sui_info(zei, ord, 0, db);
1614     if (zsui)
1615     {
1616         if (zsui->info.which == ZEB_SU_SET_USE)
1617         {
1618             if (set)
1619                 *set = zsui->info.u.su.set;
1620             if (use)
1621                 *use = zsui->info.u.su.use;
1622         }
1623         
1624         if (zsui->info.which == ZEB_SU_STR)
1625             if (string_index)
1626                 *string_index = zsui->info.u.str;
1627         
1628         if (index_type)
1629             *index_type = zsui->info.index_type;
1630         return 0;
1631     }
1632     return -1;
1633 }
1634
1635
1636
1637 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1638                                           zebAccessObject *op,
1639                                           Odr_oid *oid)
1640 {
1641     zebAccessObject ao;
1642     
1643     for (ao = *op; ao; ao = ao->next)
1644         if (!oid_oidcmp (oid, ao->oid))
1645             break;
1646     if (!ao)
1647     {
1648         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1649         ao->handle = NULL;
1650         ao->sysno = 0;
1651         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1652         ao->next = *op;
1653         *op = ao;
1654     }
1655     return ao;
1656 }
1657
1658 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1659 {
1660     oident oe;
1661     int oid[OID_SIZE];
1662
1663     oe.proto = PROTO_Z3950;
1664     oe.oclass = CLASS_ATTSET;
1665     oe.value = (enum oid_value) set;
1666
1667     if (oid_ent_to_oid (&oe, oid))
1668     {
1669         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1670         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1671                                   accessInfo->attributeSetIds, oid);
1672     }
1673 }
1674
1675 struct zebSUInfoB *zebraExplain_add_sui_info(ZebraExplainInfo zei,
1676                                              int index_type)
1677 {
1678     struct zebSUInfoB *zsui;
1679
1680     assert (zei->curDatabaseInfo);
1681     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1682     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1683     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1684     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1685     zei->dirty = 1;
1686     zsui->info.index_type = index_type;
1687     zsui->info.doc_occurrences = 0;
1688     zsui->info.term_occurrences = 0;
1689     zsui->info.ordinal = (zei->ordinalSU)++;
1690     return zsui;
1691 }
1692
1693 int zebraExplain_add_attr_su(ZebraExplainInfo zei, int index_type,
1694                              int set, int use)
1695 {
1696     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, index_type);
1697
1698     zebraExplain_addAttributeSet (zei, set);
1699     zsui->info.which = ZEB_SU_SET_USE;
1700     zsui->info.u.su.set = set;
1701     zsui->info.u.su.use = use;
1702     return zsui->info.ordinal;
1703 }
1704
1705 int zebraExplain_add_attr_str(ZebraExplainInfo zei, int index_type,
1706                               const char *index_name)
1707 {
1708     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, index_type);
1709
1710     zsui->info.which = ZEB_SU_STR;
1711     zsui->info.u.str = nmem_strdup(zei->nmem, index_name);
1712     return zsui->info.ordinal;
1713 }
1714
1715 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1716 {
1717     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1718     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1719                               accessInfo->schemas, oid);
1720 }
1721
1722 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1723 {
1724     assert (zei->curDatabaseInfo);
1725
1726     if (adjust_num)
1727     {
1728         zei->curDatabaseInfo->recordBytes += adjust_num;
1729         zei->curDatabaseInfo->dirty = 1;
1730     }
1731 }
1732
1733 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1734 {
1735     assert (zei->curDatabaseInfo);
1736
1737     if (adjust_num)
1738     {
1739         zei->curDatabaseInfo->recordCount += adjust_num;
1740         zei->curDatabaseInfo->dirty = 1;
1741     }
1742 }
1743
1744 zint zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1745 {
1746     if (adjust_num)
1747     {
1748         zei->dirty = 1;
1749     }
1750     return zei->runNumber += adjust_num;
1751 }
1752
1753 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1754 {
1755     RecordAttr *recordAttr;
1756
1757     if (rec->info[recInfo_attr])
1758         return (RecordAttr *) rec->info[recInfo_attr];
1759     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1760     rec->info[recInfo_attr] = (char *) recordAttr;
1761     rec->size[recInfo_attr] = sizeof(*recordAttr);
1762     
1763     recordAttr->recordSize = 0;
1764     recordAttr->recordOffset = 0;
1765     recordAttr->runNumber = zei->runNumber;
1766     recordAttr->staticrank = 0;
1767     return recordAttr;
1768 }
1769
1770 static void att_loadset(void *p, const char *n, const char *name)
1771 {
1772     data1_handle dh = (data1_handle) p;
1773     if (!data1_get_attset (dh, name))
1774         yaz_log(YLOG_WARN, "Directive attset failed for %s", name);
1775 }
1776
1777 int zebraExplain_get_database_ord(ZebraExplainInfo zei)
1778 {
1779     if (!zei->curDatabaseInfo)
1780         return -1;
1781     return zei->curDatabaseInfo->ordinalDatabase;
1782 }
1783
1784 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1785 {
1786     res_trav(res, "attset", dh, att_loadset);
1787 }
1788
1789 /*
1790      zebraExplain_addSU adds to AttributeDetails for a database and
1791      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1792      exist for the database.
1793
1794      If the database doesn't exist globally (in TargetInfo) an 
1795      AttributeSetInfo must be added (globally).
1796  */
1797 /*
1798  * Local variables:
1799  * c-basic-offset: 4
1800  * indent-tabs-mode: nil
1801  * End:
1802  * vim: shiftwidth=4 tabstop=8 expandtab
1803  */
1804