Trivial
[idzebra-moved-to-github.git] / isam / isam.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: isam.c,v $
7  * Revision 1.4  1994-09-26 17:11:29  quinn
8  * Trivial
9  *
10  * Revision 1.3  1994/09/26  17:06:35  quinn
11  * Back again...
12  *
13  * Revision 1.1  1994/09/12  08:02:13  quinn
14  * Not functional yet
15  *
16  */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21
22 #include <util.h>
23 #include <bfile.h>
24 #include <isam.h>
25 #include <common.h>
26 #include "isutil.h"
27 #include "rootblk.h"
28 #include "memory.h"
29 #include "physical.h"
30 #include "keyops.h"
31
32 static int splitargs(const char *s, char *bf[], int max)
33 {
34     int ct = 0;
35     for (;;)
36     {
37         while (*s && isspace(*s))
38             s++;
39         bf[ct] = (char *) s;
40         if (!*s)
41                 return ct;
42         ct++;
43         if (ct > max)
44         {
45             log(LOG_WARN, "Ignoring extra args to is resource");
46             bf[ct] = '\0';
47             return(ct - 1);
48         }
49         while (*s && !isspace(*s))
50             s++;
51     }
52 }
53
54 /*
55  * Open isam file.
56  * Process resources.
57  */
58 ISAM is_open(const char *name, int writeflag)
59 {
60     ISAM new;
61     char *nm, *r, *pp[IS_MAX_BLOCKTYPES+1], m[2];
62     int num, size, rs, tmp, i;
63     is_type_header th;
64
65     log(LOG_DEBUG, "is_open(%s, %s)", name, writeflag ? "RW" : "RDONLY");
66     new = xmalloc(sizeof(*new));
67     new->writeflag = writeflag;
68     for (i = 0; i < IS_MAX_BLOCKTYPES; i++)
69         new->types[i].index = 0;                        /* dummy */
70
71     /* determine number and size of blocktypes */
72     if (!(r = res_get(common_resource, nm = strconcat(name, ".",
73         "blocktypes", 0))) || !(num = splitargs(r, pp, IS_MAX_BLOCKTYPES)))
74     {
75         log(LOG_FATAL, "Failed to locate resource %s", nm);
76         return 0;
77     }
78     new->num_types = num;
79     for (i = 0; i < num; i++)
80     {
81         if ((rs = sscanf(pp[i], "%d%1[bBkKmM]", &size, m)) < 1)
82         {
83             log(LOG_FATAL, "Error in resource %s: %s", r, pp[i]);
84             return 0;
85         }
86         if (rs == 1)
87                 *m = 'b';
88         switch (*m)
89         {
90                 case 'b': case 'B':
91                     new->types[i].blocksize = size; break;
92                 case 'k': case 'K':
93                     new->types[i].blocksize = size * 1024; break;
94                 case 'm': case 'M':
95                     new->types[i].blocksize = size * 1048576; break;
96                 default:
97                     log(LOG_FATAL, "Illegal size suffix: %c", *m);
98                     return 0;
99         }
100         new->types[i].dbuf = xmalloc(new->types[i].blocksize);
101         m[0] = 'A' + i;
102         m[1] = '\0';
103         if (!(new->types[i].bf = bf_open(strconcat(name, m, 0), 
104             new->types[i].blocksize, writeflag)))
105         {
106             log(LOG_FATAL, "bf_open failed");
107             return 0;
108         }
109         if ((rs = is_rb_read(&new->types[i], &th)) > 0)
110         {
111             if (th.blocksize != new->types[i].blocksize)
112             {
113                 log(LOG_FATAL, "File blocksize mismatch in %s", name);
114                 exit(1);
115             }
116             new->types[i].freelist = th.freelist;
117             new->types[i].top = th.top;
118         }
119         else if (writeflag) /* write dummy superblock to determine top */
120         {
121             if ((rs = is_rb_write(&new->types[i], &th)) <=0)  /* dummy */
122             {
123                 log(LOG_FATAL, "Failed to write initial superblock.");
124                 exit(1);
125             }
126             new->types[i].freelist = -1;
127             new->types[i].top = rs;
128         }
129         /* ELSE: this is an empty file opened in read-only mode. */
130     }
131     if (!(r = res_get_def(common_resource, nm = strconcat(name, ".", "keysize",
132         0), "4")))
133     {
134         log(LOG_FATAL, "Failed to locate resource %s", nm);
135         return 0;
136     }
137     if ((new->keysize = atoi(r)) <= 0)
138     {
139         log(LOG_FATAL, "Must specify positive keysize.");
140         return 0;
141     }
142
143     /* determine repack percent */
144     if (!(r = res_get_def(common_resource, nm = strconcat(name, ".", "repack",
145         0), IS_DEF_REPACK_PERCENT)))
146     {
147         log(LOG_FATAL, "Failed to locate resource %s", nm);
148         return 0;
149     }
150     new->repack = atoi(r);
151
152     /* determine max keys/blocksize */
153     if (!(r = res_get(common_resource, nm = strconcat(name, ".",
154         "maxkeys", 0))) || !(num = splitargs(r, pp, IS_MAX_BLOCKTYPES)))
155     {
156         log(LOG_FATAL, "Failed to locate resource %s", nm);
157         return 0;
158     }
159     if (num < new->num_types -1)
160     {
161         log(LOG_FATAL, "Not enough elements in %s", nm);
162         return 0;
163     }
164     for (i = 0; i < num; i++)
165     {
166         if ((rs = sscanf(pp[i], "%d", &tmp)) < 1)
167         {
168             log(LOG_FATAL, "Error in resource %s: %s", r, pp[i]);
169             return 0;
170         }
171         new->types[i].max_keys = tmp;
172     }
173
174     /* determine max keys/block */
175     for (i = 0; i < new->num_types; i++)
176     {
177         if (!new->types[i].index)
178         {
179             new->types[i].max_keys_block = (new->types[i].blocksize - 2 *
180                 sizeof(int)) / new->keysize;
181             new->types[i].max_keys_block0 = (new->types[i].blocksize - 3 *
182                 sizeof(int)) / new->keysize;
183         }
184         else
185             new->types[i].max_keys_block = new->types[i].max_keys_block0 /
186                 new->keysize;
187         if (new->types[i].max_keys_block0 < 1)
188         {
189             log(LOG_FATAL, "Blocksize too small in %s", name);
190             exit(1);
191         }
192     }
193
194     /* determine nice fill rates */
195     if (!(r = res_get(common_resource, nm = strconcat(name, ".",
196         "nicefill", 0))) || !(num = splitargs(r, pp, IS_MAX_BLOCKTYPES)))
197     {
198         log(LOG_FATAL, "Failed to locate resource %s", nm);
199         return 0;
200     }
201     if (num < new->num_types)
202     {
203         log(LOG_FATAL, "Not enough elements in %s", nm);
204         return 0;
205     }
206     for (i = 0; i < num; i++)
207     {
208         if ((rs = sscanf(pp[i], "%d", &tmp)) < 1)
209         {
210             log(LOG_FATAL, "Error in resource %s: %s", r, pp[i]);
211             return 0;
212         }
213         new->types[i].nice_keys_block = (new->types[i].max_keys_block0 * tmp) /
214             100;
215         if (new->types[i].nice_keys_block < 1)
216                 new->types[i].nice_keys_block = 1;
217     }
218
219     new->cmp = is_default_cmp;
220     return new;
221 }
222
223 /*
224  * Close isam file.
225  */
226 int is_close(ISAM is)
227 {
228     int i;
229     is_type_header th;
230
231     log(LOG_DEBUG, "is_close()");
232     for (i = 0; i < is->num_types; i++)
233     {
234         if (is->types[i].bf)
235         {
236             if (is->writeflag)
237             {
238                 th.blocksize = is->types[i].blocksize;
239                 th.keysize = is->keysize;
240                 th.freelist = is->types[i].freelist;
241                 th.top = is->types[i].top;
242                 if (is_rb_write(&is->types[i], &th) < 0)
243                 {
244                     log(LOG_FATAL, "Failed to write headerblock");
245                     exit(1);
246                 }
247             }
248             bf_close(is->types[i].bf);
249         }
250     }
251     xfree(is);
252     return 0;
253 }
254
255 static ISAM_P is_address(int type, int pos)
256 {
257     ISAM_P r;
258
259     r = pos << 2;
260     r |= type;
261     return r;
262 }
263
264 ISAM_P is_merge(ISAM is, ISAM_P pos, int num, const char *data)
265 {
266     is_mtable tab;
267     int res;
268     char keybuf[IS_MAX_RECORD];
269     int oldnum, oldtype;
270     char operation, *record;
271
272     is_m_establish_tab(is, &tab, pos);
273     /* TODO: do something to aquire oldnum at this point */
274     if (pos)
275         if (is_m_read_full(&tab, tab.data) < 0)
276         {
277             log(LOG_FATAL, "read_full failed");
278             exit(1);
279         }
280     oldnum = tab.num_records;
281     oldtype = tab.pos_type;
282     while (num)
283     {
284         operation = *(data)++;
285         record = (char*)data;
286         data += is_keysize(is);
287         num--;
288         while (num && !memcmp(record, data, is_keysize(tab.is) + 1))
289         {
290             data += 1 + is_keysize(is);
291             num--;
292         }
293         if ((res = is_m_seek_record(&tab, record)) > 0)  /* no match */
294         {
295             if (operation == KEYOP_INSERT)
296             {
297                 log(LOG_DEBUG, "XXInserting new record.");
298                 is_m_write_record(&tab, record);
299             }
300             else
301                 log(LOG_DEBUG, "XXDeletion failed to find match.");
302         }
303         else /* match found */
304         {
305             if (operation == KEYOP_INSERT)
306             {
307                 log(LOG_DEBUG, "XXSkipping insertion - match found.");
308                 continue;
309             }
310             else if (operation == KEYOP_DELETE)
311             {
312                 /* try to avoid needlessly moving data */
313                 if (num && *(data) == KEYOP_INSERT)
314                 {
315                     /* next key is identical insert? - NOOP - skip it */
316                     if (!memcmp(record, data + 1, is_keysize(is)))
317                     {
318                         log(LOG_DEBUG, "XXNoop delete. skipping.");
319                         data += 1 + is_keysize(is);
320                         num--;
321                         continue;
322                     }
323                     /* else check if next key can fit in this position */
324                     is_m_peek_record(&tab, keybuf);
325                     res = (*is->cmp)(data + 1, keybuf);
326                     if (res < 0)
327                     {
328                         log(LOG_DEBUG, "XXReplacing record.");
329                         is_m_replace_record(&tab, data + 1);
330                         data += 1 + is_keysize(is);
331                         num--;
332                         continue;
333                     }
334                 }
335                 log(LOG_DEBUG, "Deleting record.");
336                 is_m_delete_record(&tab);
337             }
338         }
339     }
340     while (tab.pos_type < tab.is->num_types - 1 && tab.num_records >
341         tab.is->types[tab.pos_type].max_keys)
342             tab.pos_type++;
343     if (!oldnum || tab.pos_type != oldtype || (abs(oldnum - tab.num_records) *
344         100) / oldnum > tab.is->repack)
345         is_p_remap(&tab);
346     else
347         is_p_align(&tab);
348     is_p_sync(&tab);
349     return is_address(tab.pos_type, tab.data->diskpos);
350 }
351
352 /*
353  * Locate a table of keys in an isam file. The ISPT is an individual
354  * position marker for that table.
355  */
356 ISPT is_position(ISAM is, ISAM_P pos);
357
358 /*
359  * Release ISPT.
360  */
361 void is_pt_free(ISPT ip);