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