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