Buffers used during file match got bigger.
[idzebra-moved-to-github.git] / index / kinput.c
1 /*
2  * Copyright (C) 1994-1996, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: kinput.c,v $
7  * Revision 1.21  1996-11-08 11:10:23  adam
8  * Buffers used during file match got bigger.
9  * Compressed ISAM support everywhere.
10  * Bug fixes regarding masking characters in queries.
11  * Redesigned Regexp-2 queries.
12  *
13  * Revision 1.20  1996/11/01 08:58:41  adam
14  * Interface to isamc system now includes update and delete.
15  *
16  * Revision 1.19  1996/10/29 14:09:46  adam
17  * Use of cisam system - enabled if setting isamc is 1.
18  *
19  * Revision 1.18  1996/06/04 10:18:59  adam
20  * Minor changes - removed include of ctype.h.
21  *
22  * Revision 1.17  1996/05/14  15:47:07  adam
23  * Cleanup of various buffer size entities.
24  *
25  * Revision 1.16  1996/04/09  10:05:20  adam
26  * Bug fix: prev_name buffer possibly too small; allocated in key_file_init.
27  *
28  * Revision 1.15  1996/03/21  14:50:09  adam
29  * File update uses modify-time instead of change-time.
30  *
31  * Revision 1.14  1996/02/07  14:06:37  adam
32  * Better progress report during register merge.
33  * New command: clean - removes temporary shadow files.
34  *
35  * Revision 1.13  1996/02/05  12:30:00  adam
36  * Logging reduced a bit.
37  * The remaining running time is estimated during register merge.
38  *
39  * Revision 1.12  1995/12/06  17:49:19  adam
40  * Uses dict_delete now.
41  *
42  * Revision 1.11  1995/12/06  16:06:43  adam
43  * Better diagnostics. Work on 'real' dictionary deletion.
44  *
45  * Revision 1.10  1995/12/06  12:41:22  adam
46  * New command 'stat' for the index program.
47  * Filenames can be read from stdin by specifying '-'.
48  * Bug fix/enhancement of the transformation from terms to regular
49  * expressons in the search engine.
50  *
51  * Revision 1.9  1995/10/10  12:24:39  adam
52  * Temporary sort files are compressed.
53  *
54  * Revision 1.8  1995/10/04  16:57:19  adam
55  * Key input and merge sort in one pass.
56  *
57  * Revision 1.7  1995/10/02  15:18:52  adam
58  * New member in recRetrieveCtrl: diagnostic.
59  *
60  * Revision 1.6  1995/09/29  15:51:56  adam
61  * First work on multi-way read.
62  *
63  * Revision 1.5  1995/09/29  14:01:43  adam
64  * Bug fixes.
65  *
66  * Revision 1.4  1995/09/28  14:22:57  adam
67  * Sort uses smaller temporary files.
68  *
69  * Revision 1.3  1995/09/06  16:11:17  adam
70  * Option: only one word key per file.
71  *
72  * Revision 1.2  1995/09/04  12:33:42  adam
73  * Various cleanup. YAZ util used instead.
74  *
75  * Revision 1.1  1995/09/04  09:10:37  adam
76  * More work on index add/del/update.
77  * Merge sort implemented.
78  * Initial work on z39 server.
79  *
80  */
81
82 #include <fcntl.h>
83 #include <unistd.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <stdio.h>
87 #include <assert.h>
88
89 #include "index.h"
90
91 #define KEY_SIZE (1+sizeof(struct it_key))
92 #define INP_NAME_MAX 768
93 #define INP_BUF_START 60000
94 #define INP_BUF_ADD  400000
95
96 static int no_diffs   = 0;
97 static int no_updates = 0;
98 static int no_deletions = 0;
99 static int no_insertions = 0;
100 static int no_iterations = 0;
101
102 struct key_file {
103     int   no;            /* file no */
104     off_t offset;        /* file offset */
105     unsigned char *buf;  /* buffer block */
106     size_t buf_size;     /* number of read bytes in block */
107     size_t chunk;        /* number of bytes allocated */
108     size_t buf_ptr;      /* current position in buffer */
109     char *prev_name;     /* last word read */
110     int   sysno;         /* last sysno */
111     int   seqno;         /* last seqno */
112     off_t length;        /* length of file */
113                          /* handler invoked in each read */
114     void (*readHandler)(struct key_file *keyp, void *rinfo);
115     void *readInfo;
116 };
117
118 void getFnameTmp (char *fname, int no)
119 {
120     const char *pre;
121     
122     pre = res_get_def (common_resource, "keyTmpDir", ".");
123     sprintf (fname, "%s/key%d.tmp", pre, no);
124 }
125
126 void key_file_chunk_read (struct key_file *f)
127 {
128     int nr = 0, r, fd;
129     char fname[1024];
130     getFnameTmp (fname, f->no);
131     fd = open (fname, O_RDONLY);
132     if (fd == -1)
133     {
134         logf (LOG_FATAL|LOG_ERRNO, "cannot open %s", fname);
135         exit (1);
136     }
137     if (!f->length)
138     {
139         if ((f->length = lseek (fd, 0L, SEEK_END)) == (off_t) -1)
140         {
141             logf (LOG_FATAL|LOG_ERRNO, "cannot seek %s", fname);
142             exit (1);
143         }
144     }
145     if (lseek (fd, f->offset, SEEK_SET) == -1)
146     {
147         logf (LOG_FATAL|LOG_ERRNO, "cannot seek %s", fname);
148         exit (1);
149     }
150     while (f->chunk - nr > 0)
151     {
152         r = read (fd, f->buf + nr, f->chunk - nr);
153         if (r <= 0)
154             break;
155         nr += r;
156     }
157     if (r == -1)
158     {
159         logf (LOG_FATAL|LOG_ERRNO, "read of %s", fname);
160         exit (1);
161     }
162     f->buf_size = nr;
163     f->buf_ptr = 0;
164     if (f->readHandler)
165         (*f->readHandler)(f, f->readInfo);
166     close (fd);
167 }
168
169 struct key_file *key_file_init (int no, int chunk)
170 {
171     struct key_file *f;
172
173     f = xmalloc (sizeof(*f));
174     f->sysno = 0;
175     f->seqno = 0;
176     f->no = no;
177     f->chunk = chunk;
178     f->offset = 0;
179     f->length = 0;
180     f->readHandler = NULL;
181     f->buf = xmalloc (f->chunk);
182     f->prev_name = xmalloc (INP_NAME_MAX);
183     *f->prev_name = '\0';
184     key_file_chunk_read (f);
185     return f;
186 }
187
188 int key_file_getc (struct key_file *f)
189 {
190     if (f->buf_ptr < f->buf_size)
191         return f->buf[(f->buf_ptr)++];
192     if (f->buf_size < f->chunk)
193         return EOF;
194     f->offset += f->buf_size;
195     key_file_chunk_read (f);
196     if (f->buf_ptr < f->buf_size)
197         return f->buf[(f->buf_ptr)++];
198     else
199         return EOF;
200 }
201
202 int key_file_decode (struct key_file *f)
203 {
204     int c, d;
205
206     c = key_file_getc (f);
207     switch (c & 192) 
208     {
209     case 0:
210         d = c;
211         break;
212     case 64:
213         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
214         break;
215     case 128:
216         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
217         d = (d << 8) + (key_file_getc (f) & 0xff);
218         break;
219     case 192:
220         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
221         d = (d << 8) + (key_file_getc (f) & 0xff);
222         d = (d << 8) + (key_file_getc (f) & 0xff);
223         break;
224     }
225     return d;
226 }
227
228 int key_file_read (struct key_file *f, char *key)
229 {
230     int i, d, c;
231     struct it_key itkey;
232
233     c = key_file_getc (f);
234     if (c == 0)
235     {
236         strcpy (key, f->prev_name);
237         i = 1+strlen (key);
238     }
239     else if (c == EOF)
240         return 0;
241     else
242     {
243         i = 0;
244         key[i++] = c;
245         while ((key[i++] = key_file_getc (f)))
246             ;
247         strcpy (f->prev_name, key);
248         f->sysno = 0;
249     }
250     d = key_file_decode (f);
251     key[i++] = d & 1;
252     d = d >> 1;
253     itkey.sysno = d + f->sysno;
254     if (d) 
255     {
256         f->sysno = itkey.sysno;
257         f->seqno = 0;
258     }
259     d = key_file_decode (f);
260     itkey.seqno = d + f->seqno;
261     f->seqno = itkey.seqno;
262     memcpy (key + i, &itkey, sizeof(struct it_key));
263     return i + sizeof (struct it_key);
264 }
265
266 struct heap_info {
267     struct {
268         struct key_file **file;
269         char   **buf;
270     } info;
271     int    heapnum;
272     int    *ptr;
273     int    (*cmp)(const void *p1, const void *p2);
274     Dict dict;
275     ISAM isam;
276     ISAMC isamc;
277 };
278
279 struct heap_info *key_heap_init (int nkeys,
280                                  int (*cmp)(const void *p1, const void *p2))
281 {
282     struct heap_info *hi;
283     int i;
284
285     hi = xmalloc (sizeof(*hi));
286     hi->info.file = xmalloc (sizeof(*hi->info.file) * (1+nkeys));
287     hi->info.buf = xmalloc (sizeof(*hi->info.buf) * (1+nkeys));
288     hi->heapnum = 0;
289     hi->ptr = xmalloc (sizeof(*hi->ptr) * (1+nkeys));
290     hi->cmp = cmp;
291     for (i = 0; i<= nkeys; i++)
292     {
293         hi->ptr[i] = i;
294         hi->info.buf[i] = xmalloc (INP_NAME_MAX);
295     }
296     return hi;
297 }
298
299 static void key_heap_swap (struct heap_info *hi, int i1, int i2)
300 {
301     int swap;
302
303     swap = hi->ptr[i1];
304     hi->ptr[i1] = hi->ptr[i2];
305     hi->ptr[i2] = swap;
306 }
307
308
309 static void key_heap_delete (struct heap_info *hi)
310 {
311     int cur = 1, child = 2;
312
313     assert (hi->heapnum > 0);
314
315     key_heap_swap (hi, 1, hi->heapnum);
316     hi->heapnum--;
317     while (child <= hi->heapnum) {
318         if (child < hi->heapnum &&
319             (*hi->cmp)(&hi->info.buf[hi->ptr[child]],
320                        &hi->info.buf[hi->ptr[child+1]]) > 0)
321             child++;
322         if ((*hi->cmp)(&hi->info.buf[hi->ptr[cur]],
323                        &hi->info.buf[hi->ptr[child]]) > 0)
324         {            
325             key_heap_swap (hi, cur, child);
326             cur = child;
327             child = 2*cur;
328         }
329         else
330             break;
331     }
332 }
333
334 static void key_heap_insert (struct heap_info *hi, const char *buf, int nbytes,
335                              struct key_file *kf)
336 {
337     int cur, parent;
338
339     cur = ++(hi->heapnum);
340     memcpy (hi->info.buf[hi->ptr[cur]], buf, nbytes);
341     hi->info.file[hi->ptr[cur]] = kf;
342
343     parent = cur/2;
344     while (parent && (*hi->cmp)(&hi->info.buf[hi->ptr[parent]],
345                                 &hi->info.buf[hi->ptr[cur]]) > 0)
346     {
347         key_heap_swap (hi, cur, parent);
348         cur = parent;
349         parent = cur/2;
350     }
351 }
352
353 static int heap_read_one (struct heap_info *hi, char *name, char *key)
354 {
355     int n, r;
356     char rbuf[INP_NAME_MAX];
357     struct key_file *kf;
358
359     if (!hi->heapnum)
360         return 0;
361     n = hi->ptr[1];
362     strcpy (name, hi->info.buf[n]);
363     kf = hi->info.file[n];
364     r = strlen(name);
365     memcpy (key, hi->info.buf[n] + r+1, KEY_SIZE);
366     key_heap_delete (hi);
367     if ((r = key_file_read (kf, rbuf)))
368         key_heap_insert (hi, rbuf, r, kf);
369     no_iterations++;
370     return 1;
371 }
372
373 struct heap_cread_info {
374     char prev_name[INP_NAME_MAX];
375     char cur_name[INP_NAME_MAX];
376     char *key;
377     struct heap_info *hi;
378     int mode;
379     int more;
380 };
381       
382 int heap_cread_item (void *vp, char **dst, int *insertMode)
383 {
384     struct heap_cread_info *p = vp;
385     struct heap_info *hi = p->hi;
386
387     if (p->mode == 1)
388     {
389         *insertMode = p->key[0];
390         memcpy (*dst, p->key+1, sizeof(struct it_key));
391         (*dst) += sizeof(struct it_key);
392         p->mode = 2;
393         return 1;
394     }
395     strcpy (p->prev_name, p->cur_name);
396     if (!(p->more = heap_read_one (hi, p->cur_name, p->key)))
397         return 0;
398     if (*p->cur_name && strcmp (p->cur_name, p->prev_name))
399     {
400         p->mode = 1;
401         return 0;
402     }
403     *insertMode = p->key[0];
404     memcpy (*dst, p->key+1, sizeof(struct it_key));
405     (*dst) += sizeof(struct it_key);
406     return 1;
407 }
408
409 int heap_inpc (struct heap_info *hi)
410 {
411     struct heap_cread_info hci;
412     ISAMC_I isamc_i = xmalloc (sizeof(*isamc_i));
413
414     hci.key = xmalloc (KEY_SIZE);
415     hci.mode = 1;
416     hci.hi = hi;
417     hci.more = heap_read_one (hi, hci.cur_name, hci.key);
418
419     isamc_i->clientData = &hci;
420     isamc_i->read_item = heap_cread_item;
421
422     while (hci.more)
423     {
424         char this_name[INP_NAME_MAX];
425         ISAMC_P isamc_p, isamc_p2;
426         char *dict_info;
427
428         strcpy (this_name, hci.cur_name);
429         logf (LOG_DEBUG, "inserting %s", 1+hci.cur_name);
430         no_diffs++;
431         if ((dict_info = dict_lookup (hi->dict, hci.cur_name)))
432         {
433             memcpy (&isamc_p, dict_info+1, sizeof(ISAMC_P));
434             isamc_p2 = isc_merge (hi->isamc, isamc_p, isamc_i);
435             if (!isamc_p2)
436             {
437                 no_deletions++;
438                 if (!dict_delete (hi->dict, this_name))
439                     abort();
440             }
441             else 
442             {
443                 no_updates++;
444                 if (isamc_p2 != isamc_p)
445                     dict_insert (hi->dict, this_name,
446                                  sizeof(ISAMC_P), &isamc_p2);
447             }
448         } 
449         else
450         {
451             isamc_p = isc_merge (hi->isamc, 0, isamc_i);
452             no_insertions++;
453             dict_insert (hi->dict, this_name, sizeof(ISAMC_P), &isamc_p);
454         }
455     }
456     xfree (isamc_i);
457     return 0;
458
459
460 int heap_inp (struct heap_info *hi)
461 {
462     char *info;
463     char next_name[INP_NAME_MAX];
464     char cur_name[INP_NAME_MAX];
465     int key_buf_size = INP_BUF_START;
466     int key_buf_ptr;
467     char *next_key;
468     char *key_buf;
469     int more;
470     
471     next_key = xmalloc (KEY_SIZE);
472     key_buf = xmalloc (key_buf_size);
473     more = heap_read_one (hi, cur_name, key_buf);
474     while (more)                   /* EOF ? */
475     {
476         int nmemb;
477         key_buf_ptr = KEY_SIZE;
478         while (1)
479         {
480             if (!(more = heap_read_one (hi, next_name, next_key)))
481                 break;
482             if (*next_name && strcmp (next_name, cur_name))
483                 break;
484             memcpy (key_buf + key_buf_ptr, next_key, KEY_SIZE);
485             key_buf_ptr += KEY_SIZE;
486             if (key_buf_ptr+KEY_SIZE >= key_buf_size)
487             {
488                 char *new_key_buf;
489                 new_key_buf = xmalloc (key_buf_size + INP_BUF_ADD);
490                 memcpy (new_key_buf, key_buf, key_buf_size);
491                 key_buf_size += INP_BUF_ADD;
492                 xfree (key_buf);
493                 key_buf = new_key_buf;
494             }
495         }
496         no_diffs++;
497         nmemb = key_buf_ptr / KEY_SIZE;
498         assert (nmemb*KEY_SIZE == key_buf_ptr);
499         if ((info = dict_lookup (hi->dict, cur_name)))
500         {
501             ISAM_P isam_p, isam_p2;
502             logf (LOG_DEBUG, "updating %s", 1+cur_name);
503             memcpy (&isam_p, info+1, sizeof(ISAM_P));
504             isam_p2 = is_merge (hi->isam, isam_p, nmemb, key_buf);
505             if (!isam_p2)
506             {
507                 no_deletions++;
508                 if (!dict_delete (hi->dict, cur_name))
509                     abort ();
510             }
511             else 
512             {
513                 no_updates++;
514                 if (isam_p2 != isam_p)
515                     dict_insert (hi->dict, cur_name, sizeof(ISAM_P), &isam_p2);
516             }
517         }
518         else
519         {
520             ISAM_P isam_p;
521             logf (LOG_DEBUG, "inserting %s", 1+cur_name);
522             no_insertions++;
523             isam_p = is_merge (hi->isam, 0, nmemb, key_buf);
524             dict_insert (hi->dict, cur_name, sizeof(ISAM_P), &isam_p);
525         }
526         memcpy (key_buf, next_key, KEY_SIZE);
527         strcpy (cur_name, next_name);
528     }
529     return 0;
530 }
531
532 struct progressInfo {
533     time_t   startTime;
534     time_t   lastTime;
535     off_t    totalBytes;
536     off_t    totalOffset;
537 };
538
539 void progressFunc (struct key_file *keyp, void *info)
540 {
541     struct progressInfo *p = info;
542     time_t now, remaining;
543
544     if (keyp->buf_size <= 0 || p->totalBytes <= 0)
545         return ;
546     time (&now);
547
548     if (now >= p->lastTime+10)
549     {
550         p->lastTime = now;
551         remaining = (now - p->startTime)*
552             ((double) p->totalBytes/p->totalOffset - 1.0);
553         if (remaining <= 130)
554             logf (LOG_LOG, "Merge %2.1f%% completed; %ld seconds remaining",
555                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining);
556         else
557             logf (LOG_LOG, "Merge %2.1f%% completed; %ld minutes remaining",
558                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining/60);
559     }
560     p->totalOffset += keyp->buf_size;
561 }
562
563 void key_input (int nkeys, int cache)
564                 
565 {
566     Dict dict;
567     ISAM isam = NULL;
568     ISAMC isamc = NULL;
569     struct key_file **kf;
570     char rbuf[1024];
571     int i, r;
572     struct heap_info *hi;
573     struct progressInfo progressInfo;
574
575     if (nkeys < 0)
576     {
577         char fname[1024];
578         nkeys = 0;
579         while (1)
580         {
581             getFnameTmp (fname, nkeys+1);
582             if (access (fname, R_OK) == -1)
583                 break;
584             nkeys++;
585         }
586         if (!nkeys)
587             return ;
588     }
589     dict = dict_open (FNAME_DICT, cache, 1);
590     if (!dict)
591     {
592         logf (LOG_FATAL, "dict_open fail");
593         exit (1);
594     }
595     if (res_get_match (common_resource, "isam", "c", NULL))
596     {
597         isamc = isc_open (FNAME_ISAMC, 1, key_isamc_m ());
598         if (!isamc)
599         {
600             logf (LOG_FATAL, "isc_open fail");
601             exit (1);
602         }
603     }
604     else
605     {
606         isam = is_open (FNAME_ISAM, key_compare, 1, sizeof(struct it_key));
607         if (!isam)
608         {
609             logf (LOG_FATAL, "is_open fail");
610             exit (1);
611         }
612     }
613     kf = xmalloc ((1+nkeys) * sizeof(*kf));
614     progressInfo.totalBytes = 0;
615     progressInfo.totalOffset = 0;
616     time (&progressInfo.startTime);
617     time (&progressInfo.lastTime);
618     for (i = 1; i<=nkeys; i++)
619     {
620         kf[i] = key_file_init (i, 32768);
621         kf[i]->readHandler = progressFunc;
622         kf[i]->readInfo = &progressInfo;
623         progressInfo.totalBytes += kf[i]->length;
624         progressInfo.totalOffset += kf[i]->buf_size;
625     }
626     hi = key_heap_init (nkeys, key_qsort_compare);
627     hi->dict = dict;
628     hi->isam = isam;
629     hi->isamc = isamc;
630
631     for (i = 1; i<=nkeys; i++)
632         if ((r = key_file_read (kf[i], rbuf)))
633             key_heap_insert (hi, rbuf, r, kf[i]);
634     if (isamc)
635         heap_inpc (hi);
636     else
637         heap_inp (hi);
638     dict_close (dict);
639     if (isam)
640         is_close (isam);
641     if (isamc)
642         isc_close (isamc);
643     
644     for (i = 1; i<=nkeys; i++)
645     {
646         getFnameTmp (rbuf, i);
647         unlink (rbuf);
648     }
649     logf (LOG_LOG, "Iterations . . .%7d", no_iterations);
650     logf (LOG_LOG, "Distinct words .%7d", no_diffs);
651     logf (LOG_LOG, "Updates. . . . .%7d", no_updates);
652     logf (LOG_LOG, "Deletions. . . .%7d", no_deletions);
653     logf (LOG_LOG, "Insertions . . .%7d", no_insertions);
654 }
655
656