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