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_qc = "$Header$";
16 #include <mit-copyright.h>
18 #include "sms_server.h"
24 ## int query_timeout = 30;
28 #define INGRES_BAD_INT 4111
29 #define INGRES_BAD_DATE 4302
30 #define INGRES_DEADLOCK 4700
31 #define INGRES_TIMEOUT 4702
34 * ingerr: (supposedly) called when Ingres indicates an error.
35 * I have not yet been able to get this to work to intercept a
36 * database open error.
39 static int ingerr(num)
46 sms_errcode = SMS_INTEGER;
49 sms_errcode = SMS_DATE;
52 sms_errcode = SMS_DEADLOCK;
53 com_err(whoami, 0, "INGRES deadlock detected");
56 sms_errcode = SMS_BUSY;
57 com_err(whoami, 0, "timed out getting lock");
60 sms_errcode = SMS_INGRES_ERR;
61 com_err(whoami, SMS_INGRES_ERR, " code %d\n", *num);
62 critical_alert("SMS", "SMS server encountered INGRES ERROR %d", *num);
68 int sms_open_database()
72 static first_open = 1;
77 /* initialize local argv */
78 for (i = 0; i < 16; i++)
79 Argv[i] = malloc(ARGLEN);
88 /* open the database */
90 ## set lockmode session where level = table, timeout = query_timeout
91 ## set lockmode on capacls where readlock = shared
92 ## set lockmode on alias where readlock = shared
96 int sms_close_database()
102 sms_check_access(cl, name, argc, argv_ro)
109 struct query *get_query_by_name();
113 q = get_query_by_name(name, cl->args->sms_version_no);
114 if (q == (struct query *)0)
115 return(SMS_NO_HANDLE);
117 return(sms_verify_query(cl, q, argc, argv_ro));
120 sms_process_query(cl, name, argc, argv_ro, action, actarg)
128 register struct query *q;
130 register struct validate *v;
135 ## char *table, *rvar;
136 struct save_queue *sq;
137 struct query *get_query_by_name();
139 struct save_queue *sq_create();
144 /* list queries command */
145 if (!strcmp(name, "_list_queries")) {
146 list_queries(cl->args->sms_version_no, action, actarg);
150 /* help query command */
151 if (!strcmp(name, "_help")) {
154 q = get_query_by_name(argv_ro[0], cl->args->sms_version_no);
155 if (q == (struct query *)0) return(SMS_NO_HANDLE);
156 help_query(q, action, actarg);
160 /* get query structure, return error if named query does not exist */
161 q = get_query_by_name(name, cl->args->sms_version_no);
162 if (q == (struct query *)0) return(SMS_NO_HANDLE);
165 if (q->type != RETRIEVE) {
169 /* setup argument vector, verify access and arguments */
170 if ((status = sms_verify_query(cl, q, argc, argv_ro)) != SMS_SUCCESS)
173 /* perform any special query pre-processing */
174 if (v && v->pre_rtn) {
175 status = (*v->pre_rtn)(q, Argv, cl, 0);
176 if (status != SMS_SUCCESS)
182 /* for queries that do not permit wildcarding, check if row
185 status = validate_row(q, Argv, v);
186 if (status != SMS_EXISTS) break;
189 /* build "where" clause if needed */
191 build_qual(q->qual, q->argc, Argv, qual);
197 /* build "sort" clause if needed */
198 if (v && v->valobj) {
199 psort = build_sort(v, sort);
204 /* if there is a followup routine, then we must save the results */
205 /* of the first query for use by the followup routine */
206 /* if q->rvar = NULL, perform post_rtn only */
208 if (v && v->post_rtn) {
210 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
211 if (status != SMS_SUCCESS) {
215 status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
217 /* normal retrieve */
218 status = do_retrieve(q, pqual, psort, action, actarg);
220 if (status != SMS_SUCCESS) break;
222 status = (*v->post_rtn)(q, Argv, cl, action, actarg);
228 /* see if row already exists */
230 status = validate_row(q, Argv, v);
231 if (status != SMS_EXISTS) break;
234 /* build "where" clause and perform update */
235 /* if q->rvar = NULL, perform post_rtn only */
237 build_qual(q->qual, q->argc, Argv, qual);
238 incremental_before(q->rtable, qual, argv_ro);
239 status = do_update(q, &Argv[q->argc], qual, action, actarg);
240 incremental_after(q->rtable, qual, argv_ro);
241 if (status != SMS_SUCCESS) break;
242 flush_name(argv_ro[0], q->rtable);
244 if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) {
245 ## repeat replace tblstats (updates = tblstats.updates + 1,
247 ## where tblstats.#table = @table
251 /* execute followup routine (if any) */
252 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
257 /* see if row already exists */
259 status = validate_row(q, Argv, v);
260 if (status != SMS_NO_MATCH) break;
263 /* increment id number if necessary */
265 status = set_next_object_id(v->object_id, q->rtable);
266 if (status != SMS_SUCCESS) break;
269 /* build "where" clause if needed */
271 build_qual(q->qual, q->argc, Argv, qual);
277 /* perform the append */
278 /* if q->rvar = NULL, perform post_rtn only */
280 incremental_clear_before();
281 status = do_append(q, &Argv[q->argc], pqual, action, actarg);
282 if (status != SMS_SUCCESS) break;
283 if (v && v->object_id) {
284 sprintf(qual, "%s.%s = values.value and values.name = \"%s\"",
285 q->rvar, v->object_id, v->object_id);
286 incremental_after(q->rtable, qual, argv_ro);
288 incremental_after(q->rtable, pqual, argv_ro);
291 ## repeat replace tblstats (appends = tblstats.appends + 1,
293 ## where tblstats.#table = @table
296 /* execute followup routine */
297 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
301 /* see if row already exists */
303 status = validate_row(q, Argv, v);
304 if (status != SMS_EXISTS) break;
307 /* build "where" clause and perform delete */
308 /* if q->rvar = NULL, perform post_rtn only */
310 build_qual(q->qual, q->argc, Argv, qual);
313 ## range of rvar is table
314 incremental_before(q->rtable, qual, argv_ro);
315 status = do_delete(q, qual, action, actarg);
316 incremental_clear_after();
317 if (status != SMS_SUCCESS) break;
318 flush_name(argv_ro[0], q->rtable);
319 ## repeat replace tblstats (deletes = tblstats.deletes + 1,
321 ## where tblstats.#table = @table
324 /* execute followup routine */
325 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
331 if (status == SMS_SUCCESS && ingres_errno != 0) {
332 com_err(whoami, SMS_INTERNAL, "Server didn't notice INGRES ERROR %d",
334 status = sms_errcode;
337 if (q->type != RETRIEVE) {
338 if (status == SMS_SUCCESS) {
339 ## end transaction /* commit to this */
345 fprintf(journal, "%% %s %s %s",
346 cl->clname, cl->entity, ctime(&now));
347 fprintf(journal, "%s[%d] ", q->name, cl->args->sms_version_no);
348 for (i = 0; i < argc; i++) {
352 requote(buf, argv_ro[i], sizeof(buf));
358 incremental_update();
360 if (ingres_errno != INGRES_DEADLOCK) {
361 ## abort /* it never happened */
365 ## set lockmode session where readlock = system
368 if (status != SMS_SUCCESS && log_flags & LOG_RES)
369 com_err(whoami, status, " (Query failed)");
373 build_qual(fmt, argc, argv, qual)
385 for (i = 0; i < argc; i++) {
387 if (c++ == (char *)0) return(SMS_ARGS);
391 *(int *)&args[i] = *(int *)argv[i]; /* sigh */
393 return(SMS_INGRES_ERR);
402 sprintf(qual, fmt, args[0]);
406 sprintf(qual, fmt, args[0], args[1]);
410 sprintf(qual, fmt, args[0], args[1], args[2]);
414 sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
422 register struct validate *v;
425 register struct valobj *vo;
434 if (vo->type == V_SORT) {
435 sprintf(elem, "RET_VAR%d", vo->index + 1);
436 if (*sort) strcat(sort, ", ");
442 return ((*sort) ? sort : 0);
446 /* Build arguement vector, verify query and arguments */
448 sms_verify_query(cl, q, argc, argv_ro)
456 register struct validate *v = q->validate;
458 register int privileged = 0;
461 /* copy the arguments into a local argv that we can modify */
462 if (argc >= QMAXARGS)
464 for (i = 0; i < argc; i++) {
465 if ((len = strlen(argv_ro[i])) < ARGLEN)
466 strcpy(Argv[i], argv_ro[i]);
468 return(SMS_ARG_TOO_LONG);
469 if (Argv[i][len-1] == '\\')
470 return(SMS_BAD_CHAR);
473 /* check initial query access */
474 status = check_query_access(q, Argv, cl);
475 if (status != SMS_SUCCESS && status != SMS_PERM)
477 if (status == SMS_SUCCESS)
480 /* check argument count */
482 if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
483 if (argc != argreq) return(SMS_ARGS);
485 /* validate arguments */
486 if (v && v->valobj) {
487 status = validate_fields(q, Argv, v->valobj, v->objcnt);
488 if (status != SMS_SUCCESS) return(status);
491 /* perform special query access check */
492 if (!privileged && v && v->acs_rtn) {
493 status = (*v->acs_rtn)(q, Argv, cl);
494 if (status != SMS_SUCCESS && status != SMS_PERM)
496 if (status == SMS_SUCCESS)
500 return(privileged ? SMS_SUCCESS : SMS_PERM);
504 /* This routine caches info from the database. Each query acl is stored
505 * in the query structure, and whether that acl contains everybody.
508 check_query_access(q, argv, cl)
518 ## static int def_uid;
523 /* initialize default uid */
525 ## retrieve (def_uid = users.users_id) where users.login = "default"
528 /* get query access control list */
533 ## retrieve (acl_id = capacls.list_id) where capacls.tag = name
534 ## inquire_equel (rowcount = "rowcount", errorno = "errorno")
535 if (errorno != 0) return(SMS_INGRES_ERR);
536 if (rowcount == 0) return(SMS_PERM);
539 /* check for default access */
540 ## retrieve (exists = any(imembers.#member_id where
541 ## imembers.list_id = acl_id and
542 ## imembers.member_type = "USER" and
543 ## imembers.#member_id = def_uid))
544 q->everybody = exists;
550 if (get_client(cl, &client_type, &client_id) != SMS_SUCCESS)
552 if (find_member("LIST", acl_id, client_type, client_id, 0))
559 get_client(cl, client_type, client_id)
564 if (cl->users_id > 0) {
565 *client_id = cl->users_id;
566 *client_type = "USER";
570 if (cl->client_id < 0) {
571 *client_id = -cl->users_id;
572 *client_type = "KERBEROS";
579 ##find_member(list_type, list_id, member_type, member_id)
582 ## char *member_type;
585 ## int exists, errorno;
587 if (!strcmp(strtrim(list_type), strtrim(member_type)) &&
588 list_id == member_id)
591 /* see if client is a direct member of list */
592 ## repeat retrieve (exists = any(imembers.#member_id where
593 ## imembers.#list_id = @list_id and
594 ## imembers.#member_type = @member_type and
595 ## imembers.#member_id = @member_id))
596 ## inquire_equel(errorno = "errorno")
604 do_retrieve(q, pqual, psort, action, actarg)
605 register struct query *q;
617 static char **vaddrs = (char **)NULL;
622 if ((vaddrs = (char **)malloc(sizeof(char *) * QMAXARGS)) == NULL) {
623 com_err(whoami, SMS_NO_MEM, "setting up static argv");
626 for (i = 0; i < QMAXARGS; i++) {
627 if ((vaddrs[i] = malloc(QMAXARGSIZE)) == NULL) {
628 com_err(whoami, SMS_NO_MEM, "setting up static argv");
637 ## range of rvar is rtable
644 ## retrieve unique (param (q->tlist, vaddrs)) where cqual
647 (*action)(q->vcnt, vaddrs, actarg);
650 ## retrieve unique (param (q->tlist, vaddrs))
653 (*action)(q->vcnt, vaddrs, actarg);
660 ## retrieve unique (param (q->tlist, vaddrs)) where cqual
662 (*action)(q->vcnt, vaddrs, actarg);
665 ## retrieve unique (param (q->tlist, vaddrs))
667 (*action)(q->vcnt, vaddrs, actarg);
672 ## inquire_equel (rowcount = "rowcount", errorno = "errorno")
673 if (errorno != 0) return(SMS_INGRES_ERR);
674 return ((rowcount == 0) ? SMS_NO_MATCH : SMS_SUCCESS);
677 do_update(q, argv, qual, action, actarg)
678 register struct query *q;
691 ## range of rvar is rtable
694 ## replace rvar (param (q->tlist, argv))
697 ## inquire_equel (errorno = "errorno")
698 if (errorno == INGRES_BAD_INT)
700 else if (errorno != 0)
701 return(SMS_INGRES_ERR);
705 do_append(q, argv, pqual, action, actarg)
706 register struct query *q;
719 ## range of rvar is rtable
723 ## append to rtable (param (q->tlist, argv)) where cqual
725 ## append to rtable (param (q->tlist, argv))
728 ## inquire_equel (errorno = "errorno")
729 if (errorno == INGRES_BAD_INT)
731 else if (errorno != 0)
732 return(SMS_INGRES_ERR);
736 do_delete(q, qual, action, actarg)
737 register struct query *q;
749 ## range of rvar is rtable
752 ## delete rvar where cqual
754 ## inquire_equel (errorno = "errorno")
755 if (errorno != 0) return(SMS_INGRES_ERR);
761 ** set_next_object_id - set next object id in values table
763 ** Inputs: object - object name in values table and in objects
764 ** table - name of table objects are found in
766 ** - called before an APPEND operation to set the next object id to
767 ** be used for the new record to the next free value
771 set_next_object_id(object, table)
776 ## int rowcount, exists, value;
780 ## repeat retrieve (value = values.#value) where values.#name = @name
781 ## inquire_equel(rowcount = "rowcount")
785 ## retrieve (exists = any(tbl.name where tbl.name = value))
786 ## inquire_equel(rowcount = "rowcount")
791 if (value > MAX_ID_VALUE)
792 value = MIN_ID_VALUE;
793 ## retrieve (exists = any(tbl.name where tbl.name = value))
797 com_err(whoami, 0, "setting ID %s to %d", name, value);
798 ## repeat replace values (#value = @value) where values.#name = @name
803 /* Turn a kerberos name into the user's ID of the account that principal
804 * owns. Sets the kerberos ID and user ID.
807 set_krb_mapping(name, login, ok, kid, uid)
814 ## int u_id, k_id, rowcount;
821 ## repeat retrieve (u_id = krbmap.#users_id, k_id = krbmap.#string_id)
822 ## where krbmap.string_id = strings.string_id and strings.string = @krbname
823 ## inquire_equel (rowcount = "rowcount")
831 if (name_to_id(name, "STRING", &k_id) == SMS_SUCCESS)
839 if (name_to_id(login, "USER", uid) != SMS_SUCCESS)
847 /* For now this just checks the argc's. It should also see that there
848 * are no duplicate names.
851 sanity_check_queries()
854 int maxv = 0, maxa = 0;
855 #ifdef MULTIPROTOCOLS
856 extern int QueryCount1, QueryCount2;
857 extern struct query Queries1[], Queries2[];
859 extern int QueryCount2;
860 extern struct query Queries2[];
861 #endif MULTIPROTOCOLS
863 #define MAX(x,y) ((x) > (y) ? (x) : (y))
865 #ifdef MULTIPROTOCOLS
866 for (i = 0; i < QueryCount1; i++) {
867 maxv = MAX(maxv, Queries1[i].vcnt);
868 maxa = MAX(maxa, Queries1[i].argc);
870 #endif MULTIPROTOCOLS
871 for (i = 0; i < QueryCount2; i++) {
872 maxv = MAX(maxv, Queries2[i].vcnt);
873 maxa = MAX(maxa, Queries2[i].argc);
875 if (MAX(maxv, maxa) > QMAXARGS) {
876 com_err(whoami, 0, "A query has more args than QMAXARGS");