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