]>
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> | |
14 | #include "query.h" | |
15 | #include "mr_server.h" | |
16 | EXEC SQL INCLUDE sqlca; | |
17 | ||
45bf7573 | 18 | EXEC SQL WHENEVER SQLERROR CALL ingerr; |
a49e54ea | 19 | |
7de02494 | 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 | |
8ea172d2 | 33 | * Subnet |
7de02494 | 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; | |
a49e54ea | 53 | struct item *trans; /* keep track of transactions */ |
7de02494 | 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; | |
a49e54ea | 99 | cachehead.trans = (struct item *)NULL; |
7de02494 | 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 | { | |
a49e54ea | 112 | register struct item *i, *t; |
7de02494 | 113 | EXEC SQL BEGIN DECLARE SECTION; |
4bd3537c | 114 | char *iname; |
7de02494 | 115 | int j, rowcount; |
116 | EXEC SQL END DECLARE SECTION; | |
86f3e5d5 | 117 | char key; |
7de02494 | 118 | int h, ctr; |
119 | ||
4bd3537c | 120 | h = hashname(name, type); |
7de02494 | 121 | for (i = cachehead.next; i != &cachehead; i = i->next) { |
122 | if (i->nhash != h || | |
4bd3537c | 123 | strcmp(name, i->name) || |
7de02494 | 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++; | |
4bd3537c | 138 | iname = name; |
86f3e5d5 | 139 | key = *type; |
7de02494 | 140 | |
86f3e5d5 | 141 | if (!strcasecmp(type, "subnet")) |
142 | key = 'N'; | |
143 | ||
144 | switch (key) { | |
7de02494 | 145 | case 'U': |
146 | case 'u': | |
cb8990ed | 147 | if (index(iname, '@') || (strlen(iname) > 8)) { |
06c1c8ff | 148 | sqlca.sqlcode = 100; |
149 | break; | |
150 | } | |
7de02494 | 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': | |
99e09b48 | 159 | EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name=UPPERCASE(:iname); |
7de02494 | 160 | break; |
86f3e5d5 | 161 | case 'N': |
162 | case 'n': | |
8ea172d2 | 163 | EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name=UPPERCASE(:iname); |
164 | break; | |
7de02494 | 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; | |
4bd3537c | 187 | if (name[0] == '#' && !strcasecmp(type, "USER")) |
7de02494 | 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 | } | |
4bd3537c | 197 | strcpy(i->name, name); |
7de02494 | 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; | |
a49e54ea | 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 | } | |
7de02494 | 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 | { | |
45bf7573 | 225 | register struct item *i, *t; |
7de02494 | 226 | EXEC SQL BEGIN DECLARE SECTION; |
227 | char iname[NAMESZ]; | |
228 | int j, rowcount; | |
229 | EXEC SQL END DECLARE SECTION; | |
86f3e5d5 | 230 | char key; |
7de02494 | 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; | |
86f3e5d5 | 249 | key = *type; |
250 | if (!strcasecmp(type, "subnet")) | |
251 | key = 'N'; | |
7de02494 | 252 | |
86f3e5d5 | 253 | switch (key) { |
7de02494 | 254 | case 'U': |
255 | case 'u': | |
a112f91e | 256 | EXEC SQL SELECT CHAR(login) INTO :iname FROM users WHERE users_id=:j; |
7de02494 | 257 | break; |
258 | case 'L': | |
259 | case 'l': | |
a112f91e | 260 | EXEC SQL SELECT CHAR(name) INTO :iname FROM list WHERE list_id=:j; |
7de02494 | 261 | break; |
262 | case 'M': | |
263 | case 'm': | |
a112f91e | 264 | EXEC SQL SELECT CHAR(name) INTO :iname FROM machine WHERE mach_id=:j; |
7de02494 | 265 | break; |
86f3e5d5 | 266 | case 'N': |
267 | case 'n': | |
8ea172d2 | 268 | EXEC SQL SELECT CHAR(name) INTO :iname FROM subnet WHERE snet_id=:j; |
269 | break; | |
7de02494 | 270 | case 'C': |
271 | case 'c': | |
a112f91e | 272 | EXEC SQL SELECT CHAR(name) INTO :iname FROM cluster WHERE clu_id=:j; |
7de02494 | 273 | break; |
274 | case 'F': | |
275 | case 'f': | |
a112f91e | 276 | EXEC SQL SELECT CHAR(label) INTO :iname FROM filesys WHERE filsys_id=:j; |
7de02494 | 277 | break; |
278 | case 'S': | |
279 | case 's': | |
a112f91e | 280 | EXEC SQL SELECT CHAR(string) INTO :iname FROM strings WHERE string_id=:j; |
7de02494 | 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; | |
a49e54ea | 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 | } | |
7de02494 | 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 | { | |
a49e54ea | 334 | register struct item *i, *t; |
7de02494 | 335 | |
336 | for (i = cachehead.next; i != &cachehead; i = i->next) | |
a49e54ea | 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 | } | |
7de02494 | 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; | |
a49e54ea | 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 | } | |
7de02494 | 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 | } | |
a49e54ea | 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 | } |