Prepare for extra root tag node for better XML support
[idzebra-moved-to-github.git] / index / zinfo.c
1 /*
2  * Copyright (C) 1994-2002, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Id: zinfo.c,v 1.31 2002-07-03 10:05:19 adam Exp $
7  */
8
9 #include <stdlib.h>
10 #include <assert.h>
11 #include <string.h>
12 #include <time.h>
13
14 #include <zebraver.h>
15 #include "zinfo.h"
16
17 #define ZINFO_DEBUG 0
18
19 struct zebSUInfo {
20     int set;
21     int use;
22     int ordinal;
23 };
24
25 struct zebSUInfoB {
26     struct zebSUInfo info;
27     struct zebSUInfoB *next;
28 };
29
30 typedef struct zebAccessObjectB *zebAccessObject;
31 struct zebAccessObjectB {
32     void *handle;
33     int sysno;
34     Odr_oid *oid;
35     zebAccessObject next;
36 };
37
38 typedef struct zebAccessInfoB *zebAccessInfo;
39 struct zebAccessInfoB {
40     zebAccessObject attributeSetIds;
41     zebAccessObject schemas;
42 };
43
44 typedef struct {
45     struct zebSUInfoB *SUInfo;
46     int sysno;
47     int dirty;
48     int readFlag;
49     data1_node *data1_tree;
50 } *zebAttributeDetails;
51
52 struct zebDatabaseInfoB {
53     zebAttributeDetails attributeDetails;
54     char *databaseName;
55     data1_node *data1_database;
56     int recordCount;     /* records in db */
57     int recordBytes;     /* size of records */
58     int sysno;           /* sysno of database info */
59     int readFlag;        /* 1: read is needed when referenced; 0 if not */
60     int dirty;           /* 1: database is dirty: write is needed */
61     struct zebDatabaseInfoB *next;
62     zebAccessInfo accessInfo;
63 };
64
65 struct zebraExplainAttset {
66     char *name;
67     int ordinal;
68     struct zebraExplainAttset *next;
69 };
70
71 struct zebraCategoryListInfo {
72     int dirty;
73     int sysno;
74     data1_node *data1_categoryList;
75 };
76
77 struct zebraExplainInfo {
78     int  ordinalSU;
79     int  runNumber;
80     int  dirty;
81     int write_flag;
82     Records records;
83     data1_handle dh;
84     Res res;
85     struct zebraExplainAttset *attsets;
86     NMEM nmem;
87     data1_node *data1_target;
88     struct zebraCategoryListInfo *categoryList;
89     struct zebDatabaseInfoB *databaseInfo;
90     struct zebDatabaseInfoB *curDatabaseInfo;
91     zebAccessInfo accessInfo;
92     char date[15]; /* YYYY MMDD HH MM SS */
93     int (*updateFunc)(void *handle, Record drec, data1_node *n);
94     void *updateHandle;
95 };
96
97 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n);
98 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n);
99
100 static data1_node *read_sgml_rec (data1_handle dh, NMEM nmem, Record rec)
101 {
102     return data1_read_sgml (dh, nmem, rec->info[recInfo_storeData]);
103 }
104
105 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
106                                         struct zebDatabaseInfoB *zdi,
107                                         int key_flush);
108 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
109                                                 zebAttributeDetails zad,
110                                                 const char *databaseName,
111                                                 int key_flush);
112 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush);
113 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
114                                             zebAccessObject o,
115                                             int key_flush);
116 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
117                                             struct zebraCategoryListInfo *zcl,
118                                             int key_flush);
119
120
121 static Record createRecord (Records records, int *sysno)
122 {
123     Record rec;
124     if (*sysno)
125     {
126         rec = rec_get (records, *sysno);
127         xfree (rec->info[recInfo_storeData]);
128     }
129     else
130     {
131         rec = rec_new (records);
132         *sysno = rec->sysno;
133         
134         rec->info[recInfo_fileType] =
135             rec_strdup ("grs.sgml", &rec->size[recInfo_fileType]);
136         rec->info[recInfo_databaseName] =
137             rec_strdup ("IR-Explain-1",
138                         &rec->size[recInfo_databaseName]); 
139     }
140     return rec;
141 }
142
143 void zebraExplain_flush (ZebraExplainInfo zei, void *handle)
144 {
145     if (!zei)
146         return;
147     zei->updateHandle = handle;
148     if (zei->write_flag)
149     {
150         struct zebDatabaseInfoB *zdi;
151         zebAccessObject o;
152
153         /* write each database info record */
154         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
155         {
156             zebraExplain_writeDatabase (zei, zdi, 1);
157             zebraExplain_writeAttributeDetails (zei, zdi->attributeDetails,
158                                                 zdi->databaseName, 1);
159         }
160         zebraExplain_writeTarget (zei, 1);
161         zebraExplain_writeCategoryList (zei,
162                                         zei->categoryList,
163                                         1);
164         assert (zei->accessInfo);
165         for (o = zei->accessInfo->attributeSetIds; o; o = o->next)
166             if (!o->sysno)
167                 zebraExplain_writeAttributeSet (zei, o, 1);
168         for (o = zei->accessInfo->schemas; o; o = o->next)
169             if (!o->sysno)
170             {
171 /*              zebraExplain_writeSchema (zei, o, 1); */
172             }
173
174         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
175         {
176             zebraExplain_writeDatabase (zei, zdi, 0);
177             zebraExplain_writeAttributeDetails (zei, zdi->attributeDetails,
178                                                 zdi->databaseName, 0);
179         }
180         zebraExplain_writeTarget (zei, 0);
181     }
182 }
183
184 void zebraExplain_close (ZebraExplainInfo zei)
185 {
186 #if ZINFO_DEBUG
187     yaz_log (LOG_LOG, "zebraExplain_close");
188 #endif
189     if (!zei)
190         return;
191     zebraExplain_flush (zei, zei->updateHandle);
192     nmem_destroy (zei->nmem);
193 }
194
195 void zebraExplain_mergeOids (ZebraExplainInfo zei, data1_node *n,
196                              zebAccessObject *op)
197 {
198     data1_node *np;
199
200     for (np = n->child; np; np = np->next)
201     {
202         char str[64];
203         int len;
204         Odr_oid *oid;
205         zebAccessObject ao;
206
207         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "oid"))
208             continue;
209         len = np->child->u.data.len;
210         if (len > 63)
211             len = 63;
212         memcpy (str, np->child->u.data.data, len);
213         str[len] = '\0';
214         
215         oid = odr_getoidbystr_nmem (zei->nmem, str);
216
217         for (ao = *op; ao; ao = ao->next)
218             if (!oid_oidcmp (oid, ao->oid))
219             {
220                 ao->sysno = 1;
221                 break;
222             }
223         if (!ao)
224         {
225             ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
226             ao->handle = NULL;
227             ao->sysno = 1;
228             ao->oid = oid;
229             ao->next = *op;
230             *op = ao;
231         }
232     }
233 }
234
235 void zebraExplain_mergeAccessInfo (ZebraExplainInfo zei, data1_node *n,
236                                    zebAccessInfo *accessInfo)
237 {
238     data1_node *np;
239     
240     if (!n)
241     {
242         *accessInfo = (zebAccessInfo)
243             nmem_malloc (zei->nmem, sizeof(**accessInfo));
244         (*accessInfo)->attributeSetIds = NULL;
245         (*accessInfo)->schemas = NULL;
246     }
247     else
248     {
249         if (!(n = data1_search_tag (zei->dh, n->child, "accessInfo")))
250             return;
251         if ((np = data1_search_tag (zei->dh, n->child, "attributeSetIds")))
252             zebraExplain_mergeOids (zei, np,
253                                     &(*accessInfo)->attributeSetIds);
254         if ((np = data1_search_tag (zei->dh, n->child, "schemas")))
255             zebraExplain_mergeOids (zei, np,
256                                     &(*accessInfo)->schemas);
257     }
258 }
259
260 ZebraExplainInfo zebraExplain_open (
261     Records records, data1_handle dh,
262     Res res,
263     int writeFlag,
264     void *updateHandle,
265     int (*updateFunc)(void *handle, Record drec, data1_node *n))
266 {
267     Record trec;
268     ZebraExplainInfo zei;
269     struct zebDatabaseInfoB **zdip;
270     time_t our_time;
271     struct tm *tm;
272     NMEM nmem = nmem_create ();
273
274 #if ZINFO_DEBUG
275     logf (LOG_LOG, "zebraExplain_open wr=%d", writeFlag);
276 #endif
277     zei = (ZebraExplainInfo) nmem_malloc (nmem, sizeof(*zei));
278     zei->write_flag = writeFlag;
279     zei->updateHandle = updateHandle;
280     zei->updateFunc = updateFunc;
281     zei->dirty = 0;
282     zei->curDatabaseInfo = NULL;
283     zei->records = records;
284     zei->nmem = nmem;
285     zei->dh = dh;
286     zei->attsets = NULL;
287     zei->res = res;
288     zei->categoryList = (struct zebraCategoryListInfo *)
289         nmem_malloc (zei->nmem, sizeof(*zei->categoryList));
290     zei->categoryList->sysno = 0;
291     zei->categoryList->dirty = 0;
292     zei->categoryList->data1_categoryList = NULL;
293
294     time (&our_time);
295     tm = localtime (&our_time);
296     sprintf (zei->date, "%04d%02d%02d%02d%02d%02d",
297              tm->tm_year+1900, tm->tm_mon+1,  tm->tm_mday,
298              tm->tm_hour, tm->tm_min, tm->tm_sec);
299
300     zdip = &zei->databaseInfo;
301     trec = rec_get (records, 1);      /* get "root" record */
302
303     zei->ordinalSU = 1;
304     zei->runNumber = 0;
305
306     zebraExplain_mergeAccessInfo (zei, 0, &zei->accessInfo);
307     if (trec)    /* targetInfo already exists ... */
308     {
309         data1_node *node_tgtinfo, *node_zebra, *node_list, *np;
310
311         zei->data1_target = read_sgml_rec (zei->dh, zei->nmem, trec);
312 #if 0
313         if (!zei->data1_target || !zei->data1_target->u.root.absyn)
314 #else
315         if (!zei->data1_target)
316 #endif
317         {
318             logf (LOG_FATAL, "Explain schema missing. Check profilePath");
319             nmem_destroy (zei->nmem);
320             return 0;
321         }
322 #if ZINFO_DEBUG
323         data1_pr_tree (zei->dh, zei->data1_target, stderr);
324 #endif
325         node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
326                                          "/targetInfo");
327         zebraExplain_mergeAccessInfo (zei, node_tgtinfo,
328                                       &zei->accessInfo);
329
330         node_zebra = data1_search_tag (zei->dh, node_tgtinfo->child,
331                                        "zebraInfo");
332         np = 0;
333         if (node_zebra)
334         {
335             node_list = data1_search_tag (zei->dh, node_zebra->child,
336                                           "databaseList");
337             if (node_list)
338                 np = node_list->child;
339         }
340         for (; np; np = np->next)
341         {
342             data1_node *node_name = NULL;
343             data1_node *node_id = NULL;
344             data1_node *node_aid = NULL;
345             data1_node *np2;
346             if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "database"))
347                 continue;
348             for (np2 = np->child; np2; np2 = np2->next)
349             {
350                 if (np2->which != DATA1N_tag)
351                     continue;
352                 if (!strcmp (np2->u.tag.tag, "name"))
353                     node_name = np2->child;
354                 else if (!strcmp (np2->u.tag.tag, "id"))
355                     node_id = np2->child;
356                 else if (!strcmp (np2->u.tag.tag, "attributeDetailsId"))
357                     node_aid = np2->child;
358             }
359             assert (node_id && node_name && node_aid);
360             
361             *zdip = (struct zebDatabaseInfoB *) 
362                 nmem_malloc (zei->nmem, sizeof(**zdip));
363             (*zdip)->readFlag = 1;
364             (*zdip)->dirty = 0;
365             (*zdip)->data1_database = NULL;
366             (*zdip)->recordCount = 0;
367             (*zdip)->recordBytes = 0;
368             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
369
370             (*zdip)->databaseName = (char *)
371                 nmem_malloc (zei->nmem, 1+node_name->u.data.len);
372             memcpy ((*zdip)->databaseName, node_name->u.data.data,
373                     node_name->u.data.len);
374             (*zdip)->databaseName[node_name->u.data.len] = '\0';
375             (*zdip)->sysno = atoi_n (node_id->u.data.data,
376                                      node_id->u.data.len);
377             (*zdip)->attributeDetails = (zebAttributeDetails)
378                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
379             (*zdip)->attributeDetails->sysno = atoi_n (node_aid->u.data.data,
380                                                        node_aid->u.data.len);
381             (*zdip)->attributeDetails->readFlag = 1;
382             (*zdip)->attributeDetails->dirty = 0;
383             (*zdip)->attributeDetails->SUInfo = NULL;
384
385             zdip = &(*zdip)->next;
386         }
387         if (node_zebra)
388         {
389             np = data1_search_tag (zei->dh, node_zebra->child,
390                                    "ordinalSU");
391             np = np->child;
392             assert (np && np->which == DATA1N_data);
393             zei->ordinalSU = atoi_n (np->u.data.data, np->u.data.len);
394             
395             np = data1_search_tag (zei->dh, node_zebra->child,
396                                    "runNumber");
397             np = np->child;
398             assert (np && np->which == DATA1N_data);
399             zei->runNumber = atoi_n (np->u.data.data, np->u.data.len);
400             yaz_log (LOG_LOG, "READ runnumber = %d", zei->runNumber);
401             *zdip = NULL;
402         }
403         rec_rm (&trec);
404     }
405     else  /* create initial targetInfo */
406     {
407         data1_node *node_tgtinfo;
408
409         *zdip = NULL;
410         if (writeFlag)
411         {
412             char *sgml_buf;
413             int sgml_len;
414
415             zei->data1_target =
416                 data1_read_sgml (zei->dh, zei->nmem,
417                                  "<explain><targetInfo>TargetInfo\n"
418                                  "<name>Zebra</>\n"
419                                  "<namedResultSets>1</>\n"
420                                  "<multipleDBSearch>1</>\n"
421                                  "<nicknames><name>Zebra</></>\n"
422                                  "</></>\n" );
423             if (!zei->data1_target)
424             {
425                 logf (LOG_FATAL, "Explain schema missing. Check profilePath");
426                 nmem_destroy (zei->nmem);
427                 return 0;
428             }
429             node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
430                                              "/targetInfo");
431             assert (node_tgtinfo);
432
433             zebraExplain_initCommonInfo (zei, node_tgtinfo);
434             zebraExplain_initAccessInfo (zei, node_tgtinfo);
435
436             /* write now because we want to be sure about the sysno */
437             trec = rec_new (records);
438             trec->info[recInfo_fileType] =
439                 rec_strdup ("grs.sgml", &trec->size[recInfo_fileType]);
440             trec->info[recInfo_databaseName] =
441                 rec_strdup ("IR-Explain-1", &trec->size[recInfo_databaseName]);
442             
443             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
444             trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
445             memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
446             trec->size[recInfo_storeData] = sgml_len;
447             
448             rec_put (records, &trec);
449             rec_rm (&trec);
450
451         }
452         zebraExplain_newDatabase (zei, "IR-Explain-1", 0);
453             
454         if (!zei->categoryList->dirty)
455         {
456             struct zebraCategoryListInfo *zcl = zei->categoryList;
457             data1_node *node_cl;
458             
459             zcl->dirty = 1;
460             zcl->data1_categoryList =
461                 data1_read_sgml (zei->dh, zei->nmem,
462                                  "<explain><categoryList>CategoryList\n"
463                                  "</></>\n");
464         
465             if (zcl->data1_categoryList)
466             {
467                 node_cl = data1_search_tag (zei->dh, zcl->data1_categoryList,
468                                             "/categoryList");
469                 assert (node_cl);
470                 zebraExplain_initCommonInfo (zei, node_cl);
471             }
472         }
473     }
474     return zei;
475 }
476
477 static void zebraExplain_readAttributeDetails (ZebraExplainInfo zei,
478                                                zebAttributeDetails zad)
479 {
480     Record rec;
481     struct zebSUInfoB **zsuip = &zad->SUInfo;
482     data1_node *node_adinfo, *node_zebra, *node_list, *np;
483
484     assert (zad->sysno);
485     rec = rec_get (zei->records, zad->sysno);
486
487     zad->data1_tree = read_sgml_rec (zei->dh, zei->nmem, rec);
488
489     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
490                                     "/attributeDetails");
491     node_zebra = data1_search_tag (zei->dh, node_adinfo->child,
492                                  "zebraInfo");
493     node_list = data1_search_tag (zei->dh, node_zebra->child,
494                                   "attrlist");
495     for (np = node_list->child; np; np = np->next)
496     {
497         data1_node *node_set = NULL;
498         data1_node *node_use = NULL;
499         data1_node *node_ordinal = NULL;
500         data1_node *np2;
501         char oid_str[128];
502         int oid_str_len;
503
504         if (np->which != DATA1N_tag || strcmp (np->u.tag.tag, "attr"))
505             continue;
506         for (np2 = np->child; np2; np2 = np2->next)
507         {
508             if (np2->which != DATA1N_tag || !np2->child ||
509                 np2->child->which != DATA1N_data)
510                 continue;
511             if (!strcmp (np2->u.tag.tag, "set"))
512                 node_set = np2->child;
513             else if (!strcmp (np2->u.tag.tag, "use"))
514                 node_use = np2->child;
515             else if (!strcmp (np2->u.tag.tag, "ordinal"))
516                 node_ordinal = np2->child;
517         }
518         assert (node_set && node_use && node_ordinal);
519
520         oid_str_len = node_set->u.data.len;
521         if (oid_str_len >= (int) sizeof(oid_str))
522             oid_str_len = sizeof(oid_str)-1;
523         memcpy (oid_str, node_set->u.data.data, oid_str_len);
524         oid_str[oid_str_len] = '\0';
525
526         *zsuip = (struct zebSUInfoB *)
527             nmem_malloc (zei->nmem, sizeof(**zsuip));
528         (*zsuip)->info.set = oid_getvalbyname (oid_str);
529
530         (*zsuip)->info.use = atoi_n (node_use->u.data.data,
531                                      node_use->u.data.len);
532         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
533                                          node_ordinal->u.data.len);
534         logf (LOG_DEBUG, "set=%d use=%d ordinal=%d",
535               (*zsuip)->info.set, (*zsuip)->info.use, (*zsuip)->info.ordinal);
536         zsuip = &(*zsuip)->next;
537     }
538     *zsuip = NULL;
539     zad->readFlag = 0;
540     rec_rm (&rec);
541 }
542
543 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
544                                        struct zebDatabaseInfoB *zdi)
545 {
546     Record rec;
547     data1_node *node_dbinfo, *node_zebra, *np;
548
549     assert (zdi->sysno);
550     rec = rec_get (zei->records, zdi->sysno);
551
552     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
553     
554     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
555                                     "/databaseInfo");
556     assert (node_dbinfo);
557     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
558
559     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
560                                  "zebraInfo");
561     if (node_zebra
562         && (np = data1_search_tag (zei->dh, node_zebra->child,
563                                    "recordBytes")) 
564         && np->child && np->child->which == DATA1N_data)
565         zdi->recordBytes = atoi_n (np->child->u.data.data,
566                                    np->child->u.data.len);
567     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
568                                 "recordCount")) &&
569         (np = data1_search_tag (zei->dh, np->child,
570                                 "recordCountActual")) &&
571         np->child->which == DATA1N_data)
572     {
573         zdi->recordCount = atoi_n (np->child->u.data.data,
574                                    np->child->u.data.len);
575     }
576     zdi->readFlag = 0;
577     rec_rm (&rec);
578 }
579
580 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
581 {
582     struct zebDatabaseInfoB *zdi;
583     const char *database_n = strrchr (database, '/');
584
585     if (database_n)
586         database_n++;
587     else
588         database_n = database;
589     
590     assert (zei);
591     if (zei->curDatabaseInfo &&
592         !strcmp (zei->curDatabaseInfo->databaseName, database))
593         return 0;
594     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
595     {
596         if (!strcmp (zdi->databaseName, database_n))
597             break;
598     }
599     if (!zdi)
600         return -1;
601 #if ZINFO_DEBUG
602     logf (LOG_LOG, "zebraExplain_curDatabase: %s", database);
603 #endif
604     if (zdi->readFlag)
605     {
606 #if ZINFO_DEBUG
607         logf (LOG_LOG, "zebraExplain_readDatabase: %s", database);
608 #endif
609         zebraExplain_readDatabase (zei, zdi);
610     }
611     if (zdi->attributeDetails->readFlag)
612     {
613 #if ZINFO_DEBUG
614         logf (LOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
615 #endif
616         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
617     }
618     zei->curDatabaseInfo = zdi;
619     return 0;
620 }
621
622 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
623 {
624     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "commonInfo", 0, n);
625     data1_mk_tag_data_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
626     data1_mk_tag_data_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
627     data1_mk_tag_data_text (zei->dh, c, "languageCode", "EN", zei->nmem);
628 }
629
630 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
631 {
632     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
633     assert (c);
634     data1_mk_tag_data_text_uni (zei->dh, c, "dateChanged", zei->date,
635                                 zei->nmem);
636 }
637
638 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
639 {
640     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "accessInfo", 0, n);
641     data1_node *d = data1_mk_tag (zei->dh, zei->nmem, "unitSystems", 0, c);
642     data1_mk_tag_data_text (zei->dh, d, "string", "ISO", zei->nmem);
643 }
644
645 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
646                                            zebAccessInfo accessInfo)
647 {
648     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
649     data1_node *d;
650     zebAccessObject p;
651     
652     if (!c)
653     {
654         data1_pr_tree (zei->dh, n, stdout);
655         exit (0);
656         assert (c);
657     }
658
659     if ((p = accessInfo->attributeSetIds))
660     {
661         d = data1_mk_tag_uni (zei->dh, zei->nmem, "attributeSetIds", c);
662         for (; p; p = p->next)
663             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
664     }
665     if ((p = accessInfo->schemas))
666     {
667         d = data1_mk_tag_uni (zei->dh, zei->nmem, "schemas", c);
668         for (; p; p = p->next)
669             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
670     }
671 }
672
673 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
674                               int explain_database)
675 {
676     struct zebDatabaseInfoB *zdi;
677     data1_node *node_dbinfo, *node_adinfo;
678     const char *database_n = strrchr (database, '/');
679
680     if (database_n)
681         database_n++;
682     else
683         database_n = database;
684
685 #if ZINFO_DEBUG
686     logf (LOG_LOG, "zebraExplain_newDatabase: %s", database);
687 #endif
688     assert (zei);
689     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
690     {
691         if (!strcmp (zdi->databaseName, database_n))
692             break;
693     }
694     if (zdi)
695         return -1;
696     /* it's new really. make it */
697     zdi = (struct zebDatabaseInfoB *) nmem_malloc (zei->nmem, sizeof(*zdi));
698     zdi->next = zei->databaseInfo;
699     zei->databaseInfo = zdi;
700     zdi->sysno = 0;
701     zdi->recordCount = 0;
702     zdi->recordBytes = 0;
703     zdi->readFlag = 0;
704     zdi->databaseName = nmem_strdup (zei->nmem, database_n);
705
706     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
707     
708     assert (zei->dh);
709     assert (zei->nmem);
710
711     zdi->data1_database =
712         data1_read_sgml (zei->dh, zei->nmem, 
713                          "<explain><databaseInfo>DatabaseInfo\n"
714                          "</></>\n");
715     if (!zdi->data1_database)
716         return -2;
717
718     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
719                                     "/databaseInfo");
720     assert (node_dbinfo);
721
722     zebraExplain_initCommonInfo (zei, node_dbinfo);
723     zebraExplain_initAccessInfo (zei, node_dbinfo);
724
725     data1_mk_tag_data_text (zei->dh, node_dbinfo, "name",
726                                database, zei->nmem);
727     
728     if (explain_database)
729         data1_mk_tag_data_text (zei->dh, node_dbinfo, "explainDatabase",
730                                 "", zei->nmem);
731     
732     data1_mk_tag_data_text (zei->dh, node_dbinfo, "userFee",
733                             "0", zei->nmem);
734     
735     data1_mk_tag_data_text (zei->dh, node_dbinfo, "available",
736                             "1", zei->nmem);
737     
738 #if ZINFO_DEBUG
739     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
740 #endif
741     zdi->dirty = 1;
742     zei->dirty = 1;
743     zei->curDatabaseInfo = zdi;
744
745     zdi->attributeDetails = (zebAttributeDetails)
746         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
747     zdi->attributeDetails->readFlag = 0;
748     zdi->attributeDetails->sysno = 0;
749     zdi->attributeDetails->dirty = 1;
750     zdi->attributeDetails->SUInfo = NULL;
751     zdi->attributeDetails->data1_tree =
752         data1_read_sgml (zei->dh, zei->nmem,
753                          "<explain><attributeDetails>AttributeDetails\n"
754                          "</></>\n");
755
756     node_adinfo = data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree,
757                                     "/attributeDetails");
758     assert (node_adinfo);
759
760     zebraExplain_initCommonInfo (zei, node_adinfo);
761
762     return 0;
763 }
764
765 static void writeAttributeValueDetails (ZebraExplainInfo zei,
766                                   zebAttributeDetails zad,
767                                   data1_node *node_atvs, data1_attset *attset)
768
769 {
770     struct zebSUInfoB *zsui;
771     int set_ordinal = attset->reference;
772     data1_attset_child *c;
773
774     for (c = attset->children; c; c = c->next)
775         writeAttributeValueDetails (zei, zad, node_atvs, c->child);
776     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
777     {
778         data1_node *node_attvalue, *node_value;
779         if (set_ordinal != zsui->info.set)
780             continue;
781         node_attvalue = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
782                                       0 /* attr */, node_atvs);
783         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
784                                    0 /* attr */, node_attvalue);
785         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
786                                zsui->info.use, zei->nmem);
787     }
788 }
789
790 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
791                                             struct zebraCategoryListInfo *zcl,
792                                             int key_flush)
793 {
794     char *sgml_buf;
795     int sgml_len;
796     int i;
797     Record drec;
798     data1_node *node_ci, *node_categoryList;
799     int sysno = 0;
800     static char *category[] = {
801         "CategoryList",
802         "TargetInfo",
803         "DatabaseInfo",
804         "AttributeDetails",
805         NULL
806     };
807
808     assert (zcl);
809     if (!zcl->dirty)
810         return ;
811     zcl->dirty = 1;
812     node_categoryList = zcl->data1_categoryList;
813
814 #if ZINFO_DEBUG
815     logf (LOG_LOG, "zebraExplain_writeCategoryList");
816 #endif
817
818     drec = createRecord (zei->records, &sysno);
819     
820     node_ci = data1_search_tag (zei->dh, node_categoryList,
821                                 "/categoryList");
822     assert (node_ci);
823     node_ci = data1_mk_tag (zei->dh, zei->nmem, "categories", 0 /* attr */,
824                             node_ci);
825     assert (node_ci);
826     
827     for (i = 0; category[i]; i++)
828     {
829         data1_node *node_cat = data1_mk_tag (zei->dh, zei->nmem,  "category",
830                                              0 /* attr */, node_ci);
831
832         data1_mk_tag_data_text (zei->dh, node_cat, "name",
833                                 category[i], zei->nmem);
834     }
835     /* extract *searchable* keys from it. We do this here, because
836        record count, etc. is affected */
837     if (key_flush)
838         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
839
840     /* convert to "SGML" and write it */
841 #if ZINFO_DEBUG
842     data1_pr_tree (zei->dh, node_categoryList, stderr);
843 #endif
844     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList, 0, &sgml_len);
845     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
846     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
847     drec->size[recInfo_storeData] = sgml_len;
848     
849     rec_put (zei->records, &drec);
850 }
851
852 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
853                                                 zebAttributeDetails zad,
854                                                 const char *databaseName,
855                                                 int key_flush)
856 {
857     char *sgml_buf;
858     int sgml_len;
859     Record drec;
860     data1_node *node_adinfo, *node_list, *node_zebra, *node_attributesBySet;
861     struct zebSUInfoB *zsui;
862     int set_min;
863     
864     if (!zad->dirty)
865         return;
866     
867     zad->dirty = 0;
868 #if ZINFO_DEBUG
869     logf (LOG_LOG, "zebraExplain_writeAttributeDetails");    
870 #endif
871
872     drec = createRecord (zei->records, &zad->sysno);
873     assert (zad->data1_tree);
874
875     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
876                                    "/attributeDetails");
877     zebraExplain_updateCommonInfo (zei, node_adinfo);
878
879     data1_mk_tag_data_text (zei->dh, node_adinfo, "name",
880                             databaseName, zei->nmem);
881
882     /* extract *searchable* keys from it. We do this here, because
883        record count, etc. is affected */
884     if (key_flush)
885         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
886
887     node_attributesBySet = data1_mk_tag_uni (zei->dh, zei->nmem,
888                                            "attributesBySet", node_adinfo);
889     set_min = -1;
890     while (1)
891     {
892         data1_node *node_asd;
893         data1_attset *attset;
894         int set_ordinal = -1;
895         for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
896         {
897             if ((set_ordinal < 0 || set_ordinal > zsui->info.set)
898                 && zsui->info.set > set_min)
899                 set_ordinal = zsui->info.set;
900         }
901         if (set_ordinal < 0)
902             break;
903         set_min = set_ordinal;
904         node_asd = data1_mk_tag (zei->dh, zei->nmem,
905                                  "attributeSetDetails",
906                                  0 /* attr */, node_attributesBySet);
907
908         attset = data1_attset_search_id (zei->dh, set_ordinal);
909         if (!attset)
910         {
911             zebraExplain_loadAttsets (zei->dh, zei->res);
912             attset = data1_attset_search_id (zei->dh, set_ordinal);
913         }
914         if (attset)
915         {
916             int oid[OID_SIZE];
917             oident oe;
918             
919             oe.proto = PROTO_Z3950;
920             oe.oclass = CLASS_ATTSET;
921             oe.value = (enum oid_value) set_ordinal;
922             
923             if (oid_ent_to_oid (&oe, oid))
924             {
925                 data1_node *node_abt, *node_atd, *node_atvs;
926                 data1_mk_tag_data_oid (zei->dh, node_asd, "oid",
927                                        oid, zei->nmem);
928                 
929                 node_abt = data1_mk_tag (zei->dh, zei->nmem,
930                                          "attributesByType",
931                                          0 /*attr */, node_asd);
932                 node_atd = data1_mk_tag (zei->dh, zei->nmem,
933                                          "attributeTypeDetails", 
934                                          0 /* attr */, node_abt);
935                 data1_mk_tag_data_int (zei->dh, node_atd,
936                                        "type", 1, zei->nmem);
937                 node_atvs = data1_mk_tag (zei->dh, zei->nmem, 
938                                           "attributeValues",
939                                           0 /* attr */, node_atd);
940                 writeAttributeValueDetails (zei, zad, node_atvs, attset);
941             }
942         }
943     }
944     /* zebra info (private) */
945     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
946                                  "zebraInfo", node_adinfo);
947     node_list = data1_mk_tag_uni (zei->dh, zei->nmem,
948                                  "attrlist", node_zebra);
949     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
950     {
951         struct oident oident;
952         int oid[OID_SIZE];
953         data1_node *node_attr;
954         
955         node_attr = data1_mk_tag (zei->dh, zei->nmem, "attr", 0 /* attr */,
956                                   node_list);
957         
958         oident.proto = PROTO_Z3950;
959         oident.oclass = CLASS_ATTSET;
960         oident.value = (enum oid_value) zsui->info.set;
961         oid_ent_to_oid (&oident, oid);
962         
963         data1_mk_tag_data_text (zei->dh, node_attr, "set",
964                                 oident.desc, zei->nmem);
965         data1_mk_tag_data_int (zei->dh, node_attr, "use",
966                                zsui->info.use, zei->nmem);
967         data1_mk_tag_data_int (zei->dh, node_attr, "ordinal",
968                                zsui->info.ordinal, zei->nmem);
969     }
970     /* convert to "SGML" and write it */
971 #if ZINFO_DEBUG
972     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
973 #endif
974     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
975                                   0, &sgml_len);
976     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
977     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
978     drec->size[recInfo_storeData] = sgml_len;
979     
980     rec_put (zei->records, &drec);
981 }
982
983 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
984                                         struct zebDatabaseInfoB *zdi,
985                                         int key_flush)
986 {
987     char *sgml_buf;
988     int sgml_len;
989     Record drec;
990     data1_node *node_dbinfo, *node_count, *node_zebra;
991     
992     if (!zdi->dirty)
993         return;
994
995     zdi->dirty = 0;
996 #if ZINFO_DEBUG
997     logf (LOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
998 #endif
999     drec = createRecord (zei->records, &zdi->sysno);
1000     assert (zdi->data1_database);
1001
1002     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
1003                                     "/databaseInfo");
1004
1005     assert (node_dbinfo);
1006     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1007     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1008
1009     /* extract *searchable* keys from it. We do this here, because
1010        record count, etc. is affected */
1011     if (key_flush)
1012         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1013     /* record count */
1014     node_count = data1_mk_tag_uni (zei->dh, zei->nmem,
1015                                  "recordCount", node_dbinfo);
1016     data1_mk_tag_data_int (zei->dh, node_count, "recordCountActual",
1017                               zdi->recordCount, zei->nmem);
1018
1019     /* zebra info (private) */
1020     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1021                                  "zebraInfo", node_dbinfo);
1022     data1_mk_tag_data_int (zei->dh, node_zebra,
1023                            "recordBytes", zdi->recordBytes, zei->nmem);
1024     /* convert to "SGML" and write it */
1025 #if ZINFO_DEBUG
1026     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1027 #endif
1028     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1029                                   0, &sgml_len);
1030     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1031     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1032     drec->size[recInfo_storeData] = sgml_len;
1033     
1034     rec_put (zei->records, &drec);
1035 }
1036
1037 static void writeAttributeValues (ZebraExplainInfo zei,
1038                                   data1_node *node_values,
1039                                   data1_attset *attset)
1040 {
1041     data1_att *atts;
1042     data1_attset_child *c;
1043
1044     if (!attset)
1045         return;
1046
1047     for (c = attset->children; c; c = c->next)
1048         writeAttributeValues (zei, node_values, c->child);
1049     for (atts = attset->atts; atts; atts = atts->next)
1050     {
1051         data1_node *node_value;
1052         
1053         node_value = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
1054                                    0 /* attr */, node_values);
1055         data1_mk_tag_data_text (zei->dh, node_value, "name",
1056                                 atts->name, zei->nmem);
1057         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
1058                                    0 /* attr */, node_value);
1059         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
1060                                atts->value, zei->nmem);
1061     }
1062 }
1063
1064
1065 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1066                                             zebAccessObject o,
1067                                             int key_flush)
1068 {
1069     char *sgml_buf;
1070     int sgml_len;
1071     Record drec;
1072     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1073     data1_node *node_values;
1074     struct oident *entp;
1075     struct data1_attset *attset = NULL;
1076     
1077     if ((entp = oid_getentbyoid (o->oid)))
1078         attset = data1_attset_search_id (zei->dh, entp->value);
1079             
1080 #if ZINFO_DEBUG
1081     logf (LOG_LOG, "zebraExplain_writeAttributeSet %s",
1082           attset ? attset->name : "<unknown>");    
1083 #endif
1084
1085     drec = createRecord (zei->records, &o->sysno);
1086     node_root =
1087         data1_read_sgml (zei->dh, zei->nmem,
1088                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1089                          "</></>\n" );
1090
1091     node_attinfo = data1_search_tag (zei->dh, node_root,
1092                                    "/attributeSetInfo");
1093
1094     assert (node_attinfo);
1095     zebraExplain_initCommonInfo (zei, node_attinfo);
1096     zebraExplain_updateCommonInfo (zei, node_attinfo);
1097
1098     data1_mk_tag_data_oid (zei->dh, node_attinfo,
1099                             "oid", o->oid, zei->nmem);
1100     if (attset && attset->name)
1101         data1_mk_tag_data_text (zei->dh, node_attinfo,
1102                                 "name", attset->name, zei->nmem);
1103     
1104     node_attributes = data1_mk_tag_uni (zei->dh, zei->nmem,
1105                                       "attributes", node_attinfo);
1106     node_atttype = data1_mk_tag_uni (zei->dh, zei->nmem,
1107                                    "attributeType", node_attributes);
1108     data1_mk_tag_data_text (zei->dh, node_atttype,
1109                             "name", "Use", zei->nmem);
1110     data1_mk_tag_data_text (zei->dh, node_atttype,
1111                             "description", "Use Attribute", zei->nmem);
1112     data1_mk_tag_data_int (zei->dh, node_atttype,
1113                            "type", 1, zei->nmem);
1114     node_values = data1_mk_tag (zei->dh, zei->nmem,
1115                                 "attributeValues", 0 /* attr */, node_atttype);
1116     if (attset)
1117         writeAttributeValues (zei, node_values, attset);
1118
1119     /* extract *searchable* keys from it. We do this here, because
1120        record count, etc. is affected */
1121     if (key_flush)
1122         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1123     /* convert to "SGML" and write it */
1124 #if ZINFO_DEBUG
1125     data1_pr_tree (zei->dh, node_root, stderr);
1126 #endif
1127     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 0, &sgml_len);
1128     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1129     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1130     drec->size[recInfo_storeData] = sgml_len;
1131     
1132     rec_put (zei->records, &drec);
1133 }
1134
1135 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush)
1136 {
1137     struct zebDatabaseInfoB *zdi;
1138     data1_node *node_tgtinfo, *node_list, *node_zebra;
1139     Record trec;
1140     int sgml_len;
1141     char *sgml_buf;
1142
1143     if (!zei->dirty)
1144         return;
1145     zei->dirty = 0;
1146
1147     trec = rec_get (zei->records, 1);
1148     xfree (trec->info[recInfo_storeData]);
1149
1150     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
1151                                      "/targetInfo");
1152     assert (node_tgtinfo);
1153
1154     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1155     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1156
1157     /* convert to "SGML" and write it */
1158     if (key_flush)
1159         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1160
1161     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1162                                  "zebraInfo", node_tgtinfo);
1163     data1_mk_tag_data_text (zei->dh, node_zebra, "version",
1164                                ZEBRAVER, zei->nmem);
1165     node_list = data1_mk_tag (zei->dh, zei->nmem,
1166                               "databaseList", 0 /* attr */, node_zebra);
1167     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1168     {
1169         data1_node *node_db;
1170         node_db = data1_mk_tag (zei->dh, zei->nmem,
1171                                 "database", 0 /* attr */, node_list);
1172         data1_mk_tag_data_text (zei->dh, node_db, "name",
1173                                 zdi->databaseName, zei->nmem);
1174         data1_mk_tag_data_int (zei->dh, node_db, "id",
1175                                zdi->sysno, zei->nmem);
1176         data1_mk_tag_data_int (zei->dh, node_db, "attributeDetailsId",
1177                                zdi->attributeDetails->sysno, zei->nmem);
1178     }
1179     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalSU",
1180                            zei->ordinalSU, zei->nmem);
1181
1182     data1_mk_tag_data_int (zei->dh, node_zebra, "runNumber",
1183                               zei->runNumber, zei->nmem);
1184
1185 #if ZINFO_DEBUG
1186     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1187 #endif
1188     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1189                                   0, &sgml_len);
1190     trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1191     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1192     trec->size[recInfo_storeData] = sgml_len;
1193     
1194     rec_put (zei->records, &trec);
1195 }
1196
1197 int zebraExplain_lookupSU (ZebraExplainInfo zei, int set, int use)
1198 {
1199     struct zebSUInfoB *zsui;
1200
1201     assert (zei->curDatabaseInfo);
1202     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1203          zsui; zsui=zsui->next)
1204         if (zsui->info.use == use && zsui->info.set == set)
1205             return zsui->info.ordinal;
1206     return -1;
1207 }
1208
1209 int zebraExplain_lookup_ord (ZebraExplainInfo zei, int ord,
1210                              const char **db, int *set, int *use)
1211 {
1212     struct zebDatabaseInfoB *zdb;
1213     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1214     {
1215         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1216         for ( ;zsui; zsui = zsui->next)
1217             if (zsui->info.ordinal == ord)
1218             {
1219                 *db = zdb->databaseName;
1220                 *set = zsui->info.set;
1221                 *use = zsui->info.use;
1222                 return 0;
1223             }
1224     }
1225     return -1;
1226 }
1227
1228 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1229                                           zebAccessObject *op,
1230                                           Odr_oid *oid)
1231 {
1232     zebAccessObject ao;
1233     
1234     for (ao = *op; ao; ao = ao->next)
1235         if (!oid_oidcmp (oid, ao->oid))
1236             break;
1237     if (!ao)
1238     {
1239         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1240         ao->handle = NULL;
1241         ao->sysno = 0;
1242         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1243         ao->next = *op;
1244         *op = ao;
1245     }
1246     return ao;
1247 }
1248
1249 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1250 {
1251     oident oe;
1252     int oid[OID_SIZE];
1253
1254     oe.proto = PROTO_Z3950;
1255     oe.oclass = CLASS_ATTSET;
1256     oe.value = (enum oid_value) set;
1257
1258     if (oid_ent_to_oid (&oe, oid))
1259     {
1260         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1261         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1262                                   accessInfo->attributeSetIds, oid);
1263     }
1264 }
1265
1266 int zebraExplain_addSU (ZebraExplainInfo zei, int set, int use)
1267 {
1268     struct zebSUInfoB *zsui;
1269
1270     assert (zei->curDatabaseInfo);
1271     for (zsui = zei->curDatabaseInfo->attributeDetails->SUInfo;
1272          zsui; zsui=zsui->next)
1273         if (zsui->info.use == use && zsui->info.set == set)
1274             return -1;
1275     zebraExplain_addAttributeSet (zei, set);
1276     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1277     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1278     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1279     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1280     zei->dirty = 1;
1281     zsui->info.set = set;
1282     zsui->info.use = use;
1283     zsui->info.ordinal = (zei->ordinalSU)++;
1284     return zsui->info.ordinal;
1285 }
1286
1287 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1288 {
1289     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1290     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1291                               accessInfo->schemas, oid);
1292 }
1293
1294 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1295 {
1296     assert (zei->curDatabaseInfo);
1297
1298     if (adjust_num)
1299     {
1300         zei->curDatabaseInfo->recordBytes += adjust_num;
1301         zei->curDatabaseInfo->dirty = 1;
1302     }
1303 }
1304
1305 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1306 {
1307     assert (zei->curDatabaseInfo);
1308
1309     if (adjust_num)
1310     {
1311         zei->curDatabaseInfo->recordCount += adjust_num;
1312         zei->curDatabaseInfo->dirty = 1;
1313     }
1314 }
1315
1316 int zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1317 {
1318     if (adjust_num)
1319     {
1320         zei->dirty = 1;
1321         yaz_log (LOG_LOG, "zinfo run number=%d", zei->runNumber+adjust_num);
1322     }
1323     return zei->runNumber += adjust_num;
1324 }
1325
1326 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1327 {
1328     RecordAttr *recordAttr;
1329
1330     if (rec->info[recInfo_attr])
1331         return (RecordAttr *) rec->info[recInfo_attr];
1332     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1333     rec->info[recInfo_attr] = (char *) recordAttr;
1334     rec->size[recInfo_attr] = sizeof(*recordAttr);
1335     
1336     recordAttr->recordSize = 0;
1337     recordAttr->recordOffset = 0;
1338     recordAttr->runNumber = zei->runNumber;
1339     return recordAttr;
1340 }
1341
1342 static void att_loadset(void *p, const char *n, const char *name)
1343 {
1344     data1_handle dh = (data1_handle) p;
1345     if (!data1_get_attset (dh, name))
1346         logf (LOG_WARN, "Directive attset failed for %s", name);
1347 }
1348
1349 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1350 {
1351     res_trav(res, "attset", dh, att_loadset);
1352 }
1353
1354 /*
1355      zebraExplain_addSU adds to AttributeDetails for a database and
1356      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1357      exist for the database.
1358
1359      If the database doesn't exist globally (in TargetInfo) an 
1360      AttributeSetInfo must be added (globally).
1361  */