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>
18 #include "mr_server.h"
22 EXEC SQL INCLUDE sqlca;
23 EXEC SQL INCLUDE sqlda;
26 extern char *whoami, *table_name[], *sqlbuffer[QMAXARGS];
27 extern int dbms_errno, mr_errcode;
29 EXEC SQL BEGIN DECLARE SECTION;
30 extern char stmt_buf[];
31 EXEC SQL END DECLARE SECTION;
33 int validate_chars(char *argv[], struct valobj *vo);
34 int validate_id(struct query *, char *argv[], struct valobj *vo);
35 int validate_name(char *argv[], struct valobj *vo);
36 int validate_rename(char *argv[], struct valobj *vo);
37 int validate_type(char *argv[], struct valobj *vo);
38 int validate_typedata(struct query *, char *argv[], struct valobj *vo);
39 int validate_len(char *argv[], struct valobj *vo);
40 int validate_num(char *argv[], struct valobj *vo);
41 int lock_table(struct valobj *vo);
42 int readlock_table(struct valobj *vo);
43 int convert_wildcards_uppercase(char *arg);
45 extern SQLDA *sqlald(int,int,int);
47 EXEC SQL WHENEVER SQLERROR DO dbmserr();
49 /* Validation Routines */
51 int validate_row(q, argv, v)
52 register struct query *q;
54 register struct validate *v;
56 EXEC SQL BEGIN DECLARE SECTION;
59 EXEC SQL END DECLARE SECTION;
61 /* build where clause */
62 build_qual(v->qual, v->argc, argv, qual);
64 if (log_flags & LOG_VALID)
65 /* tell the logfile what we're doing */
66 com_err(whoami, 0, "validating row: %s", qual);
68 /* look for the record */
69 sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",
70 table_name[q->rtable],qual);
72 if (dbms_errno) return(mr_errcode);
74 rowcount = atoi(sqlbuffer[0]);
75 if (rowcount == 0) return(MR_NO_MATCH);
76 if (rowcount > 1) return(MR_NOT_UNIQUE);
80 int validate_fields(q, argv, vo, n)
82 register char *argv[];
83 register struct valobj *vo;
91 if (log_flags & LOG_VALID)
92 com_err(whoami, 0, "validating %s in %s: %s",
93 vo->namefield, table_name[vo->table], argv[vo->index]);
94 status = validate_name(argv, vo);
98 if (log_flags & LOG_VALID)
99 com_err(whoami, 0, "validating %s in %s: %s",
100 vo->idfield, table_name[vo->table], argv[vo->index]);
101 status = validate_id(q, argv, vo);
106 if (log_flags & LOG_VALID)
107 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
108 status = validate_date(argv, vo);
113 if (log_flags & LOG_VALID)
114 com_err(whoami, 0, "validating %s type: %s",
115 table_name[vo->table], argv[vo->index]);
116 status = validate_type(argv, vo);
120 if (log_flags & LOG_VALID)
121 com_err(whoami, 0, "validating typed data (%s): %s",
122 argv[vo->index - 1], argv[vo->index]);
123 status = validate_typedata(q, argv, vo);
127 if (log_flags & LOG_VALID)
128 com_err(whoami, 0, "validating rename %s in %s",
129 argv[vo->index], table_name[vo->table]);
130 status = validate_rename(argv, vo);
134 if (log_flags & LOG_VALID)
135 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
136 status = validate_chars(argv, vo);
140 if (log_flags & LOG_VALID)
141 com_err(whoami, 0, "validating length: %s", argv[vo->index]);
142 status = validate_len(argv, vo);
146 if (log_flags & LOG_VALID)
147 com_err(whoami, 0, "validating number: %s", argv[vo->index]);
148 status = validate_num(argv, vo);
152 status = lock_table(vo);
156 status = readlock_table(vo);
160 status = convert_wildcards(argv[vo->index]);
164 status = convert_wildcards_uppercase(argv[vo->index]);
169 if (status != MR_EXISTS) return(status);
173 if (dbms_errno) return(mr_errcode);
178 /* validate_chars: verify that there are no illegal characters in
179 * the string. Legal characters are printing chars other than
180 * ", *, ?, \, [ and ].
182 static int illegalchars[] = {
183 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
184 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
185 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
187 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
188 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
189 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
190 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
191 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
192 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
193 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
194 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
195 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
196 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
197 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
198 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
201 int validate_chars(argv, vo)
203 register struct valobj *vo;
205 char *s=argv[vo->index];
207 EXEC SQL BEGIN DECLARE SECTION;
210 EXEC SQL END DECLARE SECTION;
213 /* check for bad characters */
215 if (illegalchars[*s++])
218 /* check for length */
219 tname = table_name[vo->table];
220 cname = vo->namefield;
221 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
222 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
224 if((strlen(argv[vo->index]) > len) &&
225 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
226 argv[vo->index][len]='\0'; /* truncate */
232 int validate_id(q, argv, vo)
235 register struct valobj *vo;
237 EXEC SQL BEGIN DECLARE SECTION;
238 char *name, *namefield, *idfield;
239 int id, rowcount, tbl;
240 EXEC SQL END DECLARE SECTION;
244 name = argv[vo->index];
246 namefield = vo->namefield;
247 idfield = vo->idfield;
249 if ((tbl==USERS_TABLE && !strcmp(namefield, "login")) ||
250 tbl==MACHINE_TABLE || tbl==SUBNET_TABLE || tbl==FILESYS_TABLE ||
251 tbl==LIST_TABLE || tbl==CLUSTER_TABLE || tbl==STRINGS_TABLE) {
252 if (tbl==MACHINE_TABLE || tbl==SUBNET_TABLE)
253 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
254 status = name_to_id(name, tbl, &id);
256 *(int *)argv[vo->index] = id;
258 } else if (status == MR_NO_MATCH && tbl==STRINGS_TABLE &&
259 (q->type == APPEND || q->type == UPDATE)) {
261 cache_entry(name, STRINGS_TABLE, id);
262 *(int *)argv[vo->index] = id;
264 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
269 /* else, it's `dubu', which uses unix_uid from users */
270 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
271 WHERE unix_uid = :name;
272 if (dbms_errno) return(mr_errcode);
273 if (rowcount != 1) return(vo->error);
274 EXEC SQL SELECT users_id INTO :id FROM users
275 WHERE unix_uid = :name;
276 *(int *)argv[vo->index] = id;
281 int validate_name(argv, vo)
283 register struct valobj *vo;
285 char *name, *namefield;
288 name = argv[vo->index];
289 namefield = vo->namefield;
290 if (vo->table==SERVERS_TABLE && !strcmp(namefield, "name")) {
291 for (c = name; *c; c++)
295 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
296 table_name[vo->table],table_name[vo->table],namefield,name);
299 if (dbms_errno) return(mr_errcode);
300 return ((atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error);
304 validate_date(argv, vo)
308 EXEC SQL BEGIN DECLARE SECTION;
312 EXEC SQL END DECLARE SECTION;
314 idate = argv[vo->index];
315 EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
316 if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
321 int validate_rename(argv, vo)
325 EXEC SQL BEGIN DECLARE SECTION;
326 char *name, *namefield, *idfield;
328 EXEC SQL END DECLARE SECTION;
332 status = validate_chars(argv, vo);
333 if(status != MR_EXISTS) return status;
334 name=argv[vo->index];
335 /* minor kludge to upcasify machine names */
336 if (vo->table == MACHINE_TABLE)
337 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
338 namefield = vo->namefield;
339 idfield = vo->idfield;
342 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
344 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",
345 namefield,table_name[vo->table],namefield,name);
348 if (dbms_errno) return(mr_errcode);
349 if (sqlca.sqlcode==SQL_NO_MATCH)
350 return(MR_EXISTS); /* how's _that_ for intuitive? */
354 status = name_to_id(name, vo->table, &id);
355 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
362 int validate_type(argv, vo)
364 register struct valobj *vo;
366 EXEC SQL BEGIN DECLARE SECTION;
370 EXEC SQL END DECLARE SECTION;
373 typename = vo->namefield;
374 c = val = argv[vo->index];
376 if (illegalchars[*c++])
380 /* uppercase type fields */
381 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
383 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
384 WHERE name = :typename AND type='TYPE' AND trans = :val;
385 if (dbms_errno) return(mr_errcode);
386 return (cnt ? MR_EXISTS : vo->error);
389 /* validate member or type-specific data field */
391 int validate_typedata(q, argv, vo)
392 register struct query *q;
393 register char *argv[];
394 register struct valobj *vo;
396 EXEC SQL BEGIN DECLARE SECTION;
401 EXEC SQL END DECLARE SECTION;
405 /* get named object */
406 name = argv[vo->index];
408 /* get field type string (known to be at index-1) */
409 field_type = argv[vo->index-1];
411 /* get corresponding data type associated with field type name */
412 EXEC SQL SELECT trans INTO :data_type FROM alias
413 WHERE name = :field_type AND type='TYPEDATA';
414 if (dbms_errno) return(mr_errcode);
415 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
417 /* now retrieve the record id corresponding to the named object */
418 if (strchr(data_type, ' '))
419 *strchr(data_type, ' ') = 0;
420 if (!strcmp(data_type, "user")) {
422 if (strchr(name, '@'))
424 status = name_to_id(name, USERS_TABLE, &id);
425 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
427 if (status) return(status);
428 } else if (!strcmp(data_type, "list")) {
430 status = name_to_id(name, LIST_TABLE, &id);
431 if (status && status == MR_NOT_UNIQUE)
433 if (status == MR_NO_MATCH) {
434 /* if idfield is non-zero, then if argv[0] matches the string
435 * that we're trying to resolve, we should get the value of
436 * numvalues.[idfield] for the id.
438 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
439 set_next_object_id(q->validate->object_id, q->rtable, 0);
441 EXEC SQL SELECT value INTO :id FROM numvalues
443 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
446 } else if (status) return(status);
447 } else if (!strcmp(data_type, "machine")) {
449 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
450 status = name_to_id(name, MACHINE_TABLE, &id);
451 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
453 if (status) return(status);
454 } else if (!strcmp(data_type, "string")) {
456 status = name_to_id(name, STRINGS_TABLE, &id);
457 if (status && status == MR_NOT_UNIQUE)
459 if (status == MR_NO_MATCH) {
460 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
462 cache_entry(name, STRINGS_TABLE, id);
463 } else if (status) return(status);
464 } else if (!strcmp(data_type, "none")) {
470 /* now set value in argv */
471 *(int *)argv[vo->index] = id;
477 /* Make sure the data fits in the field */
478 int validate_len(argv, vo)
479 register char *argv[];
480 register struct valobj *vo;
482 EXEC SQL BEGIN DECLARE SECTION;
485 EXEC SQL END DECLARE SECTION;
487 tname = table_name[vo->table];
488 cname = vo->namefield;
489 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
490 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
492 if((strlen(argv[vo->index]) > len) &&
493 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
494 argv[vo->index][len]='\0'; /* truncate */
499 /* Lock the table named by the validation object */
505 sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
506 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
515 * Get a read lock on the table by accessing the magic lock
516 * record. Certain tables are constructed so that they contain
517 * an id field whose value is zero and a modtime field. We
518 * manipulate the modtime field of the id 0 record to effect
519 * locking of the table
522 int readlock_table(vo)
526 sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
527 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
534 return(MR_EXISTS); /* validate_fields expects us to return
535 * this value if everything went okay
539 /* Make sure the data is numeric */
540 int validate_num(argv, vo)
541 register char *argv[];
542 register struct valobj *vo;
544 char *p = argv[vo->index];
548 if (*p < '0' || *p > '9') return MR_INTEGER;
553 /* Check the database at startup time. */
555 void sanity_check_database(void)
557 EXEC SQL BEGIN DECLARE SECTION;
559 EXEC SQL END DECLARE SECTION;
561 /* Sometimes a crash can leave strings_id in numvalues in an
562 incorrect state. Check for that and fix it. */
564 EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name='strings_id';
566 for (id=oid+1; sqlca.sqlcode==0; id++)
567 EXEC SQL SELECT string_id INTO :id FROM strings
571 EXEC SQL UPDATE numvalues SET value=:id-1 WHERE name='strings_id';
575 char *sqlbuffer[QMAXARGS];
577 /* Dynamic SQL support routines */
578 SQLDA *mr_alloc_sqlda()
583 it=sqlald(QMAXARGS, ARGLEN, 0);
585 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
589 for(j=0; j<QMAXARGS; j++) {
590 it->V[j]=sqlbuffer[j]=malloc(ARGLEN);
591 it->T[j]=97; /* 97 = CHARZ = null-terminated string */
599 /* Convert normal Unix-style wildcards to SQL voodoo */
600 int convert_wildcards(arg)
603 static char buffer[ARGLEN];
604 register char *s, *d;
606 for(d=buffer,s=arg;*s;s++) {
608 case '*': *d++='%'; *d++='%'; break;
609 case '?': *d++='_'; break;
610 case '_': *d++='*'; *d++ = *s; break;
611 case '%': *d++='*'; *d++='%'; *d++='%'; break;
612 default: *d++ = *s; break;
617 /* Copy back into argv */
623 /* This version includes uppercase conversion, for things like gmac.
624 * This is necessary because "LIKE" doesn't work with "uppercase()".
625 * Including it in a wildcard routine saves making two passes over
626 * the argument string.
628 int convert_wildcards_uppercase(arg)
631 static char buffer[ARGLEN];
632 register char *s, *d;
634 for(d=buffer,s=arg;*s;s++) {
636 case '*': *d++='%'; *d++='%'; break;
637 case '?': *d++='_'; break;
638 case '_': *d++='*'; *d++ = *s; break;
639 case '%': *d++='*'; *d++='%'; *d++='%'; break;
640 default: *d++=toupper(*s); break; /* This is the only diff. */
645 /* Copy back into argv */
652 /* Adds a string to the string table. Returns the id number.
656 EXEC SQL BEGIN DECLARE SECTION;
658 EXEC SQL END DECLARE SECTION;
660 EXEC SQL BEGIN DECLARE SECTION;
663 EXEC SQL END DECLARE SECTION;
665 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
667 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
669 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);