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