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