]>
Commit | Line | Data |
---|---|---|
7ac48069 | 1 | /* $Id$ |
7de02494 | 2 | * |
7ac48069 | 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>. | |
7de02494 | 8 | */ |
9 | ||
7de02494 | 10 | #include <mit-copyright.h> |
7de02494 | 11 | #include "mr_server.h" |
03c05291 | 12 | #include "query.h" |
7ac48069 | 13 | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | ||
7de02494 | 17 | EXEC SQL INCLUDE sqlca; |
18 | ||
7ac48069 | 19 | RCSID("$Header$"); |
20 | ||
03c05291 | 21 | EXEC SQL WHENEVER SQLERROR DO dbmserr(); |
7de02494 | 22 | |
03c05291 | 23 | extern char *whoami; |
7de02494 | 24 | |
25 | /* Cache parameters: */ | |
26 | #define CACHESIZE 101 /* number of cache slots */ | |
7de02494 | 27 | |
28 | struct item { | |
e688520a | 29 | char name[MAX_FIELD_WIDTH]; |
5eaef520 | 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 */ | |
7de02494 | 36 | }; |
37 | ||
38 | static struct item cachehead; | |
39 | static int cachesize; | |
40 | ||
41 | /* statistics counters */ | |
42 | int cachehits = 0, cachemisses = 0; | |
43 | ||
7ac48069 | 44 | int hashname(char *name, enum tables type); |
7de02494 | 45 | |
46 | /* Name hash function. */ | |
47 | ||
5eaef520 | 48 | int hashname(char *name, enum tables type) |
7de02494 | 49 | { |
44d12d58 | 50 | int val = type; |
7de02494 | 51 | |
5eaef520 | 52 | while (*name) |
53 | val = (val << 5) - val + *name++ - '`'; | |
54 | return val; | |
7de02494 | 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 | { |
44d12d58 | 64 | struct item *i; |
7de02494 | 65 | |
5eaef520 | 66 | if (cachehits + cachemisses != 0) |
67 | { | |
7de02494 | 68 | com_err(whoami, 0, "Flushing cache; %d hits, %d misses, %d%% hit rate", |
69 | cachehits, cachemisses, | |
70 | (100 * cachehits) / (cachehits + cachemisses)); | |
7de02494 | 71 | } |
5eaef520 | 72 | else |
7de02494 | 73 | cachehead.next = cachehead.prev = &cachehead; |
5eaef520 | 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; | |
7de02494 | 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 | ||
5eaef520 | 91 | int name_to_id(char *name, enum tables type, int *id) |
7de02494 | 92 | { |
44d12d58 | 93 | struct item *i, *t; |
5eaef520 | 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; | |
7de02494 | 113 | } |
114 | ||
5eaef520 | 115 | cachemisses++; |
116 | iname = name; | |
7de02494 | 117 | |
5eaef520 | 118 | switch (type) |
119 | { | |
120 | case USERS_TABLE: | |
121 | if (strchr(iname, '@') || (strlen(iname) > 8)) | |
122 | { | |
03c05291 | 123 | sqlca.sqlcode = SQL_NO_MATCH; |
124 | break; | |
06c1c8ff | 125 | } |
5eaef520 | 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; | |
e688520a | 137 | case CLUSTERS_TABLE: |
5eaef520 | 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; | |
03c05291 | 147 | return MR_SUCCESS; |
148 | } | |
5eaef520 | 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)); | |
e688520a | 166 | if (!i) |
167 | return MR_SUCCESS; | |
5eaef520 | 168 | cachesize++; |
7de02494 | 169 | } |
5eaef520 | 170 | else |
171 | { | |
172 | i = cachehead.prev; | |
173 | cachehead.prev = i->prev; | |
174 | i->prev->next = &cachehead; | |
7de02494 | 175 | } |
5eaef520 | 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; | |
7de02494 | 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 | ||
5eaef520 | 199 | int id_to_name(int id, enum tables type, char **name) |
7de02494 | 200 | { |
44d12d58 | 201 | struct item *i, *t; |
5eaef520 | 202 | EXEC SQL BEGIN DECLARE SECTION; |
e688520a | 203 | char iname[MAX_FIELD_WIDTH]; |
5eaef520 | 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); | |
e688520a | 212 | *name = xstrdup(i->name); |
5eaef520 | 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; | |
7de02494 | 221 | } |
222 | ||
5eaef520 | 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; | |
e688520a | 240 | case CLUSTERS_TABLE: |
5eaef520 | 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; | |
7de02494 | 249 | default: |
5eaef520 | 250 | return MR_INTERNAL; |
7de02494 | 251 | } |
5eaef520 | 252 | if (sqlca.sqlcode == SQL_NO_MATCH) |
253 | { | |
254 | free(*name); | |
255 | sprintf(iname, "#%d", j); | |
e688520a | 256 | *name = xstrdup(iname); |
5eaef520 | 257 | return MR_NO_MATCH; |
7de02494 | 258 | } |
5eaef520 | 259 | if (sqlca.sqlerrd[2] > 1) |
260 | return MR_INTERNAL; | |
261 | if (sqlca.sqlcode) | |
262 | return MR_DBMS_ERR; | |
263 | free(*name); | |
e688520a | 264 | *name = xstrdup(strtrim(iname)); |
5eaef520 | 265 | if (**name == '#' && type != USERS_TABLE) |
266 | return MR_SUCCESS; | |
267 | if (cachesize < CACHESIZE) | |
268 | { | |
269 | i = malloc(sizeof(struct item)); | |
e688520a | 270 | if (!i) |
271 | return MR_SUCCESS; | |
5eaef520 | 272 | cachesize++; |
7de02494 | 273 | } |
5eaef520 | 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; | |
7de02494 | 295 | } |
296 | ||
297 | ||
298 | /* Explicitly add something to the cache without doing a lookup in the | |
299 | * database. | |
300 | */ | |
301 | ||
5eaef520 | 302 | int cache_entry(char *name, enum tables type, int id) |
7de02494 | 303 | { |
44d12d58 | 304 | struct item *i, *t; |
5eaef520 | 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 | { | |
a49e54ea | 312 | strcpy(i->name, name); |
313 | i->nhash = hashname(name, type); | |
5eaef520 | 314 | } |
315 | return MR_SUCCESS; | |
316 | } | |
317 | } | |
318 | if (cachesize < CACHESIZE) | |
319 | { | |
320 | i = malloc(sizeof(struct item)); | |
e688520a | 321 | if (!i) |
322 | return MR_SUCCESS; | |
5eaef520 | 323 | cachesize++; |
324 | } | |
325 | else | |
326 | { | |
327 | i = cachehead.prev; | |
328 | cachehead.prev = i->prev; | |
329 | i->prev->next = &cachehead; | |
7de02494 | 330 | } |
5eaef520 | 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; | |
7de02494 | 346 | } |
347 | ||
348 | ||
349 | /* Flush something that may or may not already be in the cache. */ | |
350 | ||
5eaef520 | 351 | void flush_name(char *name, enum tables type) |
7de02494 | 352 | { |
44d12d58 | 353 | struct item *i; |
7de02494 | 354 | |
5eaef520 | 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; | |
7f5495cf | 360 | } |
7de02494 | 361 | |
5eaef520 | 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); | |
85330553 | 371 | break; |
7de02494 | 372 | } |
373 | } | |
374 | } | |
a49e54ea | 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 | ||
03c05291 | 381 | void cache_commit(void) |
a49e54ea | 382 | { |
5eaef520 | 383 | cachehead.trans = NULL; |
a49e54ea | 384 | } |
385 | ||
386 | ||
387 | /* Called whan a transaction is aborted to throw away any cache changes | |
388 | * from that transaction. | |
389 | */ | |
390 | ||
03c05291 | 391 | void cache_abort(void) |
a49e54ea | 392 | { |
44d12d58 | 393 | struct item *i, *t; |
a49e54ea | 394 | |
5eaef520 | 395 | for (i = cachehead.trans; i; i = t) |
396 | { | |
397 | t = i->trans; | |
398 | flush_name(i->name, i->type); | |
a49e54ea | 399 | } |
5eaef520 | 400 | cachehead.trans = NULL; |
a49e54ea | 401 | } |