6 * Copyright (C) 1987 by the Massachusetts Institute of Technology
7 * For copying and distribution information, please see the file
13 static char *rcsid_qsupport_dc = "$Header$";
16 #include <mit-copyright.h>
19 #include "mr_server.h"
21 EXEC SQL INCLUDE sqlca;
22 EXEC SQL INCLUDE sqlda;
26 extern int ingres_errno, mr_errcode;
28 EXEC SQL BEGIN DECLARE SECTION;
29 extern char stmt_buf[];
30 EXEC SQL END DECLARE SECTION;
32 EXEC SQL WHENEVER SQLERROR CALL ingerr;
34 #ifdef _DEBUG_MALLOC_INC
36 #define dbg_index(str1,c) DBindex(__FILE__, __LINE__, str1, c)
38 #define dbg_index index
41 /* Validation Routines */
43 validate_row(q, argv, v)
44 register struct query *q;
46 register struct validate *v;
48 EXEC SQL BEGIN DECLARE SECTION;
52 EXEC SQL END DECLARE SECTION;
54 /* build where clause */
55 build_qual(v->qual, v->argc, argv, qual);
57 if (log_flags & LOG_VALID)
58 /* tell the logfile what we're doing */
59 com_err(whoami, 0, "validating row: %s", qual);
61 /* look for the record */
62 sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",q->rtable,qual);
63 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
66 EXEC SQL DECLARE csr126 CURSOR FOR stmt;
68 EXEC SQL FETCH csr126 USING DESCRIPTOR :SQLDA;
69 EXEC SQL CLOSE csr126;
70 rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
72 if (ingres_errno) return(mr_errcode);
73 if (rowcount == 0) return(MR_NO_MATCH);
74 if (rowcount > 1) return(MR_NOT_UNIQUE);
78 validate_fields(q, argv, vo, n)
80 register char *argv[];
81 register struct valobj *vo;
89 if (log_flags & LOG_VALID)
90 com_err(whoami, 0, "validating %s in %s: %s",
91 vo->namefield, vo->table, argv[vo->index]);
92 status = validate_name(argv, vo);
96 if (log_flags & LOG_VALID)
97 com_err(whoami, 0, "validating %s in %s: %s",
98 vo->idfield, vo->table, argv[vo->index]);
99 status = validate_id(q, argv, vo);
103 if (log_flags & LOG_VALID)
104 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
105 status = validate_date(argv, vo);
109 if (log_flags & LOG_VALID)
110 com_err(whoami, 0, "validating %s type: %s",
111 vo->table, argv[vo->index]);
112 status = validate_type(argv, vo);
116 if (log_flags & LOG_VALID)
117 com_err(whoami, 0, "validating typed data (%s): %s",
118 argv[vo->index - 1], argv[vo->index]);
119 status = validate_typedata(q, argv, vo);
123 if (log_flags & LOG_VALID)
124 com_err(whoami, 0, "validating rename %s in %s",
125 argv[vo->index], vo->table);
126 status = validate_rename(argv, vo);
130 if (log_flags & LOG_VALID)
131 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
132 status = validate_chars(argv[vo->index]);
140 status = lock_table(vo);
144 status = readlock_table(vo);
147 status = convert_wildcards(argv[vo->index]);
151 status = convert_wildcards_uppercase(argv[vo->index]);
156 if (status != MR_EXISTS){
157 com_err(whoami,0,"validation failed type=%ld, code=%ld\n",vo->type, status);
163 if (ingres_errno) return(mr_errcode);
168 /* validate_chars: verify that there are no illegal characters in
169 * the string. Legal characters are printing chars other than
170 * ", *, ?, \, [ and ].
172 static int illegalchars[] = {
173 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
175 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
181 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
182 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
183 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
184 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
185 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
186 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
187 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
195 if (illegalchars[*s++])
201 validate_id(q, argv, vo)
204 register struct valobj *vo;
206 EXEC SQL BEGIN DECLARE SECTION;
207 char *name, *tbl, *namefield, *idfield;
209 EXEC SQL END DECLARE SECTION;
213 name = argv[vo->index];
215 namefield = vo->namefield;
216 idfield = vo->idfield;
218 if ((!strcmp(tbl, "users") && !strcmp(namefield, "login")) ||
219 !strcmp(tbl, "machine") ||
220 !strcmp(tbl, "subnet") ||
221 !strcmp(tbl, "filesys") ||
222 !strcmp(tbl, "list") ||
223 !strcmp(tbl, "cluster") ||
224 !strcmp(tbl, "strings")) {
225 if (!strcmp(tbl, "machine") || !strcmp(tbl, "subnet"))
226 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
227 status = name_to_id(name, tbl, &id);
229 *(int *)argv[vo->index] = id;
231 } else if (status == MR_NO_MATCH && !strcmp(tbl, "strings") &&
232 (q->type == APPEND || q->type == UPDATE)) {
234 cache_entry(name, "STRING", id);
235 *(int *)argv[vo->index] = id;
237 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
243 if (!strcmp(namefield, "uid")) {
244 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = %s",idfield,tbl,namefield,name);
246 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",idfield,tbl,namefield,name);
248 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
251 EXEC SQL DECLARE csr127 CURSOR FOR stmt;
252 EXEC SQL OPEN csr127;
254 EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
255 if(sqlca.sqlcode == 0) {
257 EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
258 if(sqlca.sqlcode == 0) rowcount++;
260 EXEC SQL CLOSE csr127;
264 if (rowcount != 1) return(vo->error);
265 bcopy(SQLDA->sqlvar[0].sqldata,argv[vo->index],sizeof(int));
269 validate_name(argv, vo)
271 register struct valobj *vo;
273 EXEC SQL BEGIN DECLARE SECTION;
274 char *name, *tbl, *namefield;
276 EXEC SQL END DECLARE SECTION;
279 name = argv[vo->index];
281 namefield = vo->namefield;
282 if (!strcmp(tbl, "servers") && !strcmp(namefield, "name")) {
283 for (c = name; *c; c++)
287 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
288 tbl,tbl,namefield,name);
289 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
292 EXEC SQL DECLARE csr128 CURSOR FOR stmt;
293 EXEC SQL OPEN csr128;
294 EXEC SQL FETCH csr128 USING DESCRIPTOR :SQLDA;
295 rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
296 EXEC SQL CLOSE csr128;
298 if (ingres_errno) return(mr_errcode);
299 return ((rowcount == 1) ? MR_EXISTS : vo->error);
302 validate_date(argv, vo)
306 EXEC SQL BEGIN DECLARE SECTION;
310 EXEC SQL END DECLARE SECTION;
312 idate = argv[vo->index];
313 EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
315 if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
320 validate_rename(argv, vo)
324 EXEC SQL BEGIN DECLARE SECTION;
325 char *name, *tbl, *namefield, *idfield;
327 EXEC SQL END DECLARE SECTION;
331 c = name = argv[vo->index];
333 if (illegalchars[*c++])
336 /* minor kludge to upcasify machine names */
337 if (!strcmp(tbl, "machine"))
338 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
339 namefield = vo->namefield;
340 idfield = vo->idfield;
343 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
345 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = LEFT('%s',SIZE(%s))",
346 namefield,tbl,namefield,name,namefield);
347 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
350 EXEC SQL DECLARE csr129 CURSOR FOR stmt;
351 EXEC SQL OPEN csr129;
352 EXEC SQL FETCH csr129 USING DESCRIPTOR :SQLDA;
353 if(sqlca.sqlcode == 0) id=1; else id=0;
354 EXEC SQL CLOSE csr129;
356 if (ingres_errno) return(mr_errcode);
362 status = name_to_id(name, tbl, &id);
363 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
370 validate_type(argv, vo)
372 register struct valobj *vo;
374 EXEC SQL BEGIN DECLARE SECTION;
378 EXEC SQL END DECLARE SECTION;
381 typename = vo->table;
382 c = val = argv[vo->index];
384 if (illegalchars[*c++])
388 /* uppercase type fields */
389 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
391 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
392 WHERE name = :typename AND type='TYPE' AND trans = :val;
393 if (ingres_errno) return(mr_errcode);
394 return (cnt ? MR_EXISTS : vo->error);
397 /* validate member or type-specific data field */
399 validate_typedata(q, argv, vo)
400 register struct query *q;
401 register char *argv[];
402 register struct valobj *vo;
404 EXEC SQL BEGIN DECLARE SECTION;
409 EXEC SQL END DECLARE SECTION;
413 /* get named object */
414 name = argv[vo->index];
416 /* get field type string (known to be at index-1) */
417 field_type = argv[vo->index-1];
419 /* get corresponding data type associated with field type name */
420 EXEC SQL SELECT trans INTO :data_type FROM alias
421 WHERE name = :field_type AND type='TYPEDATA';
422 if (ingres_errno) return(mr_errcode);
423 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
425 /* now retrieve the record id corresponding to the named object */
426 if (dbg_index(data_type, ' '))
427 *dbg_index(data_type, ' ') = 0;
428 if (!strcmp(data_type, "user")) {
430 if (dbg_index(name, '@'))
432 status = name_to_id(name, data_type, &id);
433 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
435 if (status) return(status);
436 } else if (!strcmp(data_type, "list")) {
438 status = name_to_id(name, data_type, &id);
439 if (status && status == MR_NOT_UNIQUE)
441 if (status == MR_NO_MATCH) {
442 /* if idfield is non-zero, then if argv[0] matches the string
443 * that we're trying to resolve, we should get the value of
444 * numvalues.[idfield] for the id.
446 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
447 set_next_object_id(q->validate->object_id, q->rtable, 0);
449 EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
451 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
454 } else if (status) return(status);
455 } else if (!strcmp(data_type, "machine")) {
457 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
458 status = name_to_id(name, data_type, &id);
459 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
461 if (status) return(status);
462 } else if (!strcmp(data_type, "string")) {
464 status = name_to_id(name, data_type, &id);
465 if (status && status == MR_NOT_UNIQUE)
467 if (status == MR_NO_MATCH) {
468 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
470 cache_entry(name, "STRING", id);
471 } else if (status) return(status);
472 } else if (!strcmp(data_type, "none")) {
478 /* now set value in argv */
479 *(int *)argv[vo->index] = id;
485 /* Lock the table named by the validation object */
490 sprintf(stmt_buf,"UPDATE %s SET modtime='now' WHERE %s.%s = 0",
491 vo->table,vo->table,vo->idfield);
492 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
493 if (sqlca.sqlcode == 100){
494 fprintf(stderr,"readlock_table: no matching rows found for %s\n",
500 if (sqlca.sqlerrd[2] != 1)
507 * Get a read lock on the table by accessing the magic lock
508 * record. Certain tables are constructed so that they contain
509 * an id field whose value is zero and a modtime field. We
510 * manipulate the modtime field of the id 0 record to effect
511 * locking of the table
517 EXEC SQL BEGIN DECLARE SECTION;
521 EXEC SQL END DECLARE SECTION;
525 sprintf(buf,"SELECT %s FROM %s WHERE %s.%s = 0",
526 vo->idfield, vo->table, vo->table, vo->idfield);
527 EXEC SQL PREPARE stmt FROM :buf;
528 EXEC SQL DESCRIBE stmt INTO SQLDA;
529 EXEC SQL DECLARE rcsr CURSOR FOR stmt;
531 EXEC SQL FETCH rcsr USING DESCRIPTOR :SQLDA;
532 /* Check for no matching rows found - this is
533 * flagged as an internal error since the table should
534 * have a magic lock record.
536 if (sqlca.sqlcode == 100){
538 com_err(whoami,0,"readlock_table: no matching rows found for %s\n",
547 return(MR_EXISTS); /* validate_fields expects us to return
548 * this value if everything went okay
552 /* Check the database at startup time. For now this just resets the
553 * inprogress flags that the DCM uses.
556 sanity_check_database()
561 /* Dynamic SQL support routines */
562 MR_SQLDA_T *mr_alloc_SQLDA()
565 short *null_indicators;
568 if((it=(MR_SQLDA_T *)malloc(sizeof(MR_SQLDA_T)))==NULL) {
569 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
573 if((null_indicators=(short *)calloc(QMAXARGS,sizeof(short)))==NULL) {
574 com_err(whoami, MR_NO_MEM, "setting up SQLDA null indicators");
578 for(j=0; j<QMAXARGS; j++) {
579 if((it->sqlvar[j].sqldata=(char *)malloc(sizeof(short)+ARGLEN))==NULL) {
580 com_err(whoami, MR_NO_MEM, "setting up SQLDA variables");
583 it->sqlvar[j].sqllen=ARGLEN;
584 it->sqlvar[j].sqlind=null_indicators+j;
585 null_indicators[j]=0;
592 /* Use this after FETCH USING DESCRIPTOR one or more
593 * result columns may contain NULLs. This routine is
594 * not currently needed, since db/schema creates all
595 * columns with a NOT NULL WITH DEFAULT clause.
597 * This is currently dead flesh, since no Moira columns
598 * allow null values; all use default values.
600 mr_fix_nulls_in_SQLDA(da)
603 register IISQLVAR *var;
607 for(j=0, var=da->sqlvar; j<da->sqld; j++, var++) {
608 switch(var->sqltype) {
615 intp=(int *)var->sqldata;
623 /* Convert normal Unix-style wildcards to SQL voodoo */
624 convert_wildcards(arg)
627 static char buffer[ARGLEN];
628 register char *s, *d;
630 for(d=buffer,s=arg;*s;s++) {
632 case '*': *d++='%'; *d++='%'; break;
633 case '?': *d++='_'; break;
636 case ']': *d++='*'; *d++ = *s; break;
637 case '%': *d++='*'; *d++='%'; *d++='%'; break;
638 default: *d++ = *s; break;
643 /* Copy back into argv */
649 /* This version includes uppercase conversion, for things like gmac.
650 * This is necessary because "LIKE" doesn't work with "uppercase()".
651 * Including it in a wildcard routine saves making two passes over
652 * the argument string.
654 convert_wildcards_uppercase(arg)
657 static char buffer[ARGLEN];
658 register char *s, *d;
660 for(d=buffer,s=arg;*s;s++) {
662 case '*': *d++='%'; *d++='%'; break;
663 case '?': *d++='_'; break;
666 case ']': *d++='*'; *d++ = *s; break;
667 case '%': *d++='*'; *d++='%'; *d++='%'; break;
668 default: *d++=toupper(*s); break; /* This is the only diff. */
673 /* Copy back into argv */
680 /* Looks like it's time to build an abstraction barrier, Yogi */
682 EXEC SQL BEGIN DECLARE SECTION;
684 EXEC SQL END DECLARE SECTION;
688 EXEC SQL PREPARE stmt FROM :stmt;
689 EXEC SQL DESCRIBE stmt INTO :SQLDA;
690 if(SQLDA->sqld==0) /* Not a SELECT */
692 EXEC SQL DECLARE csr CURSOR FOR stmt;
694 EXEC SQL FETCH csr USING DESCRIPTOR :SQLDA;
697 else if((sqlca.sqlcode<0) && mr_errcode)
707 /* Adds a string to the string table. Returns the id number.
711 EXEC SQL BEGIN DECLARE SECTION;
713 EXEC SQL END DECLARE SECTION;
715 EXEC SQL BEGIN DECLARE SECTION;
718 EXEC SQL END DECLARE SECTION;
720 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
722 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
724 /* Use sprintf to get around problem with doubled single quotes */
725 sprintf(buf,"INSERT INTO strings (string_id, string) VALUES (%d, '%s')",id,name);
726 EXEC SQL EXECUTE IMMEDIATE :buf;