]> andersk Git - moira.git/blame - server/qrtn.dc
Sped up some query-table queries by adding optimize_sql_stmt() call
[moira.git] / server / qrtn.dc
CommitLineData
1a41acb7 1/*
2 * $Source$
3 * $Author$
4 * $Header$
b070f8a1 5 *
1a41acb7 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 *
b070f8a1 10 */
11
12#ifndef lint
048b1b94 13static char *rcsid_qrtn_dc = "$Header$";
b070f8a1 14#endif lint
15
16#include <mit-copyright.h>
cc0088db 17#include <string.h>
b070f8a1 18#include "query.h"
19#include "mr_server.h"
048b1b94 20EXEC SQL INCLUDE sqlca; /* SQL Communications Area */
21EXEC SQL INCLUDE sqlda; /* SQL Descriptor Area */
22#include "qrtn.h"
23
c9a214e4 24MR_SQLDA_T *SQLDA;
048b1b94 25EXEC SQL BEGIN DECLARE SECTION;
1741dca0 26int mr_sig_length;
048b1b94 27int idummy;
28char cdummy[MR_CDUMMY_LEN];
29char stmt_buf[MR_STMTBUF_LEN];
30EXEC SQL END DECLARE SECTION;
b070f8a1 31
32char *Argv[16];
33
34int ingres_errno = 0;
35int mr_errcode = 0;
048b1b94 36EXEC SQL BEGIN DECLARE SECTION;
37int query_timeout = 30;
38EXEC SQL END DECLARE SECTION;
b070f8a1 39extern char *whoami;
40extern FILE *journal;
41
1741dca0 42#define INGRES_BAD_DATE1 40206
43#define INGRES_BAD_DATE2 40207
44#define INGRES_DEADLOCK 49900
1b247a36 45#define INGRES_TIMEOUT 39100
45bf7573 46#define INGRES_BAD_COLUMN 30110
47#define INGRES_ASGN_ERR 40204
48#define INGRES_NO_CURSOR 30120
49#define INGRES_NO_STMT 30130
50
048b1b94 51/*
1741dca0 52 * ingerr: Called when Ingres indicates an error.
b070f8a1 53 */
54
c9a214e4 55void ingerr()
b070f8a1 56{
45bf7573 57 EXEC SQL BEGIN DECLARE SECTION;
58 char err_msg[256];
59 EXEC SQL END DECLARE SECTION;
048b1b94 60 ingres_errno = -sqlca.sqlcode;
b070f8a1 61
048b1b94 62 switch (ingres_errno) {
1741dca0 63 case INGRES_BAD_DATE1:
64 case INGRES_BAD_DATE2:
b070f8a1 65 mr_errcode = MR_DATE;
66 break;
1741dca0 67 case INGRES_DEADLOCK:
b070f8a1 68 mr_errcode = MR_DEADLOCK;
69 com_err(whoami, 0, "INGRES deadlock detected");
70 break;
1b247a36 71 case INGRES_TIMEOUT:
72/* May be something other than timeout! #39100 is "Unknown error"
73 * Really should parse the error message enough to decide if it's a timeout */
74 mr_errcode = MR_BUSY;
75 com_err(whoami, 0, "timed out getting lock");
76 break;
77/* These should never come up unless someone breaks the query table */
78 case INGRES_NO_CURSOR:
45bf7573 79 mr_errcode = MR_INTERNAL;
80 com_err(whoami, 0, "Cursor not opened");
81 break;
1b247a36 82 case INGRES_NO_STMT:
45bf7573 83 mr_errcode = MR_INTERNAL;
84 com_err(whoami, 0, "Statement not declared");
85 break;
1b247a36 86 case INGRES_BAD_COLUMN:
1741dca0 87 mr_errcode = MR_INTERNAL;
88 com_err(whoami, 0, "Bad column name in query table");
89 break;
1b247a36 90 case INGRES_ASGN_ERR:
1741dca0 91 mr_errcode = MR_INTERNAL;
92 com_err(whoami, 0, "Error in SQL assignment statement");
93 break;
1b247a36 94/* Others ??? */
95 default:
b070f8a1 96 mr_errcode = MR_INGRES_ERR;
45bf7573 97 com_err(whoami, MR_INGRES_ERR, " code %d\n", ingres_errno);
98 EXEC SQL INQUIRE_SQL(:err_msg = errortext);
99 com_err(whoami, 0, "SQL error text = %s", err_msg);
c9a214e4 100 critical_alert("MOIRA", "Moira server encountered INGRES ERROR %d", ingres_errno);
b070f8a1 101 }
b070f8a1 102}
103
45bf7573 104/* This is declarative, not executed. Applies from here on, in this file. */
048b1b94 105EXEC SQL WHENEVER SQLERROR CALL ingerr;
106
b070f8a1 107int mr_open_database()
108{
109 register int i;
110 char *malloc();
c9a214e4 111 MR_SQLDA_T *mr_alloc_SQLDA();
b070f8a1 112 static first_open = 1;
113
114 if (first_open) {
115 first_open = 0;
116
117 /* initialize local argv */
118 for (i = 0; i < 16; i++)
119 Argv[i] = malloc(ARGLEN);
120
c9a214e4 121 SQLDA = mr_alloc_SQLDA();
122
b070f8a1 123 incremental_init();
124 flush_cache();
125 }
126
127 ingres_errno = 0;
128 mr_errcode = 0;
129
130 /* open the database */
048b1b94 131#ifsql INGRES
132 EXEC SQL CONNECT moira;
1741dca0 133 if(ingres_errno)
134 return (ingres_errno);
048b1b94 135 EXEC SQL set lockmode session where level = table, timeout = :query_timeout;
136 EXEC SQL set lockmode on capacls where readlock = shared;
137 EXEC SQL set lockmode on alias where readlock = shared;
138#endsql
139#ifsql INFORMIX
140 EXEC SQL DATABASE moira
141#endsql
1741dca0 142
143 if(ingres_errno)
144 return(mr_errcode);
145
146 EXEC SQL SELECT SIZE(signature) INTO :mr_sig_length FROM users WHERE users_id=0; /* Harmless on second open */
147 if(ingres_errno)
148 return(mr_errcode);
149
150 return(MR_SUCCESS);
b070f8a1 151}
152
153int mr_close_database()
154{
155 flush_cache();
048b1b94 156#ifsql INGRES
157 EXEC SQL DISCONNECT;
158#endsql
b070f8a1 159}
160
161mr_check_access(cl, name, argc, argv_ro)
162 client *cl;
163 char *name;
164 int argc;
165 char *argv_ro[];
166{
167 struct query *q;
168 struct query *get_query_by_name();
169
170 ingres_errno = 0;
171 mr_errcode = 0;
172
173 q = get_query_by_name(name, cl->args->mr_version_no);
174 if (q == (struct query *)0)
175 return(MR_NO_HANDLE);
176
048b1b94 177 return(mr_verify_query(cl, q, argc, argv_ro));
b070f8a1 178}
179
180mr_process_query(cl, name, argc, argv_ro, action, actarg)
181 client *cl;
182 char *name;
183 int argc;
184 char *argv_ro[];
185 int (*action)();
186 char *actarg;
187{
188 register struct query *q;
189 register int status;
190 register struct validate *v;
191 char qual[256];
192 char sort[32];
193 char *pqual;
194 char *psort;
048b1b94 195 EXEC SQL BEGIN DECLARE SECTION;
196 char *table_name;
197 EXEC SQL END DECLARE SECTION;
b070f8a1 198 struct save_queue *sq;
199 struct query *get_query_by_name();
200 int sq_save_args();
201 struct save_queue *sq_create();
202 char *build_sort();
203
204 ingres_errno = 0;
205 mr_errcode = 0;
206
207 /* list queries command */
208 if (!strcmp(name, "_list_queries")) {
209 list_queries(cl->args->mr_version_no, action, actarg);
210 return(MR_SUCCESS);
211 }
212
213 /* help query command */
214 if (!strcmp(name, "_help")) {
215 if (argc < 1)
216 return(MR_ARGS);
217 q = get_query_by_name(argv_ro[0], cl->args->mr_version_no);
218 if (q == (struct query *)0) return(MR_NO_HANDLE);
219 help_query(q, action, actarg);
220 return(MR_SUCCESS);
221 }
222
223 /* get query structure, return error if named query does not exist */
224 q = get_query_by_name(name, cl->args->mr_version_no);
225 if (q == (struct query *)0) return(MR_NO_HANDLE);
226 v = q->validate;
227
228 /* setup argument vector, verify access and arguments */
229 if ((status = mr_verify_query(cl, q, argc, argv_ro)) != MR_SUCCESS)
230 goto out;
231
232 /* perform any special query pre-processing */
233 if (v && v->pre_rtn) {
234 status = (*v->pre_rtn)(q, Argv, cl, 0);
235 if (status != MR_SUCCESS)
236 goto out;
237 }
238
239 switch (q->type) {
240 case RETRIEVE:
241 /* for queries that do not permit wildcarding, check if row
242 uniquely exists */
243 if (v && v->field) {
048b1b94 244 status = validate_row(q, Argv, v);
b070f8a1 245 if (status != MR_EXISTS) break;
246 }
247
248 /* build "where" clause if needed */
249 if (q->qual) {
048b1b94 250 build_qual(q->qual, q->argc, Argv, qual);
b070f8a1 251 pqual = qual;
252 } else {
253 pqual = 0;
254 }
255
256 /* build "sort" clause if needed */
257 if (v && v->valobj) {
258 psort = build_sort(v, sort);
259 } else {
260 psort = 0;
261 }
262
263 /* if there is a followup routine, then we must save the results */
264 /* of the first query for use by the followup routine */
265 /* if q->rvar = NULL, perform post_rtn only */
266 if (q->rvar) {
267 if (v && v->post_rtn) {
268 sq = sq_create();
269 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
270 if (status != MR_SUCCESS) {
271 sq_destroy(sq);
272 break;
273 }
274 status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
275 } else {
276 /* normal retrieve */
277 status = do_retrieve(q, pqual, psort, action, actarg);
278 }
279 if (status != MR_SUCCESS) break;
280 } else {
281 status = (*v->post_rtn)(q, Argv, cl, action, actarg);
282 }
283
284 break;
285
286 case UPDATE:
287 /* see if row already exists */
288 if (v->field) {
289 status = validate_row(q, Argv, v);
290 if (status != MR_EXISTS) break;
291 }
292
293 /* build "where" clause and perform update */
294 /* if q->rvar = NULL, perform post_rtn only */
295 if (q->rvar) {
296 build_qual(q->qual, q->argc, Argv, qual);
297 incremental_before(q->rtable, qual, argv_ro);
298 status = do_update(q, &Argv[q->argc], qual, action, actarg);
299 incremental_after(q->rtable, qual, argv_ro);
300 if (status != MR_SUCCESS) break;
301 flush_name(argv_ro[0], q->rtable);
048b1b94 302 table_name = q->rtable;
b070f8a1 303 if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) {
048b1b94 304 EXEC SQL UPDATE tblstats
305 SET updates = updates + 1, modtime = 'now'
306 WHERE table_name = :table_name;
b070f8a1 307 }
308 }
309
310 /* execute followup routine (if any) */
311 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
312
313 break;
314
315 case APPEND:
316 /* see if row already exists */
317 if (v->field) {
318 status = validate_row(q, Argv, v);
319 if (status != MR_NO_MATCH) break;
320 }
321
b070f8a1 322 /* build "where" clause if needed */
323 if (q->qual) {
324 build_qual(q->qual, q->argc, Argv, qual);
325 pqual = qual;
326 } else {
327 pqual = 0;
328 }
329
330 /* perform the append */
331 /* if q->rvar = NULL, perform post_rtn only */
332 if (q->rvar) {
333 incremental_clear_before();
334 status = do_append(q, &Argv[q->argc], pqual, action, actarg);
335 if (status != MR_SUCCESS) break;
336 if (v && v->object_id) {
1b247a36 337 sprintf(qual, "%s.%s = %s",q->rvar, v->object_id,
45bf7573 338 Argv[q->argc+q->vcnt]);
b070f8a1 339 incremental_after(q->rtable, qual, argv_ro);
340 } else
341 incremental_after(q->rtable, pqual, argv_ro);
342
048b1b94 343 table_name = q->rtable;
344 EXEC SQL UPDATE tblstats
345 SET appends = appends + 1, modtime = 'now'
346 WHERE table_name = :table_name;
b070f8a1 347 }
348
349 /* execute followup routine */
350 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
351 break;
352
353 case DELETE:
354 /* see if row already exists */
355 if (v->field) {
356 status = validate_row(q, Argv, v);
357 if (status != MR_EXISTS) break;
358 }
359
360 /* build "where" clause and perform delete */
361 /* if q->rvar = NULL, perform post_rtn only */
362 if (q->rvar) {
363 build_qual(q->qual, q->argc, Argv, qual);
048b1b94 364 table_name = q->rtable;
365 incremental_before(q->rtable, qual, argv_ro);
b070f8a1 366 status = do_delete(q, qual, action, actarg);
367 incremental_clear_after();
368 if (status != MR_SUCCESS) break;
369 flush_name(argv_ro[0], q->rtable);
048b1b94 370 EXEC SQL UPDATE tblstats
371 SET deletes = deletes + 1, modtime = 'now'
372 WHERE table_name = :table_name;
b070f8a1 373 }
374
375 /* execute followup routine */
376 if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
377 break;
378
379 }
380
381out:
382 if (status == MR_SUCCESS && ingres_errno != 0) {
383 com_err(whoami, MR_INTERNAL, "Server didn't notice INGRES ERROR %d",
384 ingres_errno);
385 status = mr_errcode;
386 }
387
048b1b94 388 if (q->type == RETRIEVE) {
048b1b94 389 EXEC SQL COMMIT WORK;
048b1b94 390 } else {
b070f8a1 391 if (status == MR_SUCCESS) {
048b1b94 392 EXEC SQL COMMIT WORK;
b070f8a1 393 if (journal) {
394 char buf[1024], *bp;
395 int i;
396 extern time_t now;
397
398 fprintf(journal, "%% %s %s %s",
399 cl->clname, cl->entity, ctime(&now));
400 fprintf(journal, "%s[%d] ", q->name, cl->args->mr_version_no);
401 for (i = 0; i < argc; i++) {
402 if (i != 0) {
403 putc(' ', journal);
404 }
405 requote(buf, argv_ro[i], sizeof(buf));
406 fputs(buf, journal);
407 }
408 putc('\n', journal);
409 fflush(journal);
410 }
411 incremental_update();
412 } else {
1a41acb7 413 cache_abort();
b070f8a1 414 if (ingres_errno != INGRES_DEADLOCK) {
048b1b94 415 EXEC SQL ROLLBACK WORK;
b070f8a1 416 }
417 incremental_flush();
418 }
419 }
1a41acb7 420 cache_commit(); /* commit following abort is safe */
b070f8a1 421
422 if (status != MR_SUCCESS && log_flags & LOG_RES)
423 com_err(whoami, status, " (Query failed)");
424 return(status);
425}
426
427build_qual(fmt, argc, argv, qual)
428 char *fmt;
429 int argc;
430 char *argv[];
431 char *qual;
432{
433 register char *c;
434 register int i;
435 char *args[4];
436 char *index();
437
438 c = fmt;
439 for (i = 0; i < argc; i++) {
440 c = index(c, '%');
441 if (c++ == (char *)0) return(MR_ARGS);
442 if (*c == 's')
443 args[i] = argv[i];
444 else if (*c == 'd')
445 *(int *)&args[i] = *(int *)argv[i]; /* sigh */
446 else
447 return(MR_INGRES_ERR);
448 }
1a41acb7 449 if (c = index(c, '%')) {
450 args[i] = args[i - 1];
451 }
b070f8a1 452
453 switch (argc) {
454 case 0:
455 strcpy(qual, fmt);
456 break;
457
458 case 1:
459 sprintf(qual, fmt, args[0]);
460 break;
461
462 case 2:
463 sprintf(qual, fmt, args[0], args[1]);
464 break;
465
466 case 3:
467 sprintf(qual, fmt, args[0], args[1], args[2]);
468 break;
469
470 case 4:
471 sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
472 break;
1a41acb7 473
474 default:
475 com_err(whoami, MR_INTERNAL,
476 "Internal arg count error processing query");
477 return(MR_INTERNAL);
b070f8a1 478 }
479 return(MR_SUCCESS);
480}
481
482char *
483build_sort(v, sort)
484 register struct validate *v;
485 char *sort;
486{
487 register struct valobj *vo;
488 register int n;
489 char elem[16];
490
491 n = v->objcnt;
492 vo = v->valobj;
493 *sort = 0;
494
495 while (--n >= 0) {
496 if (vo->type == V_SORT) {
048b1b94 497 sprintf(elem, "%d", vo->index + 1); /* Result column number */
b070f8a1 498 if (*sort) strcat(sort, ", ");
499 strcat(sort, elem);
500 }
501 vo++;
502 }
503
504 return ((*sort) ? sort : 0);
505}
506
507
508/* Build arguement vector, verify query and arguments */
509
510mr_verify_query(cl, q, argc, argv_ro)
511 client *cl;
512 struct query *q;
513 int argc;
514 char *argv_ro[];
515{
516 register int argreq;
517 register int status;
518 register struct validate *v = q->validate;
519 register int i;
520 register int privileged = 0;
99e09b48 521 register char *to,*fr,*stop;
b070f8a1 522
523 /* copy the arguments into a local argv that we can modify */
524 if (argc >= QMAXARGS)
525 return(MR_ARGS);
526 for (i = 0; i < argc; i++) {
99e09b48 527 /* Single quotes must be doubled for SQL */
528 for (to=Argv[i], fr=argv_ro[i], stop=to+ARGLEN; (*fr) && (to<stop);) {
529 if(*fr=='\'')
530 *to++ = *fr;
531 *to++ = *fr++;
532 }
533 if (*fr)
534 return(MR_ARG_TOO_LONG);
535 *to='\0';
536
537 if (*--to == '\\')
b070f8a1 538 return(MR_BAD_CHAR);
539 }
540
541 /* check initial query access */
542 status = check_query_access(q, Argv, cl);
543 if (status != MR_SUCCESS && status != MR_PERM)
544 return(status);
545 if (status == MR_SUCCESS)
546 privileged++;
547
548 /* check argument count */
549 argreq = q->argc;
550 if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
551 if (argc != argreq) return(MR_ARGS);
552
553 /* validate arguments */
554 if (v && v->valobj) {
555 status = validate_fields(q, Argv, v->valobj, v->objcnt);
556 if (status != MR_SUCCESS) return(status);
557 }
558
559 /* perform special query access check */
560 if (!privileged && v && v->acs_rtn) {
561 status = (*v->acs_rtn)(q, Argv, cl);
562 if (status != MR_SUCCESS && status != MR_PERM)
563 return(status);
564 if (status == MR_SUCCESS)
565 privileged++;
566 }
567
568 return(privileged ? MR_SUCCESS : MR_PERM);
569}
570
571
572/* This routine caches info from the database. Each query acl is stored
573 * in the query structure, and whether that acl contains everybody.
574 */
575
576check_query_access(q, argv, cl)
577 struct query *q;
578 char *argv[];
579 client *cl;
048b1b94 580{
581 EXEC SQL BEGIN DECLARE SECTION;
582 char *name;
583 int acl_id;
584 int rowcount;
585 int errorno;
586 static int def_uid;
587 EXEC SQL END DECLARE SECTION;
b070f8a1 588 int status;
589 int client_id;
590 char *client_type;
591
592 /* initialize default uid */
593 if (def_uid == 0) {
048b1b94 594 EXEC SQL SELECT users_id INTO :def_uid FROM users WHERE login='default';
b070f8a1 595 }
596
597 /* get query access control list */
598 if (q->acl != 0)
599 acl_id = q->acl;
600 else {
601 name = q->shortname;
048b1b94 602 EXEC SQL SELECT list_id INTO :acl_id FROM capacls WHERE tag = :name;
603 if (sqlca.sqlcode < 0) return(MR_INGRES_ERR);
604 if (sqlca.sqlcode == 100) return(MR_PERM);
b070f8a1 605 q->acl = acl_id;
606
607 /* check for default access */
048b1b94 608 EXEC SQL SELECT member_id INTO :acl_id FROM imembers
609 WHERE list_id = :acl_id AND member_type = 'USER'
610 AND member_id = :def_uid;
611 if (sqlca.sqlerrd[2] == 0)
612 q->everybody = 0;
613 else
614 q->everybody = 1;
b070f8a1 615 }
616
617 if (q->everybody)
618 return(MR_SUCCESS);
619
620 if (get_client(cl, &client_type, &client_id) != MR_SUCCESS)
621 return(MR_PERM);
622 if (find_member("LIST", acl_id, client_type, client_id, 0))
623 return(MR_SUCCESS);
624 else
625 return(MR_PERM);
048b1b94 626}
b070f8a1 627
628
629get_client(cl, client_type, client_id)
630 client *cl;
631 char **client_type;
632 int *client_id;
633{
634 if (cl->users_id > 0) {
635 *client_id = cl->users_id;
636 *client_type = "USER";
637 return(MR_SUCCESS);
638 }
639
640 if (cl->client_id < 0) {
641 *client_id = -cl->users_id;
642 *client_type = "KERBEROS";
643 return(MR_SUCCESS);
644 }
645
646 return(MR_PERM);
647}
648
048b1b94 649find_member(list_type, list_id, member_type, member_id)
1a41acb7 650 char *list_type;
048b1b94 651 EXEC SQL BEGIN DECLARE SECTION;
652 int list_id;
653 char *member_type;
654 int member_id;
655 EXEC SQL END DECLARE SECTION;
656{
657 EXEC SQL BEGIN DECLARE SECTION;
658 int flag, errorno;
659 EXEC SQL END DECLARE SECTION;
b070f8a1 660
661 if (!strcmp(strtrim(list_type), strtrim(member_type)) &&
662 list_id == member_id)
663 return(1);
664
665 /* see if client is a direct member of list */
048b1b94 666 flag = 0;
667 EXEC SQL SELECT member_id INTO :flag FROM imembers
668 WHERE list_id = :list_id AND member_type = :member_type
669 AND member_id = :member_id;
670 if(flag!=0) flag=1; /** Not strictly necessary */
671 if (sqlca.sqlcode == 0)
672 return(flag);
673 return(0);
674}
b070f8a1 675
676
048b1b94 677do_retrieve(q, pqual, psort, action, actarg)
b070f8a1 678 register struct query *q;
048b1b94 679EXEC SQL BEGIN DECLARE SECTION;
b070f8a1 680 char *pqual;
681 char *psort;
048b1b94 682EXEC SQL END DECLARE SECTION;
b070f8a1 683 int (*action)();
684 char *actarg;
048b1b94 685{
b070f8a1 686 static char **vaddrs = (char **)NULL;
c9a214e4 687 int j, rowcount;
b070f8a1 688
689 if (!vaddrs) {
690 register int i;
691
692 if ((vaddrs = (char **)malloc(sizeof(char *) * QMAXARGS)) == NULL) {
693 com_err(whoami, MR_NO_MEM, "setting up static argv");
694 exit(1);
695 }
45bf7573 696 for (i = 0; i < QMAXARGS; i++) {
c9a214e4 697 vaddrs[i]=SQLDA->sqlvar[i].sqldata;
b070f8a1 698 }
699 }
700
c9a214e4 701 build_sql_stmt(stmt_buf,"SELECT",q->tlist,vaddrs,pqual);
048b1b94 702 if(psort) { strcat(stmt_buf," ORDER BY "); strcat(stmt_buf,psort); }
c9a214e4 703 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
99e09b48 704 if(sqlca.sqlcode)
705 return(MR_INTERNAL);
048b1b94 706 EXEC SQL DECLARE csr001 CURSOR FOR stmt;
707 EXEC SQL OPEN csr001;
c9a214e4 708 rowcount = 0;
048b1b94 709 while(1) {
710 EXEC SQL FETCH csr001 USING DESCRIPTOR :SQLDA;
711 if(sqlca.sqlcode != 0) break;
712 (*action)(q->vcnt, vaddrs, actarg);
c9a214e4 713 rowcount++;
b070f8a1 714 }
048b1b94 715 EXEC SQL CLOSE csr001;
b070f8a1 716
717 if (mr_errcode) return(mr_errcode);
c9a214e4 718 return ((rowcount == 0) ? MR_NO_MATCH : MR_SUCCESS);
048b1b94 719}
720
cc0088db 721char *sqlstrstr(str,pat)
722 char *str;
723 char *pat;
724{
725 register char *p=pat;
726
727 do {
728 if(*str=='\'') { /* Skip over single-quote delimited substrings */
729 while(*++str && (*str!='\''))
730 ;
731 continue;
732 }
733 if(*str==*p) {
734 register char *s;
735 s=str;
736 while(*++p && (*++s==*p))
737 ;
738 if(*p) p=pat; /* failed */
739 }
740 } while(*p && *++str);
741
742 if(!*str) str=NULL;
743 return(str);
744}
745
746void optimize_sql_stmt(buf)
747char *buf;
748{
749 char *point=buf, *pat, *eopat, *esc1, *esc2, *csr;
750
751 for(point=buf; point=sqlstrstr(point,"LIKE"); point++) {
752 /* Now pointing to string "LIKE" */
753
754 /* Look at next word */
755 for(pat=point+4; *pat==' '; pat++) ;
756
757 /* Is it a single-quote delimited string? */
758 if(*pat!='\'') continue;
759
760 /* look for "escape" clause - save escape character */
761 /* 1. Find end of pattern */
762 for(eopat=pat+1; 1; eopat++) {
763 if(*eopat=='\'') {
764 if(eopat[1]=='\'') /* single-quote is self-escaping */
765 eopat++;
766 else
767 break;
768 }
769 }
770
771 /* 2. Look at next word */
772 for(esc1=eopat; *++esc1==' ';) ;
773
774 /* 3. esc1=0 if not "ESCAPE '?'", where the ? may be any character. */
775 if(strncmp(esc1,"ESCAPE",6)) esc1=NULL;
776
777 if(esc1) {
778 for(esc2=esc1+6; *esc2==' '; esc2++) ;
779
780 if(*esc2++!='\'') continue; /* Bad SQL syntax. Skip. */
781 /* esc2 now points at the escape character itself */
782 if(esc2[1]!='\'') continue; /* Weird escape string. Skip. */
783 } else {
784 esc2="\\";
785 }
786
787 /* Is pattern free from special characters? */
788 for(csr=pat; csr<eopat; csr++)
789 if((*csr=='%') || (*csr=='_') || (*csr==*esc2)) break;
790 if(csr!=eopat) continue; /* Uses pattern matching. Skip. */
791
792 /* Optimize the query statement */
793 /* 1. Change "LIKE" to " = " */
794 memcpy(point," = ",4);
795
796 /* 2. Change "ESCAPE" to " " */
797 if(esc1) {
798 memset(esc1,' ',6);
799 /* 3. Change "'*'" to " " */
800 /* (Changes '''' to " ") */
801 if(esc2) memset(esc2-1,' ',(*esc2=='\'')?4:3);
802 }
803 }
804}
805
048b1b94 806build_sql_stmt(result_buf,cmd,targetlist,argv,qual)
807 char *result_buf;
808 char *cmd;
809 char *targetlist;
810 char *argv[];
c9a214e4 811 char *qual;
048b1b94 812{
813 char fmt_buf[MR_STMTBUF_LEN];
99e09b48 814 register char *res, *fmt;
815
816 if(qual)
817 sprintf(fmt_buf,"%s %s WHERE %s",cmd,targetlist,qual);
818 else
819 sprintf(fmt_buf,"%s %s",cmd,targetlist);
820
821 for(res=result_buf, fmt=fmt_buf; *fmt; fmt++) {
822 if(*fmt=='%') {
823 if(*++fmt) {
824 switch(*fmt) {
825 case '%': /* %% -> % */
826 *res++ = *fmt;
827 break;
828 case 's':
829 if(*argv[0]) {
830 *res='\0';
831 strcat(res,*argv);
832 while(*++res) ;
833 }
834 argv++;
835 break;
836 case 'd':
837 sprintf(res,"%d",*(int *)*argv++); /* print to result buffer */
838 while(*++res) ;
839 break;
840 default: /* Swallow other %? pairs */
841 break;
842 }
843 } else break;
844 } else *res++ = *fmt; /* text -> result buffer */
048b1b94 845 }
846 *res='\0';
cc0088db 847
848 optimize_sql_stmt(result_buf);
048b1b94 849}
b070f8a1 850
851do_update(q, argv, qual, action, actarg)
852 register struct query *q;
853 char *argv[];
854 char *qual;
855 int (*action)();
856 char *actarg;
048b1b94 857{
858 build_sql_stmt(stmt_buf,"UPDATE",q->tlist,argv,qual);
c9a214e4 859 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
b070f8a1 860 if (mr_errcode) return(mr_errcode);
861 return(MR_SUCCESS);
048b1b94 862}
b070f8a1 863
864do_append(q, argv, pqual, action, actarg)
865 register struct query *q;
866 char *argv[];
867 char *pqual;
868 int (*action)();
869 char *actarg;
048b1b94 870{
871 build_sql_stmt(stmt_buf,"INSERT",q->tlist,argv,pqual);
c9a214e4 872 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
b070f8a1 873 if (mr_errcode) return(mr_errcode);
874 return(MR_SUCCESS);
048b1b94 875}
b070f8a1 876
877do_delete(q, qual, action, actarg)
878 register struct query *q;
879 char *qual;
880 int (*action)();
881 char *actarg;
048b1b94 882{
883 sprintf(stmt_buf,"DELETE FROM %s WHERE %s",q->rtable,qual);
884 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
b070f8a1 885 if (mr_errcode) return(mr_errcode);
886 return(MR_SUCCESS);
048b1b94 887}
b070f8a1 888
889
890/**
891 ** set_next_object_id - set next object id in values table
892 **
893 ** Inputs: object - object name in values table and in objects
894 ** table - name of table objects are found in
1a41acb7 895 ** limit - should the ID be range limited
b070f8a1 896 **
897 ** - called before an APPEND operation to set the next object id to
898 ** be used for the new record to the next free value
899 **
900 **/
901
048b1b94 902set_next_object_id(object, table_name, limit)
903 EXEC SQL BEGIN DECLARE SECTION;
1a41acb7 904 char *object;
048b1b94 905 char *table_name;
1a41acb7 906 int limit;
048b1b94 907 EXEC SQL END DECLARE SECTION;
908{
909 EXEC SQL BEGIN DECLARE SECTION;
c9a214e4 910 int value;
048b1b94 911 EXEC SQL END DECLARE SECTION;
45bf7573 912 int starting_value;
913
048b1b94 914 EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :object;
915 if (sqlca.sqlerrd[2] != 1)
1a41acb7 916 return(MR_NO_ID);
917
45bf7573 918 starting_value=value;
c9a214e4 919 while (1) {
45bf7573 920 if (limit && value > MAX_ID_VALUE)
921 value = MIN_ID_VALUE;
922
923 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s=%d",object,table_name,object,value);
924 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
99e09b48 925 if(sqlca.sqlcode)
926 return(MR_INTERNAL);
45bf7573 927 EXEC SQL DECLARE csr002 CURSOR FOR stmt;
c9a214e4 928 EXEC SQL OPEN csr002;
45bf7573 929 EXEC SQL FETCH csr002 USING DESCRIPTOR :SQLDA;
930 if (sqlca.sqlcode < 0) return(mr_errcode);
931 if (sqlca.sqlcode == 100) break;
932
c9a214e4 933 EXEC SQL CLOSE csr002;
45bf7573 934 value++;
935 if (limit && value == starting_value) {
936 com_err(whoami,0,"All id values have been used");
937 return(MR_NO_ID);
938 }
b070f8a1 939 }
c9a214e4 940 EXEC SQL CLOSE csr002;
b070f8a1 941
942 if (LOG_RES)
c9a214e4 943 com_err(whoami, 0, "setting ID %s to %d", object, value);
048b1b94 944 EXEC SQL UPDATE numvalues SET value = :value WHERE name = :object;
b070f8a1 945 return(MR_SUCCESS);
048b1b94 946}
b070f8a1 947
948
949/* Turn a kerberos name into the user's ID of the account that principal
950 * owns. Sets the kerberos ID and user ID.
951 */
952
953int set_krb_mapping(name, login, ok, kid, uid)
954char *name;
955char *login;
956int ok;
957int *kid;
958int *uid;
048b1b94 959{
960 EXEC SQL BEGIN DECLARE SECTION;
961 int u_id, k_id;
962 char *krbname;
963 EXEC SQL END DECLARE SECTION;
b070f8a1 964
965 krbname = name;
966 *kid = 0;
967 *uid = 0;
968
fbeb0263 969 EXEC SQL SELECT km.users_id, km.string_id INTO :u_id, :k_id
970 FROM krbmap km, strings str
971 WHERE km.string_id = str.string_id AND str.string = :krbname;
048b1b94 972
b070f8a1 973 if (ingres_errno) return(mr_errcode);
974
048b1b94 975 if (sqlca.sqlerrd[2] == 1) { /* rowcount */
b070f8a1 976 *kid = -k_id;
977 *uid = u_id;
978 return(MR_SUCCESS);
979 }
980
981 if (name_to_id(name, "STRINGS", &k_id) == MR_SUCCESS)
982 *kid = -k_id;
983
048b1b94 984 if (!ok) {
b070f8a1 985 *uid = *kid;
986 return(MR_SUCCESS);
987 }
988
989 if (name_to_id(login, "USERS", uid) != MR_SUCCESS)
990 *uid = 0;
991
992 if (*kid == 0)
993 *kid = *uid;
994 if (ingres_errno) return(mr_errcode);
995 return(MR_SUCCESS);
048b1b94 996}
b070f8a1 997
998
999/* For now this just checks the argc's. It should also see that there
1000 * are no duplicate names.
1001 */
1002
1003sanity_check_queries()
1004{
1005 register int i;
1006 int maxv = 0, maxa = 0;
1007#ifdef MULTIPROTOCOLS
1008 extern int QueryCount1, QueryCount2;
1009 extern struct query Queries1[], Queries2[];
1010#else
1011 extern int QueryCount2;
1012 extern struct query Queries2[];
1013#endif MULTIPROTOCOLS
1014
1015#define MAX(x,y) ((x) > (y) ? (x) : (y))
1016
1017#ifdef MULTIPROTOCOLS
1018 for (i = 0; i < QueryCount1; i++) {
1019 maxv = MAX(maxv, Queries1[i].vcnt);
1020 maxa = MAX(maxa, Queries1[i].argc);
1021 }
1022#endif MULTIPROTOCOLS
1023 for (i = 0; i < QueryCount2; i++) {
1024 maxv = MAX(maxv, Queries2[i].vcnt);
1025 maxa = MAX(maxa, Queries2[i].argc);
1026 }
1027 if (MAX(maxv, maxa) > QMAXARGS) {
1028 com_err(whoami, 0, "A query has more args than QMAXARGS");
1029 exit(1);
1030 }
1031}
048b1b94 1032
1033/* eof:qrtn.dc */
This page took 0.350209 seconds and 5 git commands to generate.