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