6 * Copyright (C) 1987, 1988 by the Massachusetts Institute of Technology
7 * For copying and distribution information, please see the file
13 static char *rcsid_qrtn_dc = "$Header$";
16 #include <mit-copyright.h>
18 #include "mr_server.h"
19 EXEC SQL INCLUDE sqlca; /* SQL Communications Area */
20 EXEC SQL INCLUDE sqlda; /* SQL Descriptor Area */
24 EXEC SQL BEGIN DECLARE SECTION;
27 char cdummy[MR_CDUMMY_LEN];
28 char stmt_buf[MR_STMTBUF_LEN];
29 EXEC SQL END DECLARE SECTION;
35 EXEC SQL BEGIN DECLARE SECTION;
36 int query_timeout = 30;
37 EXEC SQL END DECLARE SECTION;
41 /** Maybe this should be replaced by something like tytso's sql_error */
43 #define INGRES_BAD_DATE1 40206
44 #define INGRES_BAD_DATE2 40207
45 #define INGRES_DEADLOCK 49900
47 #define INGRES_BAD_COLUMN 30110
48 #define INGRES_ASGN_ERR 40204
49 #define INGRES_NO_CURSOR 30120
50 #define INGRES_NO_STMT 30130
53 * ingerr: Called when Ingres indicates an error.
58 EXEC SQL BEGIN DECLARE SECTION;
60 EXEC SQL END DECLARE SECTION;
61 ingres_errno = -sqlca.sqlcode;
63 switch (ingres_errno) {
64 case INGRES_BAD_DATE1:
65 case INGRES_BAD_DATE2:
69 mr_errcode = MR_DEADLOCK;
70 com_err(whoami, 0, "INGRES deadlock detected");
72 /* I just don't know what the equivalent to this is, yet.
73 * case INGRES_TIMEOUT:
74 * mr_errcode = MR_BUSY;
75 * com_err(whoami, 0, "timed out getting lock");
78 case INGRES_NO_CURSOR:
79 mr_errcode = MR_INTERNAL;
80 com_err(whoami, 0, "Cursor not opened");
83 mr_errcode = MR_INTERNAL;
84 com_err(whoami, 0, "Statement not declared");
87 /* Taking these out during development lets default: give me the INGRES text */
88 case INGRES_BAD_COLUMN:
89 mr_errcode = MR_INTERNAL;
90 com_err(whoami, 0, "Bad column name in query table");
93 mr_errcode = MR_INTERNAL;
94 com_err(whoami, 0, "Error in SQL assignment statement");
98 mr_errcode = MR_INGRES_ERR;
99 com_err(whoami, MR_INGRES_ERR, " code %d\n", ingres_errno);
100 EXEC SQL INQUIRE_SQL(:err_msg = errortext);
101 com_err(whoami, 0, "SQL error text = %s", err_msg);
102 critical_alert("MOIRA", "Moira server encountered INGRES ERROR %d", ingres_errno);
106 /* This is declarative, not executed. Applies from here on, in this file. */
107 EXEC SQL WHENEVER SQLERROR CALL ingerr;
109 int mr_open_database()
113 MR_SQLDA_T *mr_alloc_SQLDA();
114 static first_open = 1;
119 /* initialize local argv */
120 for (i = 0; i < 16; i++)
121 Argv[i] = malloc(ARGLEN);
123 SQLDA = mr_alloc_SQLDA();
132 /* open the database */
134 EXEC SQL CONNECT moira;
136 return (ingres_errno);
137 EXEC SQL set lockmode session where level = table, timeout = :query_timeout;
138 EXEC SQL set lockmode on capacls where readlock = shared;
139 EXEC SQL set lockmode on alias where readlock = shared;
142 EXEC SQL DATABASE moira
148 EXEC SQL SELECT SIZE(signature) INTO :mr_sig_length FROM users WHERE users_id=0; /* Harmless on second open */
155 int mr_close_database()
163 mr_check_access(cl, name, argc, argv_ro)
170 struct query *get_query_by_name();
175 q = get_query_by_name(name, cl->args->mr_version_no);
176 if (q == (struct query *)0)
177 return(MR_NO_HANDLE);
179 return(mr_verify_query(cl, q, argc, argv_ro));
182 mr_process_query(cl, name, argc, argv_ro, action, actarg)
190 register struct query *q;
192 register struct validate *v;
197 EXEC SQL BEGIN DECLARE SECTION;
199 EXEC SQL END DECLARE SECTION;
200 struct save_queue *sq;
201 struct query *get_query_by_name();
203 struct save_queue *sq_create();
209 /* list queries command */
210 if (!strcmp(name, "_list_queries")) {
211 list_queries(cl->args->mr_version_no, action, actarg);
215 /* help query command */
216 if (!strcmp(name, "_help")) {
219 q = get_query_by_name(argv_ro[0], cl->args->mr_version_no);
220 if (q == (struct query *)0) return(MR_NO_HANDLE);
221 help_query(q, action, actarg);
225 /* get query structure, return error if named query does not exist */
226 q = get_query_by_name(name, cl->args->mr_version_no);
227 if (q == (struct query *)0) return(MR_NO_HANDLE);
230 /* setup argument vector, verify access and arguments */
231 if ((status = mr_verify_query(cl, q, argc, argv_ro)) != MR_SUCCESS)
234 /* perform any special query pre-processing */
235 if (v && v->pre_rtn) {
236 status = (*v->pre_rtn)(q, Argv, cl, 0);
237 if (status != MR_SUCCESS)
243 /* for queries that do not permit wildcarding, check if row
246 status = validate_row(q, Argv, v);
247 if (status != MR_EXISTS) break;
250 /* build "where" clause if needed */
252 build_qual(q->qual, q->argc, Argv, qual);
258 /* build "sort" clause if needed */
259 if (v && v->valobj) {
260 psort = build_sort(v, sort);
265 /* if there is a followup routine, then we must save the results */
266 /* of the first query for use by the followup routine */
267 /* if q->rvar = NULL, perform post_rtn only */
269 if (v && v->post_rtn) {
271 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
272 if (status != MR_SUCCESS) {
276 status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
278 /* normal retrieve */
279 status = do_retrieve(q, pqual, psort, action, actarg);
281 if (status != MR_SUCCESS) break;
283 status = (*v->post_rtn)(q, Argv, cl, action, actarg);
289 /* see if row already exists */
291 status = validate_row(q, Argv, v);
292 if (status != MR_EXISTS) break;
295 /* build "where" clause and perform update */
296 /* if q->rvar = NULL, perform post_rtn only */
298 build_qual(q->qual, q->argc, Argv, qual);
299 incremental_before(q->rtable, qual, argv_ro);
300 status = do_update(q, &Argv[q->argc], qual, action, actarg);
301 incremental_after(q->rtable, qual, argv_ro);
302 if (status != MR_SUCCESS) break;
303 flush_name(argv_ro[0], q->rtable);
304 table_name = q->rtable;
305 if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) {
306 EXEC SQL UPDATE tblstats
307 SET updates = updates + 1, modtime = 'now'
308 WHERE table_name = :table_name;
312 /* execute followup routine (if any) */
313 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
318 /* see if row already exists */
320 status = validate_row(q, Argv, v);
321 if (status != MR_NO_MATCH) break;
325 /* This is now done by a valobj, which also fetches the id value */
327 /* increment id number if necessary */
329 status = set_next_object_id(v->object_id, q->rtable, 0);
330 if (status != MR_SUCCESS) break;
334 /* build "where" clause if needed */
336 build_qual(q->qual, q->argc, Argv, qual);
342 /* perform the append */
343 /* if q->rvar = NULL, perform post_rtn only */
345 incremental_clear_before();
346 status = do_append(q, &Argv[q->argc], pqual, action, actarg);
347 if (status != MR_SUCCESS) break;
348 if (v && v->object_id) {
349 sprintf(qual, "%s.%s = %s",q->rtable, v->object_id,
350 Argv[q->argc+q->vcnt]);
351 incremental_after(q->rtable, qual, argv_ro);
353 incremental_after(q->rtable, pqual, argv_ro);
355 table_name = q->rtable;
356 EXEC SQL UPDATE tblstats
357 SET appends = appends + 1, modtime = 'now'
358 WHERE table_name = :table_name;
361 /* execute followup routine */
362 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
366 /* see if row already exists */
368 status = validate_row(q, Argv, v);
369 if (status != MR_EXISTS) break;
372 /* build "where" clause and perform delete */
373 /* if q->rvar = NULL, perform post_rtn only */
375 build_qual(q->qual, q->argc, Argv, qual);
376 table_name = q->rtable;
377 incremental_before(q->rtable, qual, argv_ro);
378 status = do_delete(q, qual, action, actarg);
379 incremental_clear_after();
380 if (status != MR_SUCCESS) break;
381 flush_name(argv_ro[0], q->rtable);
382 EXEC SQL UPDATE tblstats
383 SET deletes = deletes + 1, modtime = 'now'
384 WHERE table_name = :table_name;
387 /* execute followup routine */
388 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
394 if (status == MR_SUCCESS && ingres_errno != 0) {
395 com_err(whoami, MR_INTERNAL, "Server didn't notice INGRES ERROR %d",
400 if (q->type == RETRIEVE) {
401 EXEC SQL COMMIT WORK;
403 if (status == MR_SUCCESS) {
404 EXEC SQL COMMIT WORK;
410 fprintf(journal, "%% %s %s %s",
411 cl->clname, cl->entity, ctime(&now));
412 fprintf(journal, "%s[%d] ", q->name, cl->args->mr_version_no);
413 for (i = 0; i < argc; i++) {
417 requote(buf, argv_ro[i], sizeof(buf));
423 incremental_update();
426 if (ingres_errno != INGRES_DEADLOCK) {
427 EXEC SQL ROLLBACK WORK;
432 cache_commit(); /* commit following abort is safe */
434 if (status != MR_SUCCESS && log_flags & LOG_RES)
435 com_err(whoami, status, " (Query failed)");
439 build_qual(fmt, argc, argv, qual)
451 for (i = 0; i < argc; i++) {
453 if (c++ == (char *)0) return(MR_ARGS);
457 *(int *)&args[i] = *(int *)argv[i]; /* sigh */
459 return(MR_INGRES_ERR);
461 if (c = index(c, '%')) {
462 args[i] = args[i - 1];
471 sprintf(qual, fmt, args[0]);
475 sprintf(qual, fmt, args[0], args[1]);
479 sprintf(qual, fmt, args[0], args[1], args[2]);
483 sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
487 com_err(whoami, MR_INTERNAL,
488 "Internal arg count error processing query");
496 register struct validate *v;
499 register struct valobj *vo;
508 if (vo->type == V_SORT) {
509 sprintf(elem, "%d", vo->index + 1); /* Result column number */
510 if (*sort) strcat(sort, ", ");
516 return ((*sort) ? sort : 0);
520 /* Build arguement vector, verify query and arguments */
522 mr_verify_query(cl, q, argc, argv_ro)
530 register struct validate *v = q->validate;
532 register int privileged = 0;
533 register char *to,*fr,*stop;
535 /* copy the arguments into a local argv that we can modify */
536 if (argc >= QMAXARGS)
538 for (i = 0; i < argc; i++) {
539 /* Single quotes must be doubled for SQL */
540 for (to=Argv[i], fr=argv_ro[i], stop=to+ARGLEN; (*fr) && (to<stop);) {
546 return(MR_ARG_TOO_LONG);
553 /* check initial query access */
554 status = check_query_access(q, Argv, cl);
555 if (status != MR_SUCCESS && status != MR_PERM)
557 if (status == MR_SUCCESS)
560 /* check argument count */
562 if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
563 if (argc != argreq) return(MR_ARGS);
565 /* validate arguments */
566 if (v && v->valobj) {
567 status = validate_fields(q, Argv, v->valobj, v->objcnt);
568 if (status != MR_SUCCESS) return(status);
571 /* perform special query access check */
572 if (!privileged && v && v->acs_rtn) {
573 status = (*v->acs_rtn)(q, Argv, cl);
574 if (status != MR_SUCCESS && status != MR_PERM)
576 if (status == MR_SUCCESS)
580 return(privileged ? MR_SUCCESS : MR_PERM);
584 /* This routine caches info from the database. Each query acl is stored
585 * in the query structure, and whether that acl contains everybody.
588 check_query_access(q, argv, cl)
593 EXEC SQL BEGIN DECLARE SECTION;
599 EXEC SQL END DECLARE SECTION;
604 /* initialize default uid */
606 EXEC SQL SELECT users_id INTO :def_uid FROM users WHERE login='default';
609 /* get query access control list */
614 EXEC SQL SELECT list_id INTO :acl_id FROM capacls WHERE tag = :name;
615 if (sqlca.sqlcode < 0) return(MR_INGRES_ERR);
616 if (sqlca.sqlcode == 100) return(MR_PERM);
619 /* check for default access */
620 EXEC SQL SELECT member_id INTO :acl_id FROM imembers
621 WHERE list_id = :acl_id AND member_type = 'USER'
622 AND member_id = :def_uid;
623 if (sqlca.sqlerrd[2] == 0)
632 if (get_client(cl, &client_type, &client_id) != MR_SUCCESS)
634 if (find_member("LIST", acl_id, client_type, client_id, 0))
641 get_client(cl, client_type, client_id)
646 if (cl->users_id > 0) {
647 *client_id = cl->users_id;
648 *client_type = "USER";
652 if (cl->client_id < 0) {
653 *client_id = -cl->users_id;
654 *client_type = "KERBEROS";
661 find_member(list_type, list_id, member_type, member_id)
663 EXEC SQL BEGIN DECLARE SECTION;
667 EXEC SQL END DECLARE SECTION;
669 EXEC SQL BEGIN DECLARE SECTION;
671 EXEC SQL END DECLARE SECTION;
673 if (!strcmp(strtrim(list_type), strtrim(member_type)) &&
674 list_id == member_id)
677 /* see if client is a direct member of list */
679 EXEC SQL SELECT member_id INTO :flag FROM imembers
680 WHERE list_id = :list_id AND member_type = :member_type
681 AND member_id = :member_id;
682 if(flag!=0) flag=1; /** Not strictly necessary */
683 if (sqlca.sqlcode == 0)
689 do_retrieve(q, pqual, psort, action, actarg)
690 register struct query *q;
691 EXEC SQL BEGIN DECLARE SECTION;
694 EXEC SQL END DECLARE SECTION;
698 static char **vaddrs = (char **)NULL;
704 if ((vaddrs = (char **)malloc(sizeof(char *) * QMAXARGS)) == NULL) {
705 com_err(whoami, MR_NO_MEM, "setting up static argv");
708 for (i = 0; i < QMAXARGS; i++) {
709 vaddrs[i]=SQLDA->sqlvar[i].sqldata;
713 build_sql_stmt(stmt_buf,"SELECT",q->tlist,vaddrs,pqual);
714 if(psort) { strcat(stmt_buf," ORDER BY "); strcat(stmt_buf,psort); }
715 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
718 EXEC SQL DECLARE csr001 CURSOR FOR stmt;
719 EXEC SQL OPEN csr001;
722 EXEC SQL FETCH csr001 USING DESCRIPTOR :SQLDA;
723 if(sqlca.sqlcode != 0) break;
724 (*action)(q->vcnt, vaddrs, actarg);
727 EXEC SQL CLOSE csr001;
729 if (mr_errcode) return(mr_errcode);
730 return ((rowcount == 0) ? MR_NO_MATCH : MR_SUCCESS);
733 build_sql_stmt(result_buf,cmd,targetlist,argv,qual)
740 char fmt_buf[MR_STMTBUF_LEN];
741 register char *res, *fmt;
744 sprintf(fmt_buf,"%s %s WHERE %s",cmd,targetlist,qual);
746 sprintf(fmt_buf,"%s %s",cmd,targetlist);
748 for(res=result_buf, fmt=fmt_buf; *fmt; fmt++) {
752 case '%': /* %% -> % */
764 sprintf(res,"%d",*(int *)*argv++); /* print to result buffer */
767 default: /* Swallow other %? pairs */
771 } else *res++ = *fmt; /* text -> result buffer */
776 do_update(q, argv, qual, action, actarg)
777 register struct query *q;
783 build_sql_stmt(stmt_buf,"UPDATE",q->tlist,argv,qual);
784 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
785 if (mr_errcode) return(mr_errcode);
789 do_append(q, argv, pqual, action, actarg)
790 register struct query *q;
796 build_sql_stmt(stmt_buf,"INSERT",q->tlist,argv,pqual);
797 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
798 if (mr_errcode) return(mr_errcode);
802 do_delete(q, qual, action, actarg)
803 register struct query *q;
808 sprintf(stmt_buf,"DELETE FROM %s WHERE %s",q->rtable,qual);
809 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
810 if (mr_errcode) return(mr_errcode);
816 ** set_next_object_id - set next object id in values table
818 ** Inputs: object - object name in values table and in objects
819 ** table - name of table objects are found in
820 ** limit - should the ID be range limited
822 ** - called before an APPEND operation to set the next object id to
823 ** be used for the new record to the next free value
827 set_next_object_id(object, table_name, limit)
828 EXEC SQL BEGIN DECLARE SECTION;
832 EXEC SQL END DECLARE SECTION;
834 EXEC SQL BEGIN DECLARE SECTION;
836 EXEC SQL END DECLARE SECTION;
839 EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :object;
840 if (sqlca.sqlerrd[2] != 1)
843 starting_value=value;
845 if (limit && value > MAX_ID_VALUE)
846 value = MIN_ID_VALUE;
848 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s=%d",object,table_name,object,value);
849 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
852 EXEC SQL DECLARE csr002 CURSOR FOR stmt;
853 EXEC SQL OPEN csr002;
854 EXEC SQL FETCH csr002 USING DESCRIPTOR :SQLDA;
855 if (sqlca.sqlcode < 0) return(mr_errcode);
856 if (sqlca.sqlcode == 100) break;
858 EXEC SQL CLOSE csr002;
860 if (limit && value == starting_value) {
861 com_err(whoami,0,"All id values have been used");
865 EXEC SQL CLOSE csr002;
868 com_err(whoami, 0, "setting ID %s to %d", object, value);
869 EXEC SQL UPDATE numvalues SET value = :value WHERE name = :object;
874 /* Turn a kerberos name into the user's ID of the account that principal
875 * owns. Sets the kerberos ID and user ID.
878 int set_krb_mapping(name, login, ok, kid, uid)
885 EXEC SQL BEGIN DECLARE SECTION;
888 EXEC SQL END DECLARE SECTION;
894 EXEC SQL SELECT km.users_id, km.string_id INTO :u_id, :k_id
895 FROM krbmap km, strings str
896 WHERE km.string_id = str.string_id AND str.string = :krbname;
898 if (ingres_errno) return(mr_errcode);
900 if (sqlca.sqlerrd[2] == 1) { /* rowcount */
906 if (name_to_id(name, "STRINGS", &k_id) == MR_SUCCESS)
914 if (name_to_id(login, "USERS", uid) != MR_SUCCESS)
919 if (ingres_errno) return(mr_errcode);
924 /* For now this just checks the argc's. It should also see that there
925 * are no duplicate names.
928 sanity_check_queries()
931 int maxv = 0, maxa = 0;
932 #ifdef MULTIPROTOCOLS
933 extern int QueryCount1, QueryCount2;
934 extern struct query Queries1[], Queries2[];
936 extern int QueryCount2;
937 extern struct query Queries2[];
938 #endif MULTIPROTOCOLS
940 #define MAX(x,y) ((x) > (y) ? (x) : (y))
942 #ifdef MULTIPROTOCOLS
943 for (i = 0; i < QueryCount1; i++) {
944 maxv = MAX(maxv, Queries1[i].vcnt);
945 maxa = MAX(maxa, Queries1[i].argc);
947 #endif MULTIPROTOCOLS
948 for (i = 0; i < QueryCount2; i++) {
949 maxv = MAX(maxv, Queries2[i].vcnt);
950 maxa = MAX(maxa, Queries2[i].argc);
952 if (MAX(maxv, maxa) > QMAXARGS) {
953 com_err(whoami, 0, "A query has more args than QMAXARGS");