Updated WIN32 code specific sections. Changed header.
[idzebra-moved-to-github.git] / index / recindex.c
1 /*
2  * Copyright (C) 1994-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recindex.c,v $
7  * Revision 1.21  1999-02-02 14:51:03  adam
8  * Updated WIN32 code specific sections. Changed header.
9  *
10  * Revision 1.20  1998/01/12 15:04:08  adam
11  * The test option (-s) only uses read-lock (and not write lock).
12  *
13  * Revision 1.19  1997/09/17 12:19:16  adam
14  * Zebra version corresponds to YAZ version 1.4.
15  * Changed Zebra server so that it doesn't depend on global common_resource.
16  *
17  * Revision 1.18  1997/07/15 16:28:42  adam
18  * Bug fix: storeData didn't work with files with multiple records.
19  * Bug fix: fixed memory management with records; not really well
20  *  thought through.
21  *
22  * Revision 1.17  1997/02/12 20:39:46  adam
23  * Implemented options -f <n> that limits the log to the first <n>
24  * records.
25  * Changed some log messages also.
26  *
27  * Revision 1.16  1996/06/04 10:19:00  adam
28  * Minor changes - removed include of ctype.h.
29  *
30  * Revision 1.15  1996/05/13  14:23:06  adam
31  * Work on compaction of set/use bytes in dictionary.
32  *
33  * Revision 1.14  1996/02/01  20:48:15  adam
34  * The total size of records are always checked in rec_cache_insert to
35  * reduce memory usage.
36  *
37  * Revision 1.13  1995/12/11  09:12:49  adam
38  * The rec_get function returns NULL if record doesn't exist - will
39  * happen in the server if the result set records have been deleted since
40  * the creation of the set (i.e. the search).
41  * The server saves a result temporarily if it is 'volatile', i.e. the
42  * set is register dependent.
43  *
44  * Revision 1.12  1995/12/07  17:38:47  adam
45  * Work locking mechanisms for concurrent updates/commit.
46  *
47  * Revision 1.11  1995/12/06  13:58:26  adam
48  * Improved flushing of records - all flushes except the last one
49  * don't write the last accessed. Also flush takes place if record
50  * info occupy more than about 256k.
51  *
52  * Revision 1.10  1995/12/06  12:41:24  adam
53  * New command 'stat' for the index program.
54  * Filenames can be read from stdin by specifying '-'.
55  * Bug fix/enhancement of the transformation from terms to regular
56  * expressons in the search engine.
57  *
58  * Revision 1.9  1995/11/30  08:34:33  adam
59  * Started work on commit facility.
60  * Changed a few malloc/free to xmalloc/xfree.
61  *
62  * Revision 1.8  1995/11/28  14:26:21  adam
63  * Bug fix: recordId with constant wasn't right.
64  * Bug fix: recordId dictionary entry wasn't deleted when needed.
65  *
66  * Revision 1.7  1995/11/28  09:09:43  adam
67  * Zebra config renamed.
68  * Use setting 'recordId' to identify record now.
69  * Bug fix in recindex.c: rec_release_blocks was invokeded even
70  * though the blocks were already released.
71  * File traversal properly deletes records when needed.
72  *
73  * Revision 1.6  1995/11/25  10:24:06  adam
74  * More record fields - they are enumerated now.
75  * New options: flagStoreData flagStoreKey.
76  *
77  * Revision 1.5  1995/11/22  17:19:18  adam
78  * Record management uses the bfile system.
79  *
80  * Revision 1.4  1995/11/20  16:59:46  adam
81  * New update method: the 'old' keys are saved for each records.
82  *
83  * Revision 1.3  1995/11/16  15:34:55  adam
84  * Uses new record management system in both indexer and server.
85  *
86  * Revision 1.2  1995/11/15  19:13:08  adam
87  * Work on record management.
88  *
89  * Revision 1.1  1995/11/15  14:46:20  adam
90  * Started work on better record management system.
91  *
92  */
93 #include <stdio.h>
94 #include <assert.h>
95 #include <string.h>
96
97 #include "recindxp.h"
98
99 static void rec_write_head (Records p)
100 {
101     int r;
102
103     assert (p);
104     assert (p->index_BFile);
105
106     r = bf_write (p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
107     if (r)
108     {
109         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
110         exit (1);
111     }
112 }
113
114 static void rec_tmp_expand (Records p, int size, int dst_type)
115 {
116     if (p->tmp_size < size + 2048 ||
117         p->tmp_size < p->head.block_size[dst_type]*2)
118     {
119         xfree (p->tmp_buf);
120         p->tmp_size = size + p->head.block_size[dst_type]*2 + 2048;
121         p->tmp_buf = xmalloc (p->tmp_size);
122     }
123 }
124
125 static int read_indx (Records p, int sysno, void *buf, int itemsize, 
126                       int ignoreError)
127 {
128     int r;
129     int pos = (sysno-1)*itemsize;
130
131     r = bf_read (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
132     if (r != 1 && !ignoreError)
133     {
134         logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
135               p->index_fname, (long) pos);
136         exit (1);
137     }
138     return r;
139 }
140
141 static void write_indx (Records p, int sysno, void *buf, int itemsize)
142 {
143     int pos = (sysno-1)*itemsize;
144
145     bf_write (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
146 }
147
148 static void rec_release_blocks (Records p, int sysno)
149 {
150     struct record_index_entry entry;
151     int freeblock, freenext;
152     int dst_type;
153
154     if (read_indx (p, sysno, &entry, sizeof(entry), 1) != 1)
155         return ;
156     p->head.total_bytes -= entry.size;
157     freeblock = entry.next;
158     assert (freeblock > 0);
159     dst_type = freeblock & 7;
160     assert (dst_type < REC_BLOCK_TYPES);
161     freeblock = freeblock / 8;
162     while (freeblock)
163     {
164         if (bf_read (p->data_BFile[dst_type], freeblock, 0, sizeof(freenext),
165                      &freenext) != 1)
166         {
167             logf (LOG_FATAL|LOG_ERRNO, "read in rec_del_single");
168             exit (1);
169         }
170         if (bf_write (p->data_BFile[dst_type], freeblock, 0, sizeof(freenext),
171                       &p->head.block_free[dst_type]))
172         {
173             logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
174             exit (1);
175         }
176         p->head.block_free[dst_type] = freeblock;
177         freeblock = freenext;
178         p->head.block_used[dst_type]--;
179     }
180 }
181
182 static void rec_delete_single (Records p, Record rec)
183 {
184     struct record_index_entry entry;
185
186     rec_release_blocks (p, rec->sysno);
187
188     entry.next = p->head.index_free;
189     entry.size = 0;
190     p->head.index_free = rec->sysno;
191     write_indx (p, rec->sysno, &entry, sizeof(entry));
192 }
193
194
195 static void rec_write_single (Records p, Record rec)
196 {
197     int i, size = 0;
198     char *cptr;
199     int dst_type = 0;
200     int no_written = 0;
201     int block_prev = -1, block_free;
202     struct record_index_entry entry;
203
204     for (i = 0; i < REC_NO_INFO; i++)
205         if (!rec->info[i])
206             size += sizeof(*rec->size);
207         else
208             size += sizeof(*rec->size) + rec->size[i];
209
210     for (i = 1; i<REC_BLOCK_TYPES; i++)
211         if (size >= p->head.block_move[i])
212             dst_type = i;
213
214     rec_tmp_expand (p, size, dst_type);
215
216     cptr = p->tmp_buf + sizeof(int);           /* a hack! */
217     for (i = 0; i < REC_NO_INFO; i++)
218     {
219         memcpy (cptr, &rec->size[i], sizeof(*rec->size));
220         cptr += sizeof(*rec->size);
221         if (rec->info[i])
222         {
223             memcpy (cptr, rec->info[i], rec->size[i]);
224             cptr += rec->size[i];
225         }
226     }
227     cptr = p->tmp_buf;
228     while (no_written < size)
229     {
230         block_free = p->head.block_free[dst_type];
231         if (block_free)
232         {
233             if (bf_read (p->data_BFile[dst_type],
234                          block_free, 0, sizeof(*p->head.block_free),
235                          &p->head.block_free[dst_type]) != 1)
236             {
237                 logf (LOG_FATAL|LOG_ERRNO, "read in %s at free block %d",
238                       p->data_fname[dst_type], block_free);
239                 exit (1);
240             }
241         }
242         else
243             block_free = p->head.block_last[dst_type]++;
244         if (block_prev == -1)
245         {
246             entry.next = block_free*8 + dst_type;
247             entry.size = size;
248             p->head.total_bytes += size;
249             write_indx (p, rec->sysno, &entry, sizeof(entry));
250         }
251         else
252         {
253             memcpy (cptr, &block_free, sizeof(int));
254             bf_write (p->data_BFile[dst_type], block_prev, 0, 0, cptr);
255             cptr = p->tmp_buf + no_written;
256         }
257         block_prev = block_free;
258         no_written += p->head.block_size[dst_type] - sizeof(int);
259         p->head.block_used[dst_type]++;
260     }
261     assert (block_prev != -1);
262     block_free = 0;
263     memcpy (cptr, &block_free, sizeof(int));
264     bf_write (p->data_BFile[dst_type], block_prev, 0,
265               sizeof(int) + (p->tmp_buf+size) - cptr, cptr);
266 }
267
268 static void rec_update_single (Records p, Record rec)
269 {
270     rec_release_blocks (p, rec->sysno);
271     rec_write_single (p, rec);
272 }
273
274 Records rec_open (BFiles bfs, int rw)
275 {
276     Records p;
277     int i, r;
278
279     p = xmalloc (sizeof(*p));
280     p->rw = rw;
281     p->tmp_size = 1024;
282     p->tmp_buf = xmalloc (p->tmp_size);
283     p->index_fname = "recindex";
284     p->index_BFile = bf_open (bfs, p->index_fname, 128, rw);
285     if (p->index_BFile == NULL)
286     {
287         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
288         exit (1);
289     }
290     r = bf_read (p->index_BFile, 0, 0, 0, p->tmp_buf);
291     switch (r)
292     {
293     case 0:
294         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
295         p->head.index_free = 0;
296         p->head.index_last = 1;
297         p->head.no_records = 0;
298         p->head.total_bytes = 0;
299         for (i = 0; i<REC_BLOCK_TYPES; i++)
300         {
301             p->head.block_free[i] = 0;
302             p->head.block_last[i] = 1;
303             p->head.block_used[i] = 0;
304         }
305         p->head.block_size[0] = 128;
306         p->head.block_move[0] = 0;
307         for (i = 1; i<REC_BLOCK_TYPES; i++)
308         {
309             p->head.block_size[i] = p->head.block_size[i-1] * 4;
310             p->head.block_move[i] = p->head.block_size[i] * 3;
311         }
312         if (rw)
313             rec_write_head (p);
314         break;
315     case 1:
316         memcpy (&p->head, p->tmp_buf, sizeof(p->head));
317         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
318         {
319             logf (LOG_FATAL, "read %s. bad header", p->index_fname);
320             exit (1);
321         }
322         break;
323     }
324     for (i = 0; i<REC_BLOCK_TYPES; i++)
325     {
326         char str[80];
327         sprintf (str, "recdata%c", i + 'A');
328         p->data_fname[i] = xmalloc (strlen(str)+1);
329         strcpy (p->data_fname[i], str);
330         p->data_BFile[i] = NULL;
331     }
332     for (i = 0; i<REC_BLOCK_TYPES; i++)
333     {
334         if (!(p->data_BFile[i] = bf_open (bfs, p->data_fname[i],
335                                           p->head.block_size[i],
336                                           rw)))
337         {
338             logf (LOG_FATAL|LOG_ERRNO, "bf_open %s", p->data_fname[i]);
339             exit (1);
340         }
341     }
342     p->cache_max = 10;
343     p->cache_cur = 0;
344     p->record_cache = xmalloc (sizeof(*p->record_cache)*p->cache_max);
345     return p;
346 }
347
348 static void rec_cache_flush (Records p, int saveCount)
349 {
350     int i, j;
351
352     if (saveCount >= p->cache_cur)
353         saveCount = 0;
354     for (i = 0; i<p->cache_cur - saveCount; i++)
355     {
356         struct record_cache_entry *e = p->record_cache + i;
357         switch (e->flag)
358         {
359         case recordFlagNop:
360             break;
361         case recordFlagNew:
362             rec_write_single (p, e->rec);
363             break;
364         case recordFlagWrite:
365             rec_update_single (p, e->rec);
366             break;
367         case recordFlagDelete:
368             rec_delete_single (p, e->rec);
369             break;
370         }
371         rec_rm (&e->rec);
372     }
373     for (j = 0; j<saveCount; j++, i++)
374         memcpy (p->record_cache+j, p->record_cache+i,
375                 sizeof(*p->record_cache));
376     p->cache_cur = saveCount;
377 }
378
379 static Record *rec_cache_lookup (Records p, int sysno,
380                                  enum recordCacheFlag flag)
381 {
382     int i;
383     for (i = 0; i<p->cache_cur; i++)
384     {
385         struct record_cache_entry *e = p->record_cache + i;
386         if (e->rec->sysno == sysno)
387         {
388             if (flag != recordFlagNop && e->flag == recordFlagNop)
389                 e->flag = flag;
390             return &e->rec;
391         }
392     }
393     return NULL;
394 }
395
396 static void rec_cache_insert (Records p, Record rec, enum recordCacheFlag flag)
397 {
398     struct record_cache_entry *e;
399
400     if (p->cache_cur == p->cache_max)
401         rec_cache_flush (p, 1);
402     else if (p->cache_cur > 0)
403     {
404         int i, j;
405         int used = 0;
406         for (i = 0; i<p->cache_cur; i++)
407         {
408             Record r = (p->record_cache + i)->rec;
409             for (j = 0; j<REC_NO_INFO; j++)
410                 used += r->size[j];
411         }
412         if (used > 256000)
413             rec_cache_flush (p, 1);
414     }
415     assert (p->cache_cur < p->cache_max);
416
417     e = p->record_cache + (p->cache_cur)++;
418     e->flag = flag;
419     e->rec = rec_cp (rec);
420 }
421
422 void rec_close (Records *pp)
423 {
424     Records p = *pp;
425     int i;
426
427     assert (p);
428
429     rec_cache_flush (p, 0);
430     xfree (p->record_cache);
431
432     if (p->rw)
433         rec_write_head (p);
434
435     if (p->index_BFile)
436         bf_close (p->index_BFile);
437
438     for (i = 0; i<REC_BLOCK_TYPES; i++)
439     {
440         if (p->data_BFile[i])
441             bf_close (p->data_BFile[i]);
442         xfree (p->data_fname[i]);
443     }
444     xfree (p->tmp_buf);
445     xfree (p);
446     *pp = NULL;
447 }
448
449
450 Record rec_get (Records p, int sysno)
451 {
452     int i;
453     Record rec, *recp;
454     struct record_index_entry entry;
455     int freeblock, dst_type;
456     char *nptr, *cptr;
457
458     assert (sysno > 0);
459     assert (p);
460
461     if ((recp = rec_cache_lookup (p, sysno, recordFlagNop)))
462         return rec_cp (*recp);
463
464     if (!read_indx (p, sysno, &entry, sizeof(entry), 1))
465         return NULL;       /* record is not there! */
466
467     if (!entry.size)
468         return NULL;       /* record is deleted */
469
470     dst_type = entry.next & 7;
471     assert (dst_type < REC_BLOCK_TYPES);
472     freeblock = entry.next / 8;
473
474     assert (freeblock > 0);
475     
476     rec = xmalloc (sizeof(*rec));
477     rec_tmp_expand (p, entry.size, dst_type);
478
479     cptr = p->tmp_buf;
480     bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
481     memcpy (&freeblock, cptr, sizeof(freeblock));
482
483     while (freeblock)
484     {
485         int tmp;
486
487         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
488         
489         memcpy (&tmp, cptr, sizeof(tmp));
490         bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
491         memcpy (&freeblock, cptr, sizeof(freeblock));
492         memcpy (cptr, &tmp, sizeof(tmp));
493     }
494
495     rec->sysno = sysno;
496     nptr = p->tmp_buf + sizeof(freeblock);
497     for (i = 0; i < REC_NO_INFO; i++)
498     {
499         memcpy (&rec->size[i], nptr, sizeof(*rec->size));
500         nptr += sizeof(*rec->size);
501         if (rec->size[i])
502         {
503             rec->info[i] = xmalloc (rec->size[i]);
504             memcpy (rec->info[i], nptr, rec->size[i]);
505             nptr += rec->size[i];
506         }
507         else
508             rec->info[i] = NULL;
509     }
510     rec_cache_insert (p, rec, recordFlagNop);
511     return rec;
512 }
513
514 Record rec_new (Records p)
515 {
516     int sysno, i;
517     Record rec;
518
519     assert (p);
520     rec = xmalloc (sizeof(*rec));
521     if (1 || p->head.index_free == 0)
522         sysno = (p->head.index_last)++;
523     else
524     {
525         struct record_index_entry entry;
526
527         read_indx (p, p->head.index_free, &entry, sizeof(entry), 0);
528         sysno = p->head.index_free;
529         p->head.index_free = entry.next;
530     }
531     (p->head.no_records)++;
532     rec->sysno = sysno;
533     for (i = 0; i < REC_NO_INFO; i++)
534     {
535         rec->info[i] = NULL;
536         rec->size[i] = 0;
537     }
538     rec_cache_insert (p, rec, recordFlagNew);
539     return rec;
540 }
541
542 void rec_del (Records p, Record *recpp)
543 {
544     Record *recp;
545
546     (p->head.no_records)--;
547     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagDelete)))
548     {
549         rec_rm (recp);
550         *recp = *recpp;
551     }
552     else
553     {
554         rec_cache_insert (p, *recpp, recordFlagDelete);
555         rec_rm (recpp);
556     }
557     *recpp = NULL;
558 }
559
560 void rec_put (Records p, Record *recpp)
561 {
562     Record *recp;
563
564     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagWrite)))
565     {
566         rec_rm (recp);
567         *recp = *recpp;
568     }
569     else
570     {
571         rec_cache_insert (p, *recpp, recordFlagWrite);
572         rec_rm (recpp);
573     }
574     *recpp = NULL;
575 }
576
577 void rec_rm (Record *recpp)
578 {
579     int i;
580
581     if (!*recpp)
582         return ;
583     for (i = 0; i < REC_NO_INFO; i++)
584         xfree ((*recpp)->info[i]);
585     xfree (*recpp);
586     *recpp = NULL;
587 }
588
589 Record rec_cp (Record rec)
590 {
591     Record n;
592     int i;
593
594     n = xmalloc (sizeof(*n));
595     n->sysno = rec->sysno;
596     for (i = 0; i < REC_NO_INFO; i++)
597         if (!rec->info[i])
598         {
599             n->info[i] = NULL;
600             n->size[i] = 0;
601         }
602         else
603         {
604             n->size[i] = rec->size[i];
605             n->info[i] = xmalloc (rec->size[i]);
606             memcpy (n->info[i], rec->info[i], rec->size[i]);
607         }
608     return n;
609 }
610
611
612 char *rec_strdup (const char *s, size_t *len)
613 {
614     char *p;
615
616     if (!s)
617     {
618         *len = 0;
619         return NULL;
620     }
621     *len = strlen(s)+1;
622     p = xmalloc (*len);
623     strcpy (p, s);
624     return p;
625 }
626