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