3 * Keep a cache of name/id mappings
5 * Copyright (C) 1989-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
11 #include "mr_server.h"
17 EXEC SQL INCLUDE sqlca;
21 EXEC SQL WHENEVER SQLERROR DO dbmserr();
25 /* Cache parameters: */
26 #define CACHESIZE 101 /* number of cache slots */
29 char name[MAX_FIELD_WIDTH];
35 struct item *trans; /* keep track of transactions */
38 static struct item cachehead;
41 /* statistics counters */
42 int cachehits = 0, cachemisses = 0;
44 int hashname(char *name, enum tables type);
46 /* Name hash function. */
48 int hashname(char *name, enum tables type)
53 val = (val << 5) - val + *name++ - '`';
58 /* Initialize the cache, flushing any old data, and report the statistics
59 * if the cache was previously in use.
62 void flush_cache(void)
66 if (cachehits + cachemisses != 0)
68 com_err(whoami, 0, "Flushing cache; %d hits, %d misses, %d%% hit rate",
69 cachehits, cachemisses,
70 (100 * cachehits) / (cachehits + cachemisses));
73 cachehead.next = cachehead.prev = &cachehead;
74 cachehits = cachemisses = cachesize = 0;
75 for (i = cachehead.next; i != &cachehead; i = i->next)
77 if (i->prev != &cachehead)
80 if (cachehead.prev != &cachehead)
82 cachehead.next = cachehead.prev = &cachehead;
83 cachehead.trans = NULL;
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.
91 int name_to_id(char *name, enum tables type, int *id)
94 EXEC SQL BEGIN DECLARE SECTION;
97 EXEC SQL END DECLARE SECTION;
99 h = hashname(name, type);
100 for (i = cachehead.next; i != &cachehead; i = i->next)
102 if (i->nhash != h || strcmp(name, i->name) || type != i->type)
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;
121 if (strchr(iname, '@') || (strlen(iname) > 8))
123 sqlca.sqlcode = SQL_NO_MATCH;
126 EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname;
129 EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname;
132 EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname);
135 EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname);
138 EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname;
141 EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname;
144 if (!iname[0]) /* special-case empty string */
149 EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname;
154 if (sqlca.sqlcode == SQL_NO_MATCH)
156 if (sqlca.sqlerrd[2] > 1)
157 return MR_NOT_UNIQUE;
161 if (name[0] == '#' && type != USERS_TABLE)
163 if (cachesize < CACHESIZE)
165 i = malloc(sizeof(struct item));
173 cachehead.prev = i->prev;
174 i->prev->next = &cachehead;
176 strcpy(i->name, name);
180 i->next = cachehead.next;
181 i->prev = &cachehead;
183 cachehead.next->prev = i;
185 /* find the end of the transaction chain & add this item */
186 for (t = &cachehead; t->trans && t != i; t = t->trans)
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.
199 int id_to_name(int id, enum tables type, char **name)
202 EXEC SQL BEGIN DECLARE SECTION;
203 char iname[MAX_FIELD_WIDTH];
205 EXEC SQL END DECLARE SECTION;
207 for (i = cachehead.next; i != &cachehead; i = i->next)
209 if (i->id != id || type != i->type)
212 *name = xstrdup(i->name);
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;
229 EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j;
232 EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j;
235 EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j;
238 EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j;
241 EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j;
244 EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j;
247 EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j;
252 if (sqlca.sqlcode == SQL_NO_MATCH)
255 sprintf(iname, "#%d", j);
256 *name = xstrdup(iname);
259 if (sqlca.sqlerrd[2] > 1)
264 *name = xstrdup(strtrim(iname));
265 if (**name == '#' && type != USERS_TABLE)
267 if (cachesize < CACHESIZE)
269 i = malloc(sizeof(struct item));
277 cachehead.prev = i->prev;
278 i->prev->next = &cachehead;
280 strcpy(i->name, *name);
282 i->nhash = hashname(*name, type);
284 i->next = cachehead.next;
285 i->prev = &cachehead;
287 cachehead.next->prev = i;
289 /* find the end of the transaction chain & add this item */
290 for (t = &cachehead; t->trans && t != i; t = t->trans)
298 /* Explicitly add something to the cache without doing a lookup in the
302 int cache_entry(char *name, enum tables type, int id)
306 for (i = cachehead.next; i != &cachehead; i = i->next)
308 if (i->id == id && i->type == type)
310 if (strcmp(i->name, name))
312 strcpy(i->name, name);
313 i->nhash = hashname(name, type);
318 if (cachesize < CACHESIZE)
320 i = malloc(sizeof(struct item));
328 cachehead.prev = i->prev;
329 i->prev->next = &cachehead;
331 strcpy(i->name, name);
333 i->nhash = hashname(name, type);
335 i->next = cachehead.next;
336 i->prev = &cachehead;
338 cachehead.next->prev = i;
340 /* find the end of the transaction chain & add this item */
341 for (t = &cachehead; t->trans && t != i; t = t->trans)
349 /* Flush something that may or may not already be in the cache. */
351 void flush_name(char *name, enum tables type)
355 /* first clear it out of the transaction chain */
356 for (i = &cachehead; i && i->trans; i = i->trans)
358 if (!strcmp(name, i->trans->name) && type == i->trans->type)
359 i->trans = i->trans->trans;
363 for (i = cachehead.next; i != &cachehead; i = i->next)
365 if (!strcmp(name, i->name) && type == i->type)
368 i->next->prev = i->prev;
369 i->prev->next = i->next;
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.
381 void cache_commit(void)
383 cachehead.trans = NULL;
387 /* Called whan a transaction is aborted to throw away any cache changes
388 * from that transaction.
391 void cache_abort(void)
395 for (i = cachehead.trans; i; i = t)
398 flush_name(i->name, i->type);
400 cachehead.trans = NULL;