f19a068823943702f4fca6245554c2f14d41037c
[idzebra-moved-to-github.git] / index / zinfo.c
1 /* $Id: zinfo.c,v 1.63 2006-05-18 12:03:05 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     int ord;
1442
1443     assert (zei->curDatabaseInfo);
1444
1445     ord = zebraExplain_lookup_attr_su(zei, 'w', set, use);
1446     if (ord != -1)
1447         return ord;
1448     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1449          zsui; zsui=zsui->next)
1450         if (zsui->info.which == ZEB_SU_SET_USE &&
1451             zsui->info.u.su.use == use && zsui->info.u.su.set == set)
1452             return zsui->info.ordinal;
1453     return -1;
1454 }
1455
1456 int zebraExplain_lookup_attr_su(ZebraExplainInfo zei, int index_type,
1457                                 int set, int use)
1458 {
1459     struct zebSUInfoB **zsui;
1460
1461 #if 0
1462     yaz_log(YLOG_LOG, "lookup_attr_su index_type=%d set=%d use=%d",
1463             index_type, set, use);
1464 #endif
1465     assert (zei->curDatabaseInfo);
1466     for (zsui = &zei->curDatabaseInfo->attributeDetails->SUInfo;
1467          *zsui; zsui = &(*zsui)->next)
1468         if ((*zsui)->info.index_type == index_type &&
1469             (*zsui)->info.which == ZEB_SU_SET_USE &&
1470             (*zsui)->info.u.su.use == use && (*zsui)->info.u.su.set == set)
1471         {
1472             struct zebSUInfoB *zsui_this = *zsui;
1473
1474             /* take it out of the list and move to front */
1475             *zsui = (*zsui)->next;
1476             zsui_this->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1477             zei->curDatabaseInfo->attributeDetails->SUInfo = zsui_this;
1478
1479             return zsui_this->info.ordinal;
1480         }
1481     return -1;
1482 }
1483
1484 int zebraExplain_lookup_attr_str(ZebraExplainInfo zei, int index_type,
1485                                  const char *str)
1486 {
1487     struct zebSUInfoB **zsui;
1488
1489     assert (zei->curDatabaseInfo);
1490     for (zsui = &zei->curDatabaseInfo->attributeDetails->SUInfo;
1491          *zsui; zsui = &(*zsui)->next)
1492         if ((*zsui)->info.index_type == index_type
1493             && (*zsui)->info.which == ZEB_SU_STR 
1494             && !strcmp((*zsui)->info.u.str, str))
1495         {
1496             struct zebSUInfoB *zsui_this = *zsui;
1497
1498             /* take it out of the list and move to front */
1499             *zsui = (*zsui)->next;
1500             zsui_this->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1501             zei->curDatabaseInfo->attributeDetails->SUInfo = zsui_this;
1502
1503             return zsui_this->info.ordinal;
1504         }
1505     return -1;
1506 }
1507
1508 int zebraExplain_trav_ord(ZebraExplainInfo zei, void *handle,
1509                           int (*f)(void *handle, int ord))
1510 {
1511     struct zebDatabaseInfoB *zdb = zei->curDatabaseInfo;
1512     if (zdb)
1513     {
1514         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1515         for ( ;zsui; zsui = zsui->next)
1516             (*f)(handle,  zsui->info.ordinal);
1517     }
1518     return 0;
1519 }
1520
1521
1522 struct zebSUInfoB *zebraExplain_get_sui_info (ZebraExplainInfo zei, int ord,
1523                                               int dirty_mark,
1524                                               const char **db)
1525 {
1526     struct zebDatabaseInfoB *zdb;
1527
1528     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1529     {
1530         struct zebSUInfoB **zsui;
1531
1532         if (zdb->attributeDetails->readFlag)
1533             zebraExplain_readAttributeDetails (zei, zdb->attributeDetails);
1534
1535         for (zsui = &zdb->attributeDetails->SUInfo; *zsui;
1536              zsui = &(*zsui)->next)
1537             if ((*zsui)->info.ordinal == ord)
1538             {
1539                 struct zebSUInfoB *zsui_this = *zsui;
1540                 
1541                 /* take it out of the list and move to front */
1542                 *zsui = (*zsui)->next;
1543                 zsui_this->next = zdb->attributeDetails->SUInfo;
1544                 zdb->attributeDetails->SUInfo = zsui_this;
1545
1546                 if (dirty_mark)
1547                     zdb->attributeDetails->dirty = 1;
1548                 if (db)
1549                     *db = zdb->databaseName;
1550                 return zsui_this;
1551             }
1552     }
1553     return 0;
1554 }
1555
1556
1557
1558 int zebraExplain_ord_adjust_occurrences(ZebraExplainInfo zei, int ord,
1559                                         int term_delta, int doc_delta)
1560 {
1561     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 1, 0);
1562     if (zsui)
1563     {
1564         zsui->info.term_occurrences += term_delta;
1565         zsui->info.doc_occurrences += doc_delta;
1566         return 0;
1567     }
1568     return -1;
1569 }
1570
1571 int zebraExplain_ord_get_occurrences(ZebraExplainInfo zei, int ord,
1572                                      zint *term_occurrences,
1573                                      zint *doc_occurrences)
1574 {
1575     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1576     if (zsui)
1577     {
1578         *term_occurrences = zsui->info.term_occurrences;
1579         *doc_occurrences = zsui->info.doc_occurrences;
1580         return 0;
1581     }
1582     return -1;
1583 }
1584
1585 zint zebraExplain_ord_get_doc_occurrences(ZebraExplainInfo zei, int ord)
1586 {
1587     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1588     if (zsui)
1589         return zsui->info.doc_occurrences;
1590     return 0;
1591 }
1592
1593 zint zebraExplain_ord_get_term_occurrences(ZebraExplainInfo zei, int ord)
1594 {
1595     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1596     if (zsui)
1597         return zsui->info.term_occurrences;
1598     return 0;
1599 }
1600
1601 int zebraExplain_lookup_ord(ZebraExplainInfo zei, int ord,
1602                             int *index_type, 
1603                             const char **db,
1604                             int *set, int *use,
1605                             const char **string_index)
1606 {
1607     struct zebSUInfoB *zsui;
1608
1609     if (set)
1610         *set = -1;
1611     if (use)
1612         *use = -1;
1613     if (index_type)
1614         *index_type = 0;
1615     if (string_index)
1616         *string_index = 0;
1617
1618     zsui = zebraExplain_get_sui_info(zei, ord, 0, db);
1619     if (zsui)
1620     {
1621         if (zsui->info.which == ZEB_SU_SET_USE)
1622         {
1623             if (set)
1624                 *set = zsui->info.u.su.set;
1625             if (use)
1626                 *use = zsui->info.u.su.use;
1627         }
1628         
1629         if (zsui->info.which == ZEB_SU_STR)
1630             if (string_index)
1631                 *string_index = zsui->info.u.str;
1632         
1633         if (index_type)
1634             *index_type = zsui->info.index_type;
1635         return 0;
1636     }
1637     return -1;
1638 }
1639
1640
1641
1642 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1643                                           zebAccessObject *op,
1644                                           Odr_oid *oid)
1645 {
1646     zebAccessObject ao;
1647     
1648     for (ao = *op; ao; ao = ao->next)
1649         if (!oid_oidcmp (oid, ao->oid))
1650             break;
1651     if (!ao)
1652     {
1653         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1654         ao->handle = NULL;
1655         ao->sysno = 0;
1656         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1657         ao->next = *op;
1658         *op = ao;
1659     }
1660     return ao;
1661 }
1662
1663 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1664 {
1665     oident oe;
1666     int oid[OID_SIZE];
1667
1668     oe.proto = PROTO_Z3950;
1669     oe.oclass = CLASS_ATTSET;
1670     oe.value = (enum oid_value) set;
1671
1672     if (oid_ent_to_oid (&oe, oid))
1673     {
1674         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1675         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1676                                   accessInfo->attributeSetIds, oid);
1677     }
1678 }
1679
1680 struct zebSUInfoB *zebraExplain_add_sui_info(ZebraExplainInfo zei,
1681                                              int index_type)
1682 {
1683     struct zebSUInfoB *zsui;
1684
1685     assert (zei->curDatabaseInfo);
1686     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1687     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1688     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1689     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1690     zei->dirty = 1;
1691     zsui->info.index_type = index_type;
1692     zsui->info.doc_occurrences = 0;
1693     zsui->info.term_occurrences = 0;
1694     zsui->info.ordinal = (zei->ordinalSU)++;
1695     return zsui;
1696 }
1697
1698 int zebraExplain_add_attr_su(ZebraExplainInfo zei, int index_type,
1699                              int set, int use)
1700 {
1701     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, index_type);
1702
1703     zebraExplain_addAttributeSet (zei, set);
1704     zsui->info.which = ZEB_SU_SET_USE;
1705     zsui->info.u.su.set = set;
1706     zsui->info.u.su.use = use;
1707     return zsui->info.ordinal;
1708 }
1709
1710 int zebraExplain_add_attr_str(ZebraExplainInfo zei, int index_type,
1711                               const char *index_name)
1712 {
1713     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, index_type);
1714
1715     zsui->info.which = ZEB_SU_STR;
1716     zsui->info.u.str = nmem_strdup(zei->nmem, index_name);
1717     return zsui->info.ordinal;
1718 }
1719
1720 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1721 {
1722     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1723     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1724                               accessInfo->schemas, oid);
1725 }
1726
1727 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1728 {
1729     assert (zei->curDatabaseInfo);
1730
1731     if (adjust_num)
1732     {
1733         zei->curDatabaseInfo->recordBytes += adjust_num;
1734         zei->curDatabaseInfo->dirty = 1;
1735     }
1736 }
1737
1738 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1739 {
1740     assert (zei->curDatabaseInfo);
1741
1742     if (adjust_num)
1743     {
1744         zei->curDatabaseInfo->recordCount += adjust_num;
1745         zei->curDatabaseInfo->dirty = 1;
1746     }
1747 }
1748
1749 zint zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1750 {
1751     if (adjust_num)
1752     {
1753         zei->dirty = 1;
1754     }
1755     return zei->runNumber += adjust_num;
1756 }
1757
1758 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1759 {
1760     RecordAttr *recordAttr;
1761
1762     if (rec->info[recInfo_attr])
1763         return (RecordAttr *) rec->info[recInfo_attr];
1764     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1765     rec->info[recInfo_attr] = (char *) recordAttr;
1766     rec->size[recInfo_attr] = sizeof(*recordAttr);
1767     
1768     recordAttr->recordSize = 0;
1769     recordAttr->recordOffset = 0;
1770     recordAttr->runNumber = zei->runNumber;
1771     recordAttr->staticrank = 0;
1772     return recordAttr;
1773 }
1774
1775 static void att_loadset(void *p, const char *n, const char *name)
1776 {
1777     data1_handle dh = (data1_handle) p;
1778     if (!data1_get_attset (dh, name))
1779         yaz_log(YLOG_WARN, "Directive attset failed for %s", name);
1780 }
1781
1782 int zebraExplain_get_database_ord(ZebraExplainInfo zei)
1783 {
1784     if (!zei->curDatabaseInfo)
1785         return -1;
1786     return zei->curDatabaseInfo->ordinalDatabase;
1787 }
1788
1789 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1790 {
1791     res_trav(res, "attset", dh, att_loadset);
1792 }
1793
1794 /*
1795      zebraExplain_addSU adds to AttributeDetails for a database and
1796      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1797      exist for the database.
1798
1799      If the database doesn't exist globally (in TargetInfo) an 
1800      AttributeSetInfo must be added (globally).
1801  */
1802 /*
1803  * Local variables:
1804  * c-basic-offset: 4
1805  * indent-tabs-mode: nil
1806  * End:
1807  * vim: shiftwidth=4 tabstop=8 expandtab
1808  */
1809