New update method: the 'old' keys are saved for each records.
[idzebra-moved-to-github.git] / index / extract.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: extract.c,v $
7  * Revision 1.27  1995-11-20 16:59:45  adam
8  * New update method: the 'old' keys are saved for each records.
9  *
10  * Revision 1.26  1995/11/20  11:56:24  adam
11  * Work on new traversal.
12  *
13  * Revision 1.25  1995/11/16  15:34:54  adam
14  * Uses new record management system in both indexer and server.
15  *
16  * Revision 1.24  1995/11/15  19:13:08  adam
17  * Work on record management.
18  *
19  * Revision 1.23  1995/10/27  14:00:10  adam
20  * Implemented detection of database availability.
21  *
22  * Revision 1.22  1995/10/17  18:02:07  adam
23  * New feature: databases. Implemented as prefix to words in dictionary.
24  *
25  * Revision 1.21  1995/10/10  12:24:38  adam
26  * Temporary sort files are compressed.
27  *
28  * Revision 1.20  1995/10/06  13:52:05  adam
29  * Bug fixes. Handler may abort further scanning.
30  *
31  * Revision 1.19  1995/10/04  12:55:16  adam
32  * Bug fix in ranked search. Use=Any keys inserted.
33  *
34  * Revision 1.18  1995/10/04  09:37:08  quinn
35  * Fixed bug.
36  *
37  * Revision 1.17  1995/10/03  14:28:57  adam
38  * Buffered read in extract works.
39  *
40  * Revision 1.16  1995/10/03  14:28:45  adam
41  * Work on more effecient read handler in extract.
42  *
43  * Revision 1.15  1995/10/02  15:42:53  adam
44  * Extract uses file descriptors instead of FILE pointers.
45  *
46  * Revision 1.14  1995/10/02  15:29:13  adam
47  * More logging in file_extract.
48  *
49  * Revision 1.13  1995/09/29  14:01:39  adam
50  * Bug fixes.
51  *
52  * Revision 1.12  1995/09/28  14:22:56  adam
53  * Sort uses smaller temporary files.
54  *
55  * Revision 1.11  1995/09/28  12:10:31  adam
56  * Bug fixes. Field prefix used in queries.
57  *
58  * Revision 1.10  1995/09/28  09:19:41  adam
59  * xfree/xmalloc used everywhere.
60  * Extract/retrieve method seems to work for text records.
61  *
62  * Revision 1.9  1995/09/27  12:22:28  adam
63  * More work on extract in record control.
64  * Field name is not in isam keys but in prefix in dictionary words.
65  *
66  * Revision 1.8  1995/09/14  07:48:22  adam
67  * Record control management.
68  *
69  * Revision 1.7  1995/09/11  13:09:32  adam
70  * More work on relevance feedback.
71  *
72  * Revision 1.6  1995/09/08  14:52:27  adam
73  * Minor changes. Dictionary is lower case now.
74  *
75  * Revision 1.5  1995/09/06  16:11:16  adam
76  * Option: only one word key per file.
77  *
78  * Revision 1.4  1995/09/05  15:28:39  adam
79  * More work on search engine.
80  *
81  * Revision 1.3  1995/09/04  12:33:41  adam
82  * Various cleanup. YAZ util used instead.
83  *
84  * Revision 1.2  1995/09/04  09:10:34  adam
85  * More work on index add/del/update.
86  * Merge sort implemented.
87  * Initial work on z39 server.
88  *
89  * Revision 1.1  1995/09/01  14:06:35  adam
90  * Split of work into more files.
91  *
92  */
93 #include <stdio.h>
94 #include <assert.h>
95 #include <unistd.h>
96 #include <fcntl.h>
97 #include <ctype.h>
98
99 #include <alexutil.h>
100 #include <recctrl.h>
101 #include "index.h"
102
103 #if RECORD_BASE
104 #include "recindex.h"
105 #endif
106
107 static Dict file_idx;
108
109
110 #if RECORD_BASE
111 static Records records = NULL;
112 #else
113 static int sys_idx_fd = -1;
114 static SYSNO sysno_next;
115 #endif
116
117 static int key_cmd;
118 static int key_sysno;
119 static const char *key_databaseName;
120 static char **key_buf;
121 static size_t ptr_top;
122 static size_t ptr_i;
123 static size_t key_buf_used;
124 static int key_file_no;
125
126 static int key_del_max;
127 static int key_del_used;
128 static char *key_del_buf;
129
130 void key_open (int mem)
131 {
132 #if !RECORD_BASE
133     void *file_key;
134 #endif
135     if (mem < 50000)
136         mem = 50000;
137     key_buf = xmalloc (mem);
138     ptr_top = mem/sizeof(char*);
139     ptr_i = 0;
140
141     key_buf_used = 0;
142     key_file_no = 0;
143
144     key_del_buf = NULL;
145     key_del_max = 0;
146
147     if (!(file_idx = dict_open (FNAME_FILE_DICT, 40, 1)))
148     {
149         logf (LOG_FATAL, "dict_open fail of %s", "fileidx");
150         exit (1);
151     }
152 #if RECORD_BASE
153     assert (!records);
154     records = rec_open (1);
155 #else
156     file_key = dict_lookup (file_idx, ".");
157     if (file_key)
158         memcpy (&sysno_next, (char*)file_key+1, sizeof(sysno_next));
159     else
160         sysno_next = 1;
161     if ((sys_idx_fd = open (FNAME_SYS_IDX, O_RDWR|O_CREAT, 0666)) == -1)
162     {
163         logf (LOG_FATAL|LOG_ERRNO, "open %s", FNAME_SYS_IDX);
164         exit (1);
165     }
166 #endif
167 }
168
169 struct encode_info {
170     int  sysno;
171     int  seqno;
172     char buf[512];
173 };
174
175 void encode_key_init (struct encode_info *i)
176 {
177     i->sysno = 0;
178     i->seqno = 0;
179 }
180
181 char *encode_key_int (int d, char *bp)
182 {
183     if (d <= 63)
184         *bp++ = d;
185     else if (d <= 16383)
186     {
187         *bp++ = 64 + (d>>8);
188         *bp++ = d  & 255;
189     }
190     else if (d <= 4194303)
191     {
192         *bp++ = 128 + (d>>16);
193         *bp++ = (d>>8) & 255;
194         *bp++ = d & 255;
195     }
196     else
197     {
198         *bp++ = 192 + (d>>24);
199         *bp++ = (d>>16) & 255;
200         *bp++ = (d>>8) & 255;
201         *bp++ = d & 255;
202     }
203     return bp;
204 }
205
206 void encode_key_write (char *k, struct encode_info *i, FILE *outf)
207 {
208     struct it_key key;
209     char *bp = i->buf;
210
211     while ((*bp++ = *k++))
212         ;
213     memcpy (&key, k+1, sizeof(struct it_key));
214     bp = encode_key_int ( (key.sysno - i->sysno) * 2 + *k, bp);
215     if (i->sysno != key.sysno)
216     {
217         i->sysno = key.sysno;
218         i->seqno = 0;
219     }
220     bp = encode_key_int (key.seqno - i->seqno, bp);
221     i->seqno = key.seqno;
222     if (fwrite (i->buf, bp - i->buf, 1, outf) != 1)
223     {
224         logf (LOG_FATAL|LOG_ERRNO, "fwrite");
225         exit (1);
226     }
227 }
228
229 void key_flush (void)
230 {
231     FILE *outf;
232     char out_fname[200];
233     char *prevcp, *cp;
234     struct encode_info encode_info;
235     
236     if (ptr_i <= 0)
237         return;
238
239     key_file_no++;
240     logf (LOG_LOG, "sorting section %d", key_file_no);
241     qsort (key_buf + ptr_top-ptr_i, ptr_i, sizeof(char*), key_qsort_compare);
242     sprintf (out_fname, TEMP_FNAME, key_file_no);
243
244     if (!(outf = fopen (out_fname, "w")))
245     {
246         logf (LOG_FATAL|LOG_ERRNO, "fopen (4) %s", out_fname);
247         exit (1);
248     }
249     logf (LOG_LOG, "writing section %d", key_file_no);
250     prevcp = cp = key_buf[ptr_top-ptr_i];
251     
252     encode_key_init (&encode_info);
253     encode_key_write (cp, &encode_info, outf);
254     while (--ptr_i > 0)
255     {
256         cp = key_buf[ptr_top-ptr_i];
257         if (strcmp (cp, prevcp))
258         {
259             encode_key_init (&encode_info);
260             encode_key_write (cp, &encode_info, outf);
261             prevcp = cp;
262         }
263         else
264             encode_key_write (cp + strlen(cp), &encode_info, outf);
265     }
266     if (fclose (outf))
267     {
268         logf (LOG_FATAL|LOG_ERRNO, "fclose %s", out_fname);
269         exit (1);
270     }
271     logf (LOG_LOG, "finished section %d", key_file_no);
272     ptr_i = 0;
273     key_buf_used = 0;
274 }
275
276 int key_close (void)
277 {
278     key_flush ();
279     xfree (key_buf);
280 #if RECORD_BASE
281     rec_close (&records);
282 #else
283     close (sys_idx_fd);
284     dict_insert (file_idx, ".", sizeof(sysno_next), &sysno_next);
285 #endif
286     dict_close (file_idx);
287
288     xfree (key_del_buf);
289     key_del_buf = NULL;
290     key_del_max = 0;
291     return key_file_no;
292 }
293
294 static void wordInit (RecWord *p)
295 {
296     p->attrSet = 1;
297     p->attrUse = 1016;
298     p->which = Word_String;
299 }
300
301 static void wordAdd (const RecWord *p)
302 {
303     struct it_key key;
304     size_t i;
305
306     if (key_buf_used + 1024 > (ptr_top-ptr_i)*sizeof(char*))
307         key_flush ();
308     ++ptr_i;
309     key_buf[ptr_top-ptr_i] = (char*)key_buf + key_buf_used;
310     key_buf_used += index_word_prefix ((char*)key_buf + key_buf_used,
311                                 p->attrSet, p->attrUse,
312                                 key_databaseName);
313     switch (p->which)
314     {
315     case Word_String:
316         for (i = 0; p->u.string[i]; i++)
317             ((char*)key_buf) [key_buf_used++] =
318                 index_char_cvt (p->u.string[i]);
319         ((char*)key_buf) [key_buf_used++] = '\0';
320         break;
321     default:
322         return ;
323     }
324     ((char*) key_buf)[key_buf_used++] = ((key_cmd == 'a') ? 1 : 0);
325     key.sysno = key_sysno;
326     key.seqno = p->seqno;
327     memcpy ((char*)key_buf + key_buf_used, &key, sizeof(key));
328     key_buf_used += sizeof(key);
329
330     if (key_cmd == 'a' && key_del_used >= 0)
331     {
332         char attrSet;
333         short attrUse;
334         if (key_del_used + 1024 > key_del_max)
335         {
336             char *kbn;
337             
338             if (!(kbn = malloc (key_del_max += 64000)))
339             {
340                 logf (LOG_FATAL, "malloc");
341                 exit (1);
342             }
343             if (key_del_buf)
344                 memcpy (kbn, key_del_buf, key_del_used);
345             free (key_del_buf);
346             key_del_buf = kbn;
347         }
348         switch (p->which)
349         {
350         case Word_String:
351             for (i = 0; p->u.string[i]; i++)
352                 ((char*)key_del_buf) [key_del_used++] = p->u.string[i];
353             ((char*)key_del_buf) [key_del_used++] = '\0';
354             break;
355         default:
356             return ;
357         }
358         attrSet = p->attrSet;
359         memcpy (key_del_buf + key_del_used, &attrSet, sizeof(attrSet));
360         key_del_used += sizeof(attrSet);
361
362         attrUse = p->attrUse;
363         memcpy (key_del_buf + key_del_used, &attrUse, sizeof(attrUse));
364         key_del_used += sizeof(attrUse);
365
366         memcpy (key_del_buf + key_del_used, &p->seqno, sizeof(p->seqno));
367         key_del_used += sizeof(p->seqno);
368     }
369 }
370
371 static void wordAddAny (const RecWord *p)
372 {
373     if (p->attrSet != 1 || p->attrUse != 1016)
374     {
375         RecWord w;
376
377         memcpy (&w, p, sizeof(w));
378         w.attrSet = 1;
379         w.attrUse = 1016;
380         wordAdd (&w);
381     }
382     wordAdd (p);
383 }
384
385 static char *file_buf;
386 static int file_offset;
387 static int file_bufsize;
388
389 static void file_read_start (int fd)
390 {
391     file_offset = 0;
392     file_buf = xmalloc (4096);
393     file_bufsize = read (fd, file_buf, 4096);
394 }
395
396 static void file_read_stop (int fd)
397 {
398     xfree (file_buf);
399 }
400
401 static int file_read (int fd, char *buf, size_t count)
402 {
403     int l = file_bufsize - file_offset;
404
405     if (count > l)
406     {
407         int r;
408         if (l > 0)
409             memcpy (buf, file_buf + file_offset, l);
410         count = count-l;
411         if (count > file_bufsize)
412         {
413             if ((r = read (fd, buf + l, count)) == -1)
414             {
415                 logf (LOG_FATAL|LOG_ERRNO, "read");
416                 exit (1);
417             }
418             file_bufsize = 0;
419             file_offset = 0;
420             return r;
421         }
422         file_bufsize = r = read (fd, file_buf, 4096);
423         if (r == -1)
424         {
425             logf (LOG_FATAL|LOG_ERRNO, "read");
426             exit (1);
427         }
428         else if (r <= count)
429         {
430             file_offset = r;
431             memcpy (buf + l, file_buf, r);
432             return l + r;
433         }
434         else
435         {
436             file_offset = count;
437             memcpy (buf + l, file_buf, count - l);
438             return count;
439         }
440     }
441     memcpy (buf, file_buf + file_offset, count);
442     file_offset += count;
443     return count;
444 }
445
446 SYSNO file_extract (int cmd, const char *fname, const char *kname,
447                     char *databaseName)
448 {
449     int i, r;
450     char ext[128];
451     SYSNO sysno;
452     char ext_res[128];
453     const char *file_type;
454     void *file_info;
455     struct recExtractCtrl extractCtrl;
456     RecType rt;
457
458     key_del_used = -1;
459     key_databaseName = databaseName;
460     for (i = strlen(fname); --i >= 0; )
461         if (fname[i] == '/')
462         {
463             strcpy (ext, "");
464             break;
465         }
466         else if (fname[i] == '.')
467         {
468             strcpy (ext, fname+i+1);
469             break;
470         }
471     sprintf (ext_res, "fileExtension.%s", ext);
472     if (!(file_type = res_get (common_resource, ext_res)))
473         return 0;
474     if (!(rt = recType_byName (file_type)))
475         return 0;
476     logf (LOG_DEBUG, "%c %s k=%s", cmd, fname, kname);
477     file_info = dict_lookup (file_idx, kname);
478     if (!file_info)
479     {
480 #if RECORD_BASE
481         Record rec = rec_new (records);
482
483         sysno = rec->sysno;
484         dict_insert (file_idx, kname, sizeof(sysno), &sysno);
485         rec->info[0] = rec_strdup (file_type, &rec->size[0]);
486         rec->info[1] = rec_strdup (kname, &rec->size[1]);
487         rec_put (records, &rec);
488 #else
489         sysno = sysno_next++;
490         dict_insert (file_idx, kname, sizeof(sysno), &sysno);
491         lseek (sys_idx_fd, sysno * SYS_IDX_ENTRY_LEN, SEEK_SET);
492         write (sys_idx_fd, file_type, strlen (file_type)+1);
493         write (sys_idx_fd, kname, strlen(kname)+1);
494 #endif
495     }
496     else
497         memcpy (&sysno, (char*) file_info+1, sizeof(sysno));
498
499     if ((extractCtrl.fd = open (fname, O_RDONLY)) == -1)
500     {
501         logf (LOG_WARN|LOG_ERRNO, "open %s", fname);
502         return 0;
503     }
504     extractCtrl.subType = "";
505     extractCtrl.init = wordInit;
506     extractCtrl.add = wordAddAny;
507
508     file_read_start (extractCtrl.fd);
509
510     extractCtrl.readf = file_read;
511     key_sysno = sysno;
512     key_cmd = cmd;
513     r = (*rt->extract)(&extractCtrl);
514
515     file_read_stop (extractCtrl.fd);
516
517     close (extractCtrl.fd);
518     if (r)
519         logf (LOG_WARN, "Couldn't extract file %s, code %d", fname, r);
520     return sysno;
521 }
522
523 int fileExtract (SYSNO *sysno, const char *fname, const char *databaseName,
524                  int deleteFlag)
525 {
526     int i, r;
527     char ext[128];
528     char ext_res[128];
529     const char *file_type;
530     struct recExtractCtrl extractCtrl;
531     RecType rt;
532     Record rec;
533
534     logf (LOG_DEBUG, "fileExtractAdd %s", fname);
535
536     key_del_used = 0;
537     for (i = strlen(fname); --i >= 0; )
538         if (fname[i] == '/')
539         {
540             strcpy (ext, "");
541             break;
542         }
543         else if (fname[i] == '.')
544         {
545             strcpy (ext, fname+i+1);
546             break;
547         }
548     sprintf (ext_res, "fileExtension.%s", ext);
549     if (!(file_type = res_get (common_resource, ext_res)))
550         return 0;
551     if (!(rt = recType_byName (file_type)))
552         return 0;
553
554     if ((extractCtrl.fd = open (fname, O_RDONLY)) == -1)
555     {
556         logf (LOG_WARN|LOG_ERRNO, "open %s", fname);
557         return 0;
558     }
559
560     extractCtrl.subType = "";
561     extractCtrl.init = wordInit;
562     extractCtrl.add = wordAddAny;
563
564     if (! *sysno)
565     {
566         logf (LOG_LOG, "add record %s", fname);
567         rec = rec_new (records);
568         *sysno = rec->sysno;
569         rec->info[0] = rec_strdup (file_type, &rec->size[0]);
570         rec->info[1] = rec_strdup (fname, &rec->size[1]);
571         rec->info[3] = rec_strdup (databaseName, &rec->size[3]);
572     }
573     else
574     {
575         size_t off;
576         char *kb;
577
578         if (deleteFlag)
579             logf (LOG_LOG, "delete record %s", fname);
580         else
581             logf (LOG_LOG, "update record %s", fname);
582         rec = rec_get (records, *sysno);
583
584         key_cmd = 'd';
585         key_sysno = *sysno;
586         key_databaseName = rec->info[3];
587         kb = rec->info[2];
588         for (off = 0; off < rec->size[2]; )
589         {
590             RecWord rw;
591             char   attrSet;
592             short  attrUse;
593
594             rw.which = Word_String;
595             rw.u.string = kb + off;
596             while (kb[off])
597                 off++;
598             off++;
599             memcpy (&attrSet, kb + off, sizeof(attrSet));
600             off += sizeof(attrSet);
601             memcpy (&attrUse, kb + off, sizeof(attrUse));
602             off += sizeof(attrUse);
603             memcpy (&rw.seqno, kb + off, sizeof(rw.seqno));
604             off += sizeof(rw.seqno);
605             rw.attrSet = attrSet;
606             rw.attrUse = attrUse;
607
608             (*extractCtrl.add) (&rw);
609         }
610         assert (off == rec->size[2]);
611         free (rec->info[2]);
612         rec->info[2] = NULL;
613         rec->size[2] = 0;
614
615         free (rec->info[3]);
616         rec->info[3] = rec_strdup (databaseName, &rec->size[3]);
617     }
618
619     if (deleteFlag)
620     {
621 #if 0
622         rec_del (records, *sysno);
623         rec_rm (&rec);
624 #endif
625         return 1;
626     }
627     
628     key_databaseName = databaseName;
629     key_sysno = *sysno;
630     key_cmd = 'a';
631
632     file_read_start (extractCtrl.fd);
633     extractCtrl.readf = file_read;
634     r = (*rt->extract)(&extractCtrl);
635     file_read_stop (extractCtrl.fd);
636
637     close (extractCtrl.fd);
638     if (r)
639     {
640         rec_rm (&rec);
641         logf (LOG_WARN, "Couldn't extract file %s, code %d", fname, r);
642         return 0;
643     }
644     if (key_del_used > 0)
645     {
646         rec->size[2] = key_del_used;
647         rec->info[2] = malloc (rec->size[2]);
648         memcpy (rec->info[2], key_del_buf, rec->size[2]);
649     }
650     rec_put (records, &rec);
651     return 1;
652 }