3 * Deal with incremental updates
5 * Copyright (C) 1989-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
11 #include "mr_server.h"
21 EXEC SQL INCLUDE sqlca;
26 extern char *table_name[];
27 extern int num_tables;
35 EXEC SQL WHENEVER SQLERROR DO dbmserr();
37 EXEC SQL BEGIN DECLARE SECTION;
38 /* structures to save before args */
39 static char *before[MAXARGC];
41 static enum tables beforetable;
43 /* structures to save after args */
44 static char *after[MAXARGC];
46 EXEC SQL END DECLARE SECTION;
48 /* structures to save entire sets of incremental changes */
49 struct save_queue *incremental_sq = NULL;
50 struct save_queue *incremental_exec = NULL;
60 void next_incremental(void);
61 char **copy_argv(char **argv, int argc);
62 void free_argv(char **argv, int argc);
63 int table_num(char *table);
65 void incremental_init(void)
70 incremental_sq = sq_create();
71 if (!incremental_exec)
72 incremental_exec = sq_create();
74 for (i = 0; i < MAXARGC; i++)
76 before[i] = xmalloc(MAX_FIELD_WIDTH);
77 after[i] = xmalloc(MAX_FIELD_WIDTH);
82 /* record the state of a table row before it is changed */
84 void incremental_before(enum tables table, char *qual, char **argv)
86 EXEC SQL BEGIN DECLARE SECTION;
88 EXEC SQL END DECLARE SECTION;
97 sprintf(stmt_buf, "SELECT u.login, u.unix_uid, u.shell, "
98 "u.winconsoleshell, u.last, u.first, u.middle, u.status, "
99 "u.clearid, u.type, u.users_id FROM users u WHERE %s", qual);
104 sprintf(stmt_buf, "SELECT m.name, m.vendor, m.mach_id FROM machine m "
110 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, "
111 "c.clu_id FROM clusters c WHERE %s", qual);
115 case CONTAINERS_TABLE:
116 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, c.contact, "
117 "c.acl_type, c.acl_id, c.cnt_id, c.list_id FROM containers c "
122 id = atoi(before[5]);
123 if (!strncmp(before[4], "USER", 4))
125 id_to_name(id, USERS_TABLE, &name);
126 strcpy(before[5], name);
128 else if (!strncmp(before[4], "LIST", 4))
130 id_to_name(id, LIST_TABLE, &name);
131 strcpy(before[5], name);
133 else if (!strncmp(before[4], "KERBEROS", 8))
135 id_to_name(id, STRINGS_TABLE, &name);
136 strcpy(before[5], name);
138 id = atoi(before[7]);
139 id_to_name(id, LIST_TABLE, &name);
140 strcpy(before[7], name);
143 strcpy(before[0], argv[0]);
144 strcpy(before[1], argv[1]);
148 strcpy(before[0], argv[0]);
149 strcpy(before[1], argv[1]);
150 name_to_id(before[0], MACHINE_TABLE, &id);
151 sprintf(before[2], "%d", id);
152 name_to_id(before[1], CONTAINERS_TABLE, &id);
153 sprintf(before[3], "%d", id);
155 EXEC SQL SELECT list_id INTO :before[4] FROM containers
157 id = atoi(before[4]);
158 id_to_name(id, LIST_TABLE, &name);
159 strcpy(before[4], name);
163 strcpy(before[0], argv[0]);
164 strcpy(before[1], argv[1]);
165 strcpy(before[2], argv[2]);
169 sprintf(stmt_buf, "SELECT fs.label, fs.type, fs.mach_id, fs.name, "
170 "fs.mount, fs.rwaccess, fs.comments, fs.owner, fs.owners, "
171 "fs.createflg, fs.lockertype, fs.filsys_id FROM filesys fs "
175 id = atoi(before[2]);
176 id_to_name(id, MACHINE_TABLE, &name);
177 strcpy(before[2], name);
178 id = atoi(before[7]);
179 id_to_name(id, USERS_TABLE, &name);
180 strcpy(before[7], name);
181 id = atoi(before[8]);
182 id_to_name(id, LIST_TABLE, &name);
183 strcpy(before[8], name);
188 strcpy(before[0], "?");
189 strcpy(before[1], argv[1]);
190 strcpy(before[2], "?");
191 sprintf(stmt_buf, "SELECT q.quota, fs.name FROM quota q, filesys fs "
192 "WHERE %s AND fs.filsys_id = q.filsys_id", qual);
194 strcpy(before[2], argv[1]);
198 sprintf(stmt_buf, "SELECT l.name, l.active, l.publicflg, l.hidden, "
199 "l.maillist, l.grouplist, l.gid, l.acl_type, l.acl_id, "
200 "l.description, l.list_id, l.mailman, l.mailman_id "
201 "FROM list l WHERE %s", qual);
204 id = atoi(before[12]);
205 id_to_name(id, MACHINE_TABLE, &name);
206 strcpy(before[12], name);
212 sprintf(stmt_buf, "SELECT active, publicflg, hidden, maillist, "
213 "grouplist, gid FROM list WHERE list_id = %d", id);
216 id_to_name(id, LIST_TABLE, &name);
218 strcpy(before[0], name);
219 strcpy(before[1], argv[1]);
222 if (!strcmp(before[1], "USER"))
224 id_to_name(id, USERS_TABLE, &name2);
225 EXEC SQL SELECT status, users_id INTO :before[9], :before[11]
226 FROM users WHERE users_id = :id;
227 EXEC SQL SELECT list_id INTO :before[10] FROM list
231 else if (!strcmp(before[1], "LIST"))
233 id_to_name(id, LIST_TABLE, &name2);
234 EXEC SQL SELECT list_id INTO :before[9] FROM list
236 sprintf(before[10], "%d", id);
239 else if (!strcmp(before[1], "STRING") || !strcmp(before[1], "KERBEROS"))
241 id_to_name(id, STRINGS_TABLE, &name2);
242 EXEC SQL SELECT list_id INTO :before[9] FROM list
245 else if (!strcmp(before[1], "MACHINE"))
247 id_to_name(id, MACHINE_TABLE, &name2);
248 EXEC SQL SELECT list_id INTO :before[9] FROM list
250 sprintf(before[10], "%d", id);
253 strcpy(before[2], name2);
259 com_err(whoami, 0, "requested incremental on unexpected table `%s'",
267 void incremental_clear_before(void)
273 /* add an element to the incremental queue for the changed row */
275 void incremental_after(enum tables table, char *qual, char **argv)
278 EXEC SQL BEGIN DECLARE SECTION;
280 EXEC SQL END DECLARE SECTION;
286 sprintf(stmt_buf, "SELECT u.login, u.unix_uid, u.shell, "
287 "u.winconsoleshell, u.last, u.first, u.middle, u.status, "
288 "u.clearid, u.type, u.users_id FROM users u WHERE %s", qual);
293 sprintf(stmt_buf, "SELECT m.name, m.vendor, m.mach_id FROM machine m "
299 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, "
300 "c.clu_id FROM clusters c WHERE %s", qual);
304 case CONTAINERS_TABLE:
305 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, c.contact, "
306 "c.acl_type, c.acl_id, c.cnt_id, c.list_id FROM containers c "
312 if (!strncmp(after[4], "USER", 4))
314 id_to_name(id, USERS_TABLE, &name);
315 strcpy(after[5], name);
317 else if (!strncmp(after[4], "LIST", 4))
319 id_to_name(id, LIST_TABLE, &name);
320 strcpy(after[5], name);
322 else if (!strncmp(after[4], "KERBEROS", 8))
324 id_to_name(id, STRINGS_TABLE, &name);
325 strcpy(after[5], name);
328 id_to_name(id, LIST_TABLE, &name);
329 strcpy(after[7], name);
332 strcpy(after[0], argv[0]);
333 strcpy(after[1], argv[1]);
337 strcpy(after[0], argv[0]);
338 strcpy(after[1], argv[1]);
339 name_to_id(after[0], MACHINE_TABLE, &id);
340 sprintf(after[2], "%d", id);
341 name_to_id(after[1], CONTAINERS_TABLE, &id);
342 sprintf(after[3], "%d", id);
344 EXEC SQL SELECT list_id INTO :after[4] FROM containers
347 id_to_name(id, LIST_TABLE, &name);
348 strcpy(after[4], name);
352 strcpy(after[0], argv[0]);
353 strcpy(after[1], argv[1]);
354 strcpy(after[2], argv[2]);
358 sprintf(stmt_buf, "SELECT fs.label, fs.type, fs.mach_id, fs.name, "
359 "fs.mount, fs.rwaccess, fs.comments, fs.owner, fs.owners, "
360 "fs.createflg, fs.lockertype, fs.filsys_id FROM filesys fs "
365 id_to_name(id, MACHINE_TABLE, &name);
366 strcpy(after[2], name);
368 id_to_name(id, USERS_TABLE, &name);
369 strcpy(after[7], name);
371 id_to_name(id, LIST_TABLE, &name);
372 strcpy(after[8], name);
377 strcpy(after[0], "?");
378 strcpy(after[1], argv[1]);
379 strcpy(after[2], "?");
380 sprintf(stmt_buf, "SELECT q.quota, fs.name FROM quota q, filesys fs "
381 "WHERE %s and fs.filsys_id = q.filsys_id and q.type = '%s'",
387 sprintf(stmt_buf, "SELECT l.name, l.active, l.publicflg, l.hidden, "
388 "l.maillist, l.grouplist, l.gid, l.acl_type, l.acl_id, "
389 "l.description, l.list_id, l.mailman, l.mailman_id "
390 "FROM list l WHERE %s", qual);
393 id = atoi(after[12]);
394 id_to_name(id, MACHINE_TABLE, &name);
395 strcpy(after[12], name);
401 sprintf(stmt_buf, "SELECT active, publicflg, hidden, maillist, "
402 "grouplist, gid FROM list WHERE list_id = %d", id);
405 id_to_name(id, LIST_TABLE, &name);
407 strcpy(after[0], name);
408 strcpy(after[1], argv[1]);
411 if (!strcmp(after[1], "USER"))
413 id_to_name(id, USERS_TABLE, &name2);
414 EXEC SQL SELECT status, users_id INTO :after[9], :after[11]
415 FROM users WHERE users_id = :id;
416 EXEC SQL SELECT list_id INTO :after[10] FROM list
420 else if (!strcmp(after[1], "LIST"))
422 id_to_name(id, LIST_TABLE, &name2);
423 EXEC SQL SELECT list_id INTO :after[9] FROM list
425 sprintf(after[10], "%d", id);
428 else if (!strcmp(after[1], "STRING") || !strcmp(after[1], "KERBEROS"))
430 id_to_name(id, STRINGS_TABLE, &name2);
431 EXEC SQL SELECT list_id INTO :after[9] FROM list
434 else if (!strcmp(after[1], "MACHINE"))
436 id_to_name(id, MACHINE_TABLE, &name2);
437 EXEC SQL SELECT list_id INTO :after[9] FROM list
439 sprintf(after[10], "%d", id);
442 strcpy(after[2], name2);
452 com_err(whoami, 0, "requested incremental on unexpected table `%s'",
458 iu = xmalloc(sizeof(struct iupdate));
459 iu->table = table_name[table];
460 iu->beforec = beforec;
461 iu->before = copy_argv(before, beforec);
463 iu->after = copy_argv(after, afterc);
464 sq_save_data(incremental_sq, iu);
467 void incremental_clear_after(void)
469 incremental_after(NO_TABLE, NULL, NULL);
473 /* Called when the current transaction is committed to start any queued
474 * incremental updates. This caches the update table the first time it
479 struct inc_cache *next;
480 char *table, *service;
484 void incremental_update(void)
486 static int inited = 0;
487 static struct inc_cache *cache;
489 EXEC SQL BEGIN DECLARE SECTION;
490 char tab[INCREMENTAL_TABLE_NAME_SIZE], serv[INCREMENTAL_SERVICE_SIZE];
491 EXEC SQL END DECLARE SECTION;
492 struct iupdate *iu, *iu_save;
498 EXEC SQL DECLARE inc CURSOR FOR SELECT table_name, service
503 EXEC SQL FETCH inc INTO :tab, :serv;
506 c = xmalloc(sizeof(struct inc_cache));
508 c->table = xstrdup(strtrim(tab));
509 c->service = xstrdup(strtrim(serv));
513 EXEC SQL COMMIT WORK;
516 while (sq_remove_data(incremental_sq, &iu))
518 for (c = cache; c; c = c->next)
520 if (!strcmp(c->table, iu->table))
522 iu->service = c->service;
523 iu_save = xmalloc(sizeof(struct iupdate));
524 iu_save->service = iu->service;
525 iu_save->table = iu->table;
526 iu_save->beforec = iu->beforec;
527 iu_save->afterc = iu->afterc;
528 iu_save->before = copy_argv(iu->before, iu->beforec);
529 iu_save->after = copy_argv(iu->after, iu->afterc);
530 sq_save_data(incremental_exec, iu_save);
535 free_argv(iu->before, iu->beforec);
536 free_argv(iu->after, iu->afterc);
540 if (inc_running == 0)
544 /* Pro*C 2.2.4 can't cope with the sigset_t below, at least in Solaris 2.6.
545 We add DEFINE=_PROC_ to the proc invocation and then #ifndef that around
546 this function so proc will pass it through without reading it. */
549 void next_incremental(void)
552 char *argv[MAXARGC * 2 + 4], cafter[3], cbefore[3], prog[MAXPATHLEN];
556 if (!incremental_exec)
559 if (sq_empty(incremental_exec) ||
560 (inc_running && now - inc_started < INC_TIMEOUT))
564 com_err(whoami, 0, "incremental timeout on pid %d", inc_pid);
566 sq_remove_data(incremental_exec, &iu);
568 sprintf(cbefore, "%d", iu->beforec);
570 sprintf(cafter, "%d", iu->afterc);
572 for (i = 0; i < iu->beforec; i++)
573 argv[4 + i] = iu->before[i];
574 for (i = 0; i < iu->afterc; i++)
575 argv[4 + iu->beforec + i] = iu->after[i];
577 sprintf(prog, "%s/%s.incr", BIN_DIR, iu->service);
579 argv[4 + iu->beforec + iu->afterc] = 0;
582 sigaddset(&sigs, SIGCHLD);
583 sigprocmask(SIG_BLOCK, &sigs, NULL);
591 com_err(whoami, 0, "Failed to start incremental update");
597 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
599 free_argv(iu->before, iu->beforec);
600 free_argv(iu->after, iu->afterc);
605 /* Called when the current transaction is aborted to throw away any queued
606 * incremental updates
609 void incremental_flush(void)
613 while (sq_get_data(incremental_sq, &iu))
615 free_argv(iu->before, iu->beforec);
616 free_argv(iu->after, iu->afterc);
619 sq_destroy(incremental_sq);
620 incremental_sq = sq_create();
624 char **copy_argv(char **argv, int argc)
626 char **ret = xmalloc(sizeof(char *) * argc);
628 ret[argc] = xstrdup(strtrim(argv[argc]));
632 void free_argv(char **argv, int argc)
639 int table_num(char *name)
643 for (i = num_tables - 1; i; i--)
645 if (!strcmp(table_name[i], name))
649 return i; /* 0 = "none" if no match */