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