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 /* structures to save before args */
38 static char *before[MAXARGC];
40 static enum tables beforetable;
42 /* structures to save after args */
43 static char *after[MAXARGC];
46 /* structures to save entire sets of incremental changes */
47 struct save_queue *incremental_sq = NULL;
48 struct save_queue *incremental_exec = NULL;
58 void next_incremental(void);
59 char **copy_argv(char **argv, int argc);
60 void free_argv(char **argv, int argc);
61 int table_num(char *table);
63 void incremental_init(void)
68 incremental_sq = sq_create();
69 if (!incremental_exec)
70 incremental_exec = sq_create();
72 for (i = 0; i < MAXARGC; i++)
74 before[i] = xmalloc(MAX_FIELD_WIDTH);
75 after[i] = xmalloc(MAX_FIELD_WIDTH);
80 /* record the state of a table row before it is changed */
82 void incremental_before(enum tables table, char *qual, char **argv)
84 EXEC SQL BEGIN DECLARE SECTION;
86 EXEC SQL END DECLARE SECTION;
95 sprintf(stmt_buf, "SELECT u.login, u.unix_uid, u.shell, "
96 "u.winconsoleshell, u.last, u.first, u.middle, u.status, "
97 "u.clearid, u.type, u.users_id FROM users u WHERE %s", qual);
102 sprintf(stmt_buf, "SELECT m.name, m.vendor, m.mach_id FROM machine m "
108 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, "
109 "c.clu_id FROM clusters c WHERE %s", qual);
113 case CONTAINERS_TABLE:
114 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, c.contact, "
115 "c.acl_type, c.acl_id, c.cnt_id, c.list_id FROM containers c "
120 id = atoi(before[5]);
121 if (!strncmp(before[4], "USER", 4))
123 id_to_name(id, USERS_TABLE, &name);
124 strcpy(before[5], name);
126 else if (!strncmp(before[4], "LIST", 4))
128 id_to_name(id, LIST_TABLE, &name);
129 strcpy(before[5], name);
131 else if (!strncmp(before[4], "KERBEROS", 8))
133 id_to_name(id, STRINGS_TABLE, &name);
134 strcpy(before[5], name);
136 id = atoi(before[7]);
137 id_to_name(id, LIST_TABLE, &name);
138 strcpy(before[7], name);
141 strcpy(before[0], argv[0]);
142 strcpy(before[1], argv[1]);
146 strcpy(before[0], argv[0]);
147 strcpy(before[1], argv[1]);
148 name_to_id(before[0], MACHINE_TABLE, &id);
149 sprintf(before[2], "%d", id);
150 name_to_id(before[1], CONTAINERS_TABLE, &id);
151 sprintf(before[3], "%d", id);
153 EXEC SQL SELECT list_id INTO :before[4] FROM containers
155 id = atoi(before[4]);
156 id_to_name(id, LIST_TABLE, &name);
157 strcpy(before[4], name);
161 strcpy(before[0], argv[0]);
162 strcpy(before[1], argv[1]);
163 strcpy(before[2], argv[2]);
167 sprintf(stmt_buf, "SELECT fs.label, fs.type, fs.mach_id, fs.name, "
168 "fs.mount, fs.rwaccess, fs.comments, fs.owner, fs.owners, "
169 "fs.createflg, fs.lockertype, fs.filsys_id FROM filesys fs "
173 id = atoi(before[2]);
174 id_to_name(id, MACHINE_TABLE, &name);
175 strcpy(before[2], name);
176 id = atoi(before[7]);
177 id_to_name(id, USERS_TABLE, &name);
178 strcpy(before[7], name);
179 id = atoi(before[8]);
180 id_to_name(id, LIST_TABLE, &name);
181 strcpy(before[8], name);
186 strcpy(before[0], "?");
187 strcpy(before[1], argv[1]);
188 strcpy(before[2], "?");
189 sprintf(stmt_buf, "SELECT q.quota, fs.name FROM quota q, filesys fs "
190 "WHERE %s AND fs.filsys_id = q.filsys_id", qual);
192 strcpy(before[2], argv[1]);
196 sprintf(stmt_buf, "SELECT l.name, l.active, l.publicflg, l.hidden, "
197 "l.maillist, l.grouplist, l.gid, l.acl_type, l.acl_id, "
198 "l.description, l.list_id FROM list l WHERE %s", qual);
204 sprintf(stmt_buf, "SELECT active, publicflg, hidden, maillist, "
205 "grouplist, gid FROM list WHERE list_id = %d", id);
208 id_to_name(id, LIST_TABLE, &name);
210 strcpy(before[0], name);
211 strcpy(before[1], argv[1]);
214 if (!strcmp(before[1], "USER"))
216 id_to_name(id, USERS_TABLE, &name2);
217 EXEC SQL SELECT status, users_id INTO :before[9], :before[11]
218 FROM users WHERE users_id = :id;
219 EXEC SQL SELECT list_id INTO :before[10] FROM list
223 else if (!strcmp(before[1], "LIST"))
225 id_to_name(id, LIST_TABLE, &name2);
226 EXEC SQL SELECT list_id INTO :before[9] FROM list
228 sprintf(before[10], "%d", id);
231 else if (!strcmp(before[1], "STRING") || !strcmp(before[1], "KERBEROS"))
233 id_to_name(id, STRINGS_TABLE, &name2);
234 EXEC SQL SELECT list_id INTO :before[9] FROM list
237 else if (!strcmp(before[1], "MACHINE"))
239 id_to_name(id, MACHINE_TABLE, &name2);
240 EXEC SQL SELECT list_id INTO :before[9] FROM list
242 sprintf(before[10], "%d", id);
245 strcpy(before[2], name2);
251 com_err(whoami, 0, "requested incremental on unexpected table `%s'",
259 void incremental_clear_before(void)
265 /* add an element to the incremental queue for the changed row */
267 void incremental_after(enum tables table, char *qual, char **argv)
270 EXEC SQL BEGIN DECLARE SECTION;
272 EXEC SQL END DECLARE SECTION;
278 sprintf(stmt_buf, "SELECT u.login, u.unix_uid, u.shell, "
279 "u.winconsoleshell, u.last, u.first, u.middle, u.status, "
280 "u.clearid, u.type, u.users_id FROM users u WHERE %s", qual);
285 sprintf(stmt_buf, "SELECT m.name, m.vendor, m.mach_id FROM machine m "
291 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, "
292 "c.clu_id FROM clusters c WHERE %s", qual);
296 case CONTAINERS_TABLE:
297 sprintf(stmt_buf, "SELECT c.name, c.description, c.location, c.contact, "
298 "c.acl_type, c.acl_id, c.cnt_id, c.list_id FROM containers c "
304 if (!strncmp(after[4], "USER", 4))
306 id_to_name(id, USERS_TABLE, &name);
307 strcpy(after[5], name);
309 else if (!strncmp(after[4], "LIST", 4))
311 id_to_name(id, LIST_TABLE, &name);
312 strcpy(after[5], name);
314 else if (!strncmp(after[4], "KERBEROS", 8))
316 id_to_name(id, STRINGS_TABLE, &name);
317 strcpy(after[5], name);
320 id_to_name(id, LIST_TABLE, &name);
321 strcpy(after[7], name);
324 strcpy(after[0], argv[0]);
325 strcpy(after[1], argv[1]);
329 strcpy(after[0], argv[0]);
330 strcpy(after[1], argv[1]);
331 name_to_id(after[0], MACHINE_TABLE, &id);
332 sprintf(after[2], "%d", id);
333 name_to_id(after[1], CONTAINERS_TABLE, &id);
334 sprintf(after[3], "%d", id);
336 EXEC SQL SELECT list_id INTO :after[4] FROM containers
339 id_to_name(id, LIST_TABLE, &name);
340 strcpy(after[4], name);
344 strcpy(after[0], argv[0]);
345 strcpy(after[1], argv[1]);
346 strcpy(after[2], argv[2]);
350 sprintf(stmt_buf, "SELECT fs.label, fs.type, fs.mach_id, fs.name, "
351 "fs.mount, fs.rwaccess, fs.comments, fs.owner, fs.owners, "
352 "fs.createflg, fs.lockertype, fs.filsys_id FROM filesys fs "
357 id_to_name(id, MACHINE_TABLE, &name);
358 strcpy(after[2], name);
360 id_to_name(id, USERS_TABLE, &name);
361 strcpy(after[7], name);
363 id_to_name(id, LIST_TABLE, &name);
364 strcpy(after[8], name);
369 strcpy(after[0], "?");
370 strcpy(after[1], argv[1]);
371 strcpy(after[2], "?");
372 sprintf(stmt_buf, "SELECT q.quota, fs.name FROM quota q, filesys fs "
373 "WHERE %s and fs.filsys_id = q.filsys_id and q.type = '%s'",
379 sprintf(stmt_buf, "SELECT l.name, l.active, l.publicflg, l.hidden, "
380 "l.maillist, l.grouplist, l.gid, l.acl_type, l.acl_id, "
381 "l.description, l.list_id FROM list l WHERE %s", qual);
387 sprintf(stmt_buf, "SELECT active, publicflg, hidden, maillist, "
388 "grouplist, gid FROM list WHERE list_id = %d", id);
391 id_to_name(id, LIST_TABLE, &name);
393 strcpy(after[0], name);
394 strcpy(after[1], argv[1]);
397 if (!strcmp(after[1], "USER"))
399 id_to_name(id, USERS_TABLE, &name2);
400 EXEC SQL SELECT status, users_id INTO :after[9], :after[11]
401 FROM users WHERE users_id = :id;
402 EXEC SQL SELECT list_id INTO :after[10] FROM list
406 else if (!strcmp(after[1], "LIST"))
408 id_to_name(id, LIST_TABLE, &name2);
409 EXEC SQL SELECT list_id INTO :after[9] FROM list
411 sprintf(after[10], "%d", id);
414 else if (!strcmp(after[1], "STRING") || !strcmp(after[1], "KERBEROS"))
416 id_to_name(id, STRINGS_TABLE, &name2);
417 EXEC SQL SELECT list_id INTO :after[9] FROM list
420 else if (!strcmp(after[1], "MACHINE"))
422 id_to_name(id, MACHINE_TABLE, &name2);
423 EXEC SQL SELECT list_id INTO :after[9] FROM list
425 sprintf(after[10], "%d", id);
428 strcpy(after[2], name2);
438 com_err(whoami, 0, "requested incremental on unexpected table `%s'",
444 iu = xmalloc(sizeof(struct iupdate));
445 iu->table = table_name[table];
446 iu->beforec = beforec;
447 iu->before = copy_argv(before, beforec);
449 iu->after = copy_argv(after, afterc);
450 sq_save_data(incremental_sq, iu);
453 void incremental_clear_after(void)
455 incremental_after(NO_TABLE, NULL, NULL);
459 /* Called when the current transaction is committed to start any queued
460 * incremental updates. This caches the update table the first time it
465 struct inc_cache *next;
466 char *table, *service;
470 void incremental_update(void)
472 static int inited = 0;
473 static struct inc_cache *cache;
475 EXEC SQL BEGIN DECLARE SECTION;
476 char tab[INCREMENTAL_TABLE_NAME_SIZE], serv[INCREMENTAL_SERVICE_SIZE];
477 EXEC SQL END DECLARE SECTION;
478 struct iupdate *iu, *iu_save;
484 EXEC SQL DECLARE inc CURSOR FOR SELECT table_name, service
489 EXEC SQL FETCH inc INTO :tab, :serv;
492 c = xmalloc(sizeof(struct inc_cache));
494 c->table = xstrdup(strtrim(tab));
495 c->service = xstrdup(strtrim(serv));
499 EXEC SQL COMMIT WORK;
502 while (sq_remove_data(incremental_sq, &iu))
504 for (c = cache; c; c = c->next)
506 if (!strcmp(c->table, iu->table))
508 iu->service = c->service;
509 iu_save = xmalloc(sizeof(struct iupdate));
510 iu_save->service = iu->service;
511 iu_save->table = iu->table;
512 iu_save->beforec = iu->beforec;
513 iu_save->afterc = iu->afterc;
514 iu_save->before = copy_argv(iu->before, iu->beforec);
515 iu_save->after = copy_argv(iu->after, iu->afterc);
516 sq_save_data(incremental_exec, iu_save);
521 free_argv(iu->before, iu->beforec);
522 free_argv(iu->after, iu->afterc);
526 if (inc_running == 0)
530 /* Pro*C 2.2.4 can't cope with the sigset_t below, at least in Solaris 2.6.
531 We add DEFINE=_PROC_ to the proc invocation and then #ifndef that around
532 this function so proc will pass it through without reading it. */
535 void next_incremental(void)
538 char *argv[MAXARGC * 2 + 4], cafter[3], cbefore[3], prog[MAXPATHLEN];
542 if (!incremental_exec)
545 if (sq_empty(incremental_exec) ||
546 (inc_running && now - inc_started < INC_TIMEOUT))
550 com_err(whoami, 0, "incremental timeout on pid %d", inc_pid);
552 sq_remove_data(incremental_exec, &iu);
554 sprintf(cbefore, "%d", iu->beforec);
556 sprintf(cafter, "%d", iu->afterc);
558 for (i = 0; i < iu->beforec; i++)
559 argv[4 + i] = iu->before[i];
560 for (i = 0; i < iu->afterc; i++)
561 argv[4 + iu->beforec + i] = iu->after[i];
563 sprintf(prog, "%s/%s.incr", BIN_DIR, iu->service);
565 argv[4 + iu->beforec + iu->afterc] = 0;
568 sigaddset(&sigs, SIGCHLD);
569 sigprocmask(SIG_BLOCK, &sigs, NULL);
577 com_err(whoami, 0, "Failed to start incremental update");
583 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
585 free_argv(iu->before, iu->beforec);
586 free_argv(iu->after, iu->afterc);
591 /* Called when the current transaction is aborted to throw away any queued
592 * incremental updates
595 void incremental_flush(void)
599 while (sq_get_data(incremental_sq, &iu))
601 free_argv(iu->before, iu->beforec);
602 free_argv(iu->after, iu->afterc);
605 sq_destroy(incremental_sq);
606 incremental_sq = sq_create();
610 char **copy_argv(char **argv, int argc)
612 char **ret = xmalloc(sizeof(char *) * argc);
614 ret[argc] = xstrdup(strtrim(argv[argc]));
618 void free_argv(char **argv, int argc)
625 int table_num(char *name)
629 for (i = num_tables - 1; i; i--)
631 if (!strcmp(table_name[i], name))
635 return i; /* 0 = "none" if no match */