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