]> andersk Git - moira.git/blob - server/qrtn.qc
Don't update the database on retrieves (Used to keep stats)
[moira.git] / server / qrtn.qc
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987, 1988 by the Massachusetts Institute of Technology
7  * 
8  */
9
10 #ifndef lint
11 static char *rcsid_qrtn_qc = "$Header$";
12 #endif lint
13
14 #include "query.h"
15 #include "sms_server.h"
16
17 char *Argv[16];
18
19 static int ingres_errno = 0;
20 extern char *whoami;
21 extern FILE *journal;
22
23 #define INGRES_BAD_INT 4111
24 #define INGRES_BAD_DATE 4302
25 #define INGRES_DEADLOCK 4700
26
27 /*
28  * ingerr: (supposedly) called when Ingres indicates an error.
29  * I have not yet been able to get this to work to intercept a
30  * database open error.
31  */
32
33 static int ingerr(num)
34     int *num;
35 {
36     char buf[256];
37
38     switch (*num) {
39     case INGRES_BAD_INT:
40         ingres_errno = SMS_INTEGER;
41         break;
42     case INGRES_BAD_DATE:
43         ingres_errno = SMS_DATE;
44         break;
45     case INGRES_DEADLOCK:
46         ingres_errno = SMS_DEADLOCK;
47         break;
48     default:
49         ingres_errno = SMS_INGRES_ERR;
50         com_err(whoami, SMS_INGRES_ERR, " code %d\n", *num);
51         critical_alert("SMS", "SMS server encountered INGRES ERROR %d", *num);
52         return (*num);
53     }
54     return (0);
55 }
56
57 int sms_open_database()
58 {
59     register int i;
60     char *malloc();
61
62     /* initialize local argv */
63     for (i = 0; i < 16; i++)
64         Argv[i] = malloc(ARGLEN);
65
66     IIseterr(ingerr);
67         
68     ingres_errno = 0;
69         
70     /* open the database */
71 ##  ingres sms
72     return ingres_errno;
73 }
74
75 int sms_close_database()
76 {
77 ##  exit
78 }
79
80 sms_check_access(cl, name, argc, argv_ro)
81     client *cl;
82     char *name;
83     int argc;
84     char *argv_ro[];
85 {
86     struct query *q;
87     struct query *get_query_by_name();
88
89     q = get_query_by_name(name, cl->args->sms_version_no);
90     if (q == (struct query *)0)
91         return(SMS_NO_HANDLE);
92
93     return(sms_verify_query(cl, q, argc, argv_ro));
94 }
95
96 sms_process_query(cl, name, argc, argv_ro, action, actarg)
97     client *cl;
98     char *name;
99     int argc;
100     char *argv_ro[];
101     int (*action)();
102     char *actarg;
103 {
104     register struct query *q;
105     register int status;
106     register struct validate *v;
107     char qual[256];
108     char sort[32];
109     char *pqual;
110     char *psort;
111 ##  char *table;
112     struct save_queue *sq;
113     struct query *get_query_by_name();
114     int sq_save_args();
115     struct save_queue *sq_create();
116     char *build_sort();
117
118     /* list queries command */
119     if (!strcmp(name, "_list_queries")) {
120         list_queries(cl->args->sms_version_no, action, actarg);
121         return(SMS_SUCCESS);
122     }
123
124     /* help query command */
125     if (!strcmp(name, "_help")) {
126         if (argc < 1)
127             return(SMS_ARGS);
128         q = get_query_by_name(argv_ro[0], cl->args->sms_version_no);
129         if (q == (struct query *)0) return(SMS_NO_HANDLE);
130         help_query(q, action, actarg);
131         return(SMS_SUCCESS);
132     }
133
134     /* get query structure, return error if named query does not exist */
135     q = get_query_by_name(name, cl->args->sms_version_no);
136     if (q == (struct query *)0) return(SMS_NO_HANDLE);
137     v = q->validate;
138
139     if (q->type != RETRIEVE)
140 ##      begin transaction
141
142     /* setup argument vector, verify access and arguments */
143     if ((status = sms_verify_query(cl, q, argc, argv_ro)) != SMS_SUCCESS)
144         goto out;
145
146     /* perform any special query pre-processing */
147     if (v && v->pre_rtn) {
148         status = (*v->pre_rtn)(q, Argv, cl, 0);
149         if (status != SMS_SUCCESS)
150             goto out;
151     }
152
153     switch (q->type) {
154     case RETRIEVE:
155         /* for queries that do not permit wildcarding, check if row
156            uniquely exists */
157         if (v && v->field) {
158             status = validate_row(q, Argv, v);
159             if (status != SMS_EXISTS) break;
160         }
161
162         /* build "where" clause if needed */
163         if (q->qual) {
164             build_qual(q->qual, q->argc, Argv, qual);
165             pqual = qual;
166         } else {
167             pqual = 0;
168         }
169
170         /* build "sort" clause if needed */
171         if (v && v->valobj) {
172             psort = build_sort(v, sort);
173         } else {
174             psort = 0;
175         }
176
177         /* if there is a followup routine, then we must save the results */
178         /* of the first query for use by the followup routine */
179         /* if q->rvar = NULL, perform post_rtn only */
180         if (q->rvar) {
181             if (v && v->post_rtn) {
182                 sq = sq_create();
183                 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
184                 if (status != SMS_SUCCESS) {
185                     sq_destroy(sq);
186                     break;
187                 }
188                 status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
189             } else {
190                 /* normal retrieve */
191                 status = do_retrieve(q, pqual, psort, action, actarg);
192             }
193             if (status != SMS_SUCCESS) break;
194         } else {
195             status = (*v->post_rtn)(q, Argv, cl, action, actarg);
196         }
197
198         break;
199
200     case UPDATE:
201         /* see if row already exists */
202         if (v->field) {
203             status = validate_row(q, Argv, v);
204             if (status != SMS_EXISTS) break;
205         }
206
207         /* build "where" clause and perform update */
208         /* if q->rvar = NULL, perform post_rtn only */
209         if (q->rvar) {
210             build_qual(q->qual, q->argc, Argv, qual);
211             status = do_update(q, &Argv[q->argc], qual, action, actarg);
212             if (status != SMS_SUCCESS) break;
213             table = q->rtable;
214             if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) {
215 ##              repeat replace tblstats (updates = tblstats.updates + 1,
216 ##                                       modtime = "now")
217 ##                  where tblstats.#table = @table
218             }
219         }
220
221         /* execute followup routine (if any) */
222         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
223
224         break;
225
226     case APPEND:
227         /* see if row already exists */
228         if (v->field) {
229             status = validate_row(q, Argv, v);
230             if (status != SMS_NO_MATCH) break;
231         }
232
233         /* increment id number if necessary */
234         if (v->object_id) {
235             status = set_next_object_id(v->object_id, q->rtable);
236             if (status != SMS_SUCCESS) break;
237         }
238
239         /* build "where" clause if needed */
240         if (q->qual) {
241             build_qual(q->qual, q->argc, Argv, qual);
242             pqual = qual;
243         } else {
244             pqual = 0;
245         }
246
247         /* perform the append */
248         /* if q->rvar = NULL, perform post_rtn only */
249         if (q->rvar) {
250             status = do_append(q, &Argv[q->argc], pqual, action, actarg);
251             if (status != SMS_SUCCESS) break;
252             table = q->rtable;
253 ##          repeat replace tblstats (appends = tblstats.appends + 1,
254 ##                                   modtime = "now")
255 ##              where tblstats.#table = @table
256         }
257         
258         /* execute followup routine */
259         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
260         break;
261
262     case DELETE:
263         /* see if row already exists */
264         if (v->field) {
265             status = validate_row(q, Argv, v);
266             if (status != SMS_EXISTS) break;
267         }
268
269         /* build "where" clause and perform delete */
270         /* if q->rvar = NULL, perform post_rtn only */
271         if (q->rvar) {
272             build_qual(q->qual, q->argc, Argv, qual);
273             status = do_delete(q, qual, action, actarg);
274             if (status != SMS_SUCCESS) break;
275             table = q->rtable;
276 ##          repeat replace tblstats (deletes = tblstats.deletes + 1,
277 ##                                   modtime = "now")
278 ##              where tblstats.#table = @table
279         }
280
281         /* execute followup routine */
282         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
283         break;
284
285     }
286
287 out:
288     if (q->type != RETRIEVE) {
289         if (status == SMS_SUCCESS) {
290 ##          end transaction     /* commit to this */
291             if (journal) {
292                 char buf[1024], *bp;
293                 int i;
294                 extern time_t now;
295
296                 fprintf(journal, "%% %s %s %s",
297                         cl->clname, cl->entity, ctime(&now));
298                 fprintf(journal, "%s[%d] ", q->name, cl->args->sms_version_no);
299                 for (i = 0; i < argc; i++) {
300                     if (i != 0) {
301                         putc(' ', journal);
302                     }
303                     requote(buf, argv_ro[i], sizeof(buf));
304                     fputs(buf, journal);
305                 }
306                 putc('\n', journal);
307                 fflush(journal);
308             }
309         } else {
310 ##          abort               /* it never happened */
311         }
312     }
313
314     if (status != SMS_SUCCESS && log_flags & LOG_RES)
315         com_err(whoami, status, " (Query failed)");
316     return(status);
317 }
318
319 build_qual(fmt, argc, argv, qual)
320         char *fmt;
321         int argc;
322         char *argv[];
323         char *qual;
324 {
325     register char *c;
326     register int i;
327     char *args[4];
328     char *index();
329
330     c = fmt;
331     for (i = 0; i < argc; i++) {
332         c = index(c, '%');
333         if (c++ == (char *)0) return(SMS_ARGS);
334         if (*c == 's')
335             args[i] = argv[i];
336         else if (*c == 'd')
337             *(int *)&args[i] = *(int *)argv[i]; /* sigh */
338         else
339             return(SMS_INGRES_ERR);
340     }
341
342     switch (argc) {
343     case 0:
344         strcpy(qual, fmt);
345         break;
346
347     case 1:
348         sprintf(qual, fmt, args[0]);
349         break;
350
351     case 2:
352         sprintf(qual, fmt, args[0], args[1]);
353         break;
354
355     case 3:
356         sprintf(qual, fmt, args[0], args[1], args[2]);
357         break;
358
359     case 4:
360         sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
361         break;
362     }
363     return(SMS_SUCCESS);
364 }
365
366 char *
367 build_sort(v, sort)
368     register struct validate *v;
369     char *sort;
370 {
371     register struct valobj *vo;
372     register int n;
373     char elem[16];
374
375     n = v->objcnt;
376     vo = v->valobj;
377     *sort = 0;
378
379     while (--n >= 0) {
380         if (vo->type == V_SORT) {
381             sprintf(elem, "RET_VAR%d", vo->index + 1);
382             if (*sort) strcat(sort, ", ");
383             strcat(sort, elem);
384         }
385         vo++;
386     }
387
388     return ((*sort) ? sort : 0);
389 }
390
391
392 /* Build arguement vector, verify query and arguments */
393
394 sms_verify_query(cl, q, argc, argv_ro)
395     client *cl;
396     struct query *q;
397     int argc;
398     char *argv_ro[];
399 {
400     register int argreq;
401     register int status;
402     register struct validate *v = q->validate;
403     register int i;
404     register int privileged = 0;
405
406     /* copy the arguments into a local argv that we can modify */
407     for (i = 0; i < argc; i++) {
408         if (strlen(argv_ro[i]) < ARGLEN)
409             strcpy(Argv[i], argv_ro[i]);
410         else
411             return(SMS_ARG_TOO_LONG);
412     }
413
414     /* check initial query access */
415     status = check_query_access(q, Argv, cl);
416     if (status != SMS_SUCCESS && status != SMS_PERM)
417         return(status);
418     if (status == SMS_SUCCESS)
419         privileged++;
420
421     /* check argument count */
422     argreq = q->argc;
423     if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
424     if (argc != argreq) return(SMS_ARGS);
425
426     /* validate arguments */
427     if (v && v->valobj) {
428         status = validate_fields(q, Argv, v->valobj, v->objcnt);
429         if (status != SMS_SUCCESS) return(status);
430     }
431
432     /* perform special query access check */
433     if (!privileged && v && v->acs_rtn) {
434         status = (*v->acs_rtn)(q, Argv, cl);
435         if (status != SMS_SUCCESS && status != SMS_PERM)
436             return(status);
437         if (status == SMS_SUCCESS)
438             privileged++;
439     }
440
441     return(privileged ? SMS_SUCCESS : SMS_PERM);
442 }
443
444 check_query_access(q, argv, cl)
445     struct query *q;
446     char *argv[];
447     client *cl;
448 ##{
449 ##  char *name;
450 ##  int acl_id;
451 ##  int exists;
452 ##  int rowcount;
453 ##  int errorno;
454 ##  static int def_uid;
455     int status;
456     int client_id;
457     char *client_type;
458
459     /* get query access control list */
460     name = q->shortname;
461 ##  repeat retrieve (acl_id = capacls.list_id) where capacls.tag = @name
462 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
463     if (errorno != 0) return(SMS_INGRES_ERR);
464     if (rowcount == 0) return(SMS_PERM);
465
466     /* initialize default uid */
467     if (def_uid == 0) {
468 ##      retrieve (def_uid = users.users_id) where users.login = "default"
469     }
470
471     /* check for default access */
472 ##  range of m is members
473 ##  repeat retrieve (exists = any(m.#member_id where m.list_id = @acl_id and
474 ##                   m.member_type = "USER" and m.#member_id = def_uid))
475     if (exists) return(SMS_SUCCESS);
476
477     /* parse client name */
478     status = get_client(cl, &client_type, &client_id);
479     if (status != SMS_SUCCESS) return(status);
480
481     /* see if client is in the list (or any of its sub-lists) */
482     exists = find_member("LIST", acl_id, client_type, client_id, 0);
483     return ((exists) ? SMS_SUCCESS : SMS_PERM);
484 ##}
485
486 get_client(cl, client_type, client_id)
487     client *cl;
488     char **client_type;
489     int *client_id;
490 ##{
491     struct krbname *krb;
492 ##  int member_id;
493 ##  char *name;
494 ##  int rowcount;
495
496     if (cl->clname == NULL)
497         return SMS_PERM;
498     
499     /* for now ignore instances */
500     krb = &cl->kname;
501
502     /* if client is from local realm, get users_id */
503     if (!strcmp(krb->realm, krb_realm)) {
504         *client_id = cl->users_id;
505         *client_type = "USER";
506         return(SMS_SUCCESS);
507     }
508
509     /* otherwise use string_id */
510     name = cl->clname;
511 ##  repeat retrieve (member_id = strings.string_id) 
512 ##      where strings.string = @name
513         
514     /* make sure we found a users or string id */
515 ##  inquire_equel (rowcount = "rowcount")
516     if (rowcount == 0) return(SMS_PERM);
517
518     *client_type = "STRING";
519     *client_id = member_id;
520     return(SMS_SUCCESS);
521 ##}
522
523 ##find_member(list_type, list_id, member_type, member_id, sq)
524     char *list_type;
525 ##  int list_id;
526 ##  char *member_type;
527 ##  int member_id;
528     struct save_queue *sq;
529 ##{
530 ##  int exists;
531 ##  int sublist;
532     int child;
533     struct save_queue *sq_create();
534
535     /* see if client is a direct member of list */
536 ##  repeat retrieve (exists = any(m.#member_id where 
537 ##                                m.#list_id = @list_id and
538 ##                                m.#member_type = @member_type and 
539 ##                                m.#member_id = @member_id))
540     if (exists) return(1);
541
542     /* are there any sub-lists? */
543 ##  repeat retrieve (exists = any(m.#member_id where m.#list_id = @list_id and
544 ##                   m.#member_type = "LIST"))
545     if (!exists) return(0);
546
547     /* yes; now recurse through sublists */
548
549     /* create a save queue */
550     if (sq == (struct save_queue *)0) {
551         sq = sq_create();
552         child = 0;
553     } else {
554         child = 1;
555     }
556
557     /* save all sublist ids */
558 ##  range of m is members
559 ##  retrieve (sublist = m.#member_id) 
560 ##      where m.#list_id = list_id and m.#member_type = "LIST"
561 ##  {
562          sq_save_unique_data(sq, sublist);
563 ##  }
564
565     if (child) return(0);
566
567     /* at top-level, check sub-lists for client (breadth-first search) */
568     while (sq_get_data(sq, &sublist)) {
569         exists = find_member(list_type, sublist, member_type, member_id, sq);
570         if (exists) {
571             sq_destroy(sq);
572             return(1);
573         }
574     }
575     sq_destroy(sq);
576     return(0);
577 ##}
578
579
580 do_retrieve(q, pqual, psort, action, actarg)
581     register struct query *q;
582     char *pqual;
583     char *psort;
584     int (*action)();
585     char *actarg;
586 ##{
587 ##  char *rvar;
588 ##  char *rtable;
589 ##  char *cqual;
590 ##  char *csort;
591 ##  int rowcount;
592 ##  int errorno;
593     static char **vaddrs = (char **)NULL;
594
595     if (!vaddrs) {
596         register int i;
597
598         if ((vaddrs = (char **)malloc(sizeof(char *) * QMAXARGS)) == NULL) {
599             com_err(whoami, SMS_NO_MEM, "setting up static argv");
600             exit(1);
601         }
602         for (i = 0; i < QMAXARGS; i++) {
603             if ((vaddrs[i] = malloc(QMAXARGSIZE)) == NULL) {
604                 com_err(whoami, SMS_NO_MEM, "setting up static argv");
605                 exit(1);
606             }
607         }
608     }
609
610     if (q->rvar) {
611         rvar = q->rvar;
612         rtable = q->rtable;
613 ##      range of rvar is rtable
614     }
615
616     if (psort) {
617         csort = psort;
618         if (pqual) {
619             cqual = pqual;
620 ##          retrieve unique (param (q->tlist, vaddrs)) where cqual
621 ##                   sort by csort
622 ##          {
623                  (*action)(q->vcnt, vaddrs, actarg);
624 ##          }
625         } else {
626 ##          retrieve unique (param (q->tlist, vaddrs))
627 ##                   sort by csort
628 ##          {
629                  (*action)(q->vcnt, vaddrs, actarg);
630 ##          }
631         }
632
633     } else {
634         if (pqual) {
635             cqual = pqual;
636 ##          retrieve unique (param (q->tlist, vaddrs)) where cqual
637 ##          {
638                  (*action)(q->vcnt, vaddrs, actarg);
639 ##          }
640         } else {
641 ##          retrieve unique (param (q->tlist, vaddrs))
642 ##          {
643                  (*action)(q->vcnt, vaddrs, actarg);
644 ##          }
645         }
646     }
647
648 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
649     if (errorno != 0) return(SMS_INGRES_ERR);
650     return ((rowcount == 0) ? SMS_NO_MATCH : SMS_SUCCESS);
651 ##}
652
653 do_update(q, argv, qual, action, actarg)
654     register struct query *q;
655     char *argv[];
656     char *qual;
657     int (*action)();
658     char *actarg;
659 ##{
660 ##  char *rvar;
661 ##  char *rtable;
662 ##  char *cqual;
663 ##  int errorno;
664       
665     rvar = q->rvar;
666     rtable = q->rtable;
667 ##  range of rvar is rtable
668
669     cqual = qual;
670 ##  replace rvar (param (q->tlist, argv))
671 ##  where cqual
672
673 ##  inquire_equel (errorno = "errorno")
674     if (errorno == INGRES_BAD_INT)
675         return(SMS_INTEGER);
676     else if (errorno != 0)
677         return(SMS_INGRES_ERR);
678     return(SMS_SUCCESS);
679 ##}
680
681 do_append(q, argv, pqual, action, actarg)
682     register struct query *q;
683     char *argv[];
684     char *pqual;
685     int (*action)();
686     char *actarg;
687 ##{
688 ##  char *rvar;
689 ##  char *rtable;
690 ##  char *cqual;
691 ##  int errorno;
692
693     rvar = q->rvar;
694     rtable = q->rtable;
695 ##  range of rvar is rtable
696
697     if (pqual) {
698         cqual = pqual;
699 ##      append to rtable (param (q->tlist, argv)) where cqual
700     } else {
701 ##      append to rtable (param (q->tlist, argv))
702     }
703
704 ##  inquire_equel (errorno = "errorno")
705     if (errorno == INGRES_BAD_INT)
706         return(SMS_INTEGER);
707     else if (errorno != 0)
708         return(SMS_INGRES_ERR);
709     return(SMS_SUCCESS);
710 ##}
711
712 do_delete(q, qual, action, actarg)
713     register struct query *q;
714     char *qual;
715     int (*action)();
716     char *actarg;
717 ##{
718 ##  char *rvar;
719 ##  char *rtable;
720 ##  char *cqual;
721 ##  int errorno;
722
723     rvar = q->rvar;
724     rtable = q->rtable;
725 ##  range of rvar is rtable
726
727     cqual = qual;
728 ##  delete rvar where cqual
729
730 ##  inquire_equel (errorno = "errorno")
731     if (errorno != 0) return(SMS_INGRES_ERR);
732     return(SMS_SUCCESS);
733 ##}
734
735
736 /**
737  ** set_next_object_id - set next object id in values table
738  **
739  ** Inputs: object - object name in values table and in objects
740  **         table - name of table objects are found in
741  **
742  ** - called before an APPEND operation to set the next object id to
743  **   be used for the new record to the next free value
744  **
745  **/
746
747 set_next_object_id(object, table)
748     char *object;
749     char *table;
750 ##{
751 ##  char *name, *tbl;
752 ##  int rowcount, exists, value;
753
754     name = object;
755     tbl = table;
756 ##  range of v is values
757 ##  repeat retrieve (value = v.#value) where v.#name = @name
758 ##  inquire_equel(rowcount = "rowcount")
759     if (rowcount != 1)
760         return(SMS_NO_ID);
761
762 ##  retrieve (exists = any(tbl.name where tbl.name = value))
763 ##  inquire_equel(rowcount = "rowcount")
764     if (rowcount != 1)
765         return(SMS_NO_ID);
766     while (exists) {
767         value++;
768         if (value > MAX_ID_VALUE)
769             value = MIN_ID_VALUE;
770 ##      retrieve (exists = any(tbl.name where tbl.name = value))
771     }
772
773     if (LOG_RES)
774         com_err(whoami, 0, "setting ID %s to %d", name, value);
775 ##  repeat replace v (#value = @value) where v.#name = @name
776     return(SMS_SUCCESS);
777 ##}
778
779
780 /* For now this just checks the argc's.  It should also see that there
781  * are no duplicate names.
782  */
783
784 sanity_check_queries()
785 {
786     register int i;
787     int maxv = 0, maxa = 0;
788     extern int QueryCount1, QueryCount2;
789     extern struct query Queries1[], Queries2[];
790 #define MAX(x,y) ((x) > (y) ? (x) : (y))
791
792     for (i = 0; i < QueryCount1; i++) {
793         maxv = MAX(maxv, Queries1[i].vcnt);
794         maxa = MAX(maxa, Queries1[i].argc);
795     }
796     for (i = 0; i < QueryCount2; i++) {
797         maxv = MAX(maxv, Queries2[i].vcnt);
798         maxa = MAX(maxa, Queries2[i].argc);
799     }
800     if (MAX(maxv, maxa) > QMAXARGS) {
801         com_err(whoami, 0, "A query has more args than QMAXARGS");
802         exit(1);
803     }
804 }
805
806
807 /*
808  * Local Variables:
809  * mode: c
810  * c-indent-level: 4
811  * c-continued-statement-offset: 4
812  * c-brace-offset: -4
813  * c-argdecl-indent: 4
814  * c-label-offset: -4
815  * End:
816  */
817
This page took 0.393407 seconds and 5 git commands to generate.