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