]> andersk Git - moira.git/blob - server/cache.pc
68f5ffe8786479d0f0be187b8163e89fa082e024
[moira.git] / server / cache.pc
1 /* $Id$
2  *
3  * Keep a cache of name/id mappings
4  *
5  * Copyright (C) 1989-1998 by the Massachusetts Institute of Technology
6  * For copying and distribution information, please see the file
7  * <mit-copyright.h>.
8  */
9
10 #include <mit-copyright.h>
11 #include "mr_server.h"
12 #include "query.h"
13
14 #include <stdlib.h>
15 #include <string.h>
16
17 EXEC SQL INCLUDE sqlca;
18
19 RCSID("$Header$");
20
21 EXEC SQL WHENEVER SQLERROR DO dbmserr();
22
23 extern char *whoami;
24
25 /* Cache parameters: */
26 #define CACHESIZE 101           /* number of cache slots */
27
28 struct item {
29   char name[MAX_FIELD_WIDTH];
30   enum tables type;
31   int nhash;
32   int id;
33   struct item *next;
34   struct item *prev;
35   struct item *trans;           /* keep track of transactions */
36 };
37
38 static struct item cachehead;
39 static int cachesize;
40
41 /* statistics counters */
42 int cachehits = 0, cachemisses = 0;
43
44 int hashname(char *name, enum tables type);
45
46 /* Name hash function. */
47
48 int hashname(char *name, enum tables type)
49 {
50   int val = type;
51
52   while (*name)
53     val = (val << 5) - val + *name++ - '`';
54   return val;
55 }
56
57
58 /* Initialize the cache, flushing any old data, and report the statistics
59  * if the cache was previously in use.
60  */
61
62 void flush_cache(void)
63 {
64   struct item *i;
65
66   if (cachehits + cachemisses != 0)
67     {
68       com_err(whoami, 0, "Flushing cache; %d hits, %d misses, %d%% hit rate",
69               cachehits, cachemisses,
70               (100 * cachehits) / (cachehits + cachemisses));
71     }
72   else
73     cachehead.next = cachehead.prev = &cachehead;
74   cachehits = cachemisses = cachesize = 0;
75   for (i = cachehead.next; i != &cachehead; i = i->next)
76     {
77       if (i->prev != &cachehead)
78         free(i->prev);
79     }
80   if (cachehead.prev != &cachehead)
81     free(cachehead.prev);
82   cachehead.next = cachehead.prev = &cachehead;
83   cachehead.trans = NULL;
84 }
85
86
87 /* Do a name to ID translation.  id will be updated with the answer if
88  * it is available, and as a side effect the cache is updated.
89  */
90
91 int name_to_id(char *name, enum tables type, int *id)
92 {
93   struct item *i, *t;
94   EXEC SQL BEGIN DECLARE SECTION;
95   char *iname;
96   int j, h;
97   EXEC SQL END DECLARE SECTION;
98
99   h = hashname(name, type);
100   for (i = cachehead.next; i != &cachehead; i = i->next)
101     {
102       if (i->nhash != h || strcmp(name, i->name) || type != i->type)
103         continue;
104       *id = i->id;
105       cachehits++;
106       i->next->prev = i->prev;
107       i->prev->next = i->next;
108       i->next = cachehead.next;
109       i->prev = &cachehead;
110       cachehead.next->prev = i;
111       cachehead.next = i;
112       return MR_SUCCESS;
113     }
114
115   cachemisses++;
116   iname = name;
117
118   switch (type)
119     {
120     case USERS_TABLE:
121       if (strchr(iname, '@') || (strlen(iname) > 8))
122         {
123           sqlca.sqlcode = SQL_NO_MATCH;
124           break;
125         }
126       EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname;
127       break;
128     case LIST_TABLE:
129       EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname;
130       break;
131     case MACHINE_TABLE:
132       EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname);
133       break;
134     case SUBNET_TABLE:
135       EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname);
136       break;
137     case CLUSTERS_TABLE:
138       EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname;
139       break;
140     case FILESYS_TABLE:
141       EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname;
142       break;
143     case STRINGS_TABLE:
144       if (!iname[0])    /* special-case empty string */
145         {
146           *id = 0;
147           return MR_SUCCESS;
148         }
149       EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname;
150       break;
151     default:
152       return MR_INTERNAL;
153     }
154   if (sqlca.sqlcode == SQL_NO_MATCH)
155     return MR_NO_MATCH;
156   if (sqlca.sqlerrd[2] > 1)
157     return MR_NOT_UNIQUE;
158   if (sqlca.sqlcode)
159     return MR_DBMS_ERR;
160   *id = j;
161   if (name[0] == '#' && type != USERS_TABLE)
162     return MR_SUCCESS;
163   if (cachesize < CACHESIZE)
164     {
165       i = malloc(sizeof(struct item));
166       if (!i)
167         return MR_SUCCESS;
168       cachesize++;
169     }
170   else
171     {
172       i = cachehead.prev;
173       cachehead.prev = i->prev;
174       i->prev->next = &cachehead;
175     }
176   strcpy(i->name, name);
177   i->type = type;
178   i->nhash = h;
179   i->id = j;
180   i->next = cachehead.next;
181   i->prev = &cachehead;
182   i->trans = NULL;
183   cachehead.next->prev = i;
184   cachehead.next = i;
185   /* find the end of the transaction chain & add this item */
186   for (t = &cachehead; t->trans && t != i; t = t->trans)
187     ;
188   if (t != i)
189     t->trans = i;
190   return MR_SUCCESS;
191 }
192
193
194 /* Perform an ID to name mapping.  name should be a pointer to a pointer to
195  * malloc'ed data.  The buffer it refers to will be freed, and a new buffer
196  * allocated with the answer.
197  */
198
199 int id_to_name(int id, enum tables type, char **name)
200 {
201   struct item *i, *t;
202   EXEC SQL BEGIN DECLARE SECTION;
203   char iname[MAX_FIELD_WIDTH];
204   int j;
205   EXEC SQL END DECLARE SECTION;
206
207   for (i = cachehead.next; i != &cachehead; i = i->next)
208     {
209       if (i->id != id || type != i->type)
210         continue;
211       free(*name);
212       *name = xstrdup(i->name);
213       cachehits++;
214       i->next->prev = i->prev;
215       i->prev->next = i->next;
216       i->next = cachehead.next;
217       i->prev = &cachehead;
218       cachehead.next->prev = i;
219       cachehead.next = i;
220       return MR_SUCCESS;
221     }
222
223   cachemisses++;
224   j = id;
225
226   switch (type)
227     {
228     case USERS_TABLE:
229       EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j;
230       break;
231     case LIST_TABLE:
232       EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j;
233       break;
234     case MACHINE_TABLE:
235       EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j;
236       break;
237     case SUBNET_TABLE:
238       EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j;
239       break;
240     case CLUSTERS_TABLE:
241       EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j;
242       break;
243     case FILESYS_TABLE:
244       EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j;
245       break;
246     case STRINGS_TABLE:
247       EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j;
248       break;
249     default:
250       return MR_INTERNAL;
251     }
252   if (sqlca.sqlcode == SQL_NO_MATCH)
253     {
254       free(*name);
255       sprintf(iname, "#%d", j);
256       *name = xstrdup(iname);
257       return MR_NO_MATCH;
258     }
259   if (sqlca.sqlerrd[2] > 1)
260     return MR_INTERNAL;
261   if (sqlca.sqlcode)
262     return MR_DBMS_ERR;
263   free(*name);
264   *name = xstrdup(strtrim(iname));
265   if (**name == '#' && type != USERS_TABLE)
266     return MR_SUCCESS;
267   if (cachesize < CACHESIZE)
268     {
269       i = malloc(sizeof(struct item));
270       if (!i)
271         return MR_SUCCESS;
272       cachesize++;
273     }
274   else
275     {
276       i = cachehead.prev;
277       cachehead.prev = i->prev;
278       i->prev->next = &cachehead;
279     }
280   strcpy(i->name, *name);
281   i->type = type;
282   i->nhash = hashname(*name, type);
283   i->id = id;
284   i->next = cachehead.next;
285   i->prev = &cachehead;
286   i->trans = NULL;
287   cachehead.next->prev = i;
288   cachehead.next = i;
289   /* find the end of the transaction chain & add this item */
290   for (t = &cachehead; t->trans && t != i; t = t->trans)
291     ;
292   if (t != i)
293     t->trans = i;
294   return MR_SUCCESS;
295 }
296
297
298 /* Explicitly add something to the cache without doing a lookup in the
299  * database.
300  */
301
302 int cache_entry(char *name, enum tables type, int id)
303 {
304   struct item *i, *t;
305
306   for (i = cachehead.next; i != &cachehead; i = i->next)
307     {
308       if (i->id == id && i->type == type)
309         {
310           if (strcmp(i->name, name))
311             {
312               strcpy(i->name, name);
313               i->nhash = hashname(name, type);
314             }
315           return MR_SUCCESS;
316         }
317     }
318   if (cachesize < CACHESIZE)
319     {
320       i = malloc(sizeof(struct item));
321       if (!i)
322         return MR_SUCCESS;
323       cachesize++;
324     }
325   else
326     {
327       i = cachehead.prev;
328       cachehead.prev = i->prev;
329       i->prev->next = &cachehead;
330     }
331   strcpy(i->name, name);
332   i->type = type;
333   i->nhash = hashname(name, type);
334   i->id = id;
335   i->next = cachehead.next;
336   i->prev = &cachehead;
337   i->trans = NULL;
338   cachehead.next->prev = i;
339   cachehead.next = i;
340   /* find the end of the transaction chain & add this item */
341   for (t = &cachehead; t->trans && t != i; t = t->trans)
342     ;
343   if (t != i)
344     t->trans = i;
345   return MR_SUCCESS;
346 }
347
348
349 /* Flush something that may or may not already be in the cache. */
350
351 void flush_name(char *name, enum tables type)
352 {
353   struct item *i;
354
355   /* first clear it out of the transaction chain */
356   for (i = &cachehead; i && i->trans; i = i->trans)
357     {
358       if (!strcmp(name, i->trans->name) && type == i->trans->type)
359         i->trans = i->trans->trans;
360     }
361
362   /* now remove it */
363   for (i = cachehead.next; i != &cachehead; i = i->next)
364     {
365       if (!strcmp(name, i->name) && type == i->type)
366         {
367           cachesize--;
368           i->next->prev = i->prev;
369           i->prev->next = i->next;
370           free(i);
371           break;
372         }
373     }
374 }
375
376
377 /* Called when a transaction is committed to also commit any cache changes.
378  * Just throws away the list of cache changes for this transaction.
379  */
380
381 void cache_commit(void)
382 {
383   cachehead.trans = NULL;
384 }
385
386
387 /* Called whan a transaction is aborted to throw away any cache changes
388  * from that transaction.
389  */
390
391 void cache_abort(void)
392 {
393   struct item *i, *t;
394
395   for (i = cachehead.trans; i; i = t)
396     {
397       t = i->trans;
398       flush_name(i->name, i->type);
399     }
400   cachehead.trans = NULL;
401 }
This page took 0.107284 seconds and 3 git commands to generate.