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