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