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