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 lock_table(struct valobj *vo);
41 int readlock_table(struct valobj *vo);
42 int convert_wildcards_uppercase(char *arg);
44 EXEC SQL WHENEVER SQLERROR DO dbmserr();
46 /* Validation Routines */
48 int validate_row(q, argv, v)
49 register struct query *q;
51 register struct validate *v;
53 EXEC SQL BEGIN DECLARE SECTION;
56 EXEC SQL END DECLARE SECTION;
58 /* build where clause */
59 build_qual(v->qual, v->argc, argv, qual);
61 if (log_flags & LOG_VALID)
62 /* tell the logfile what we're doing */
63 com_err(whoami, 0, "validating row: %s", qual);
65 /* look for the record */
66 sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",
67 table_name[q->rtable],qual);
69 if (dbms_errno) return(mr_errcode);
71 rowcount = atoi(sqlbuffer[0]);
72 if (rowcount == 0) return(MR_NO_MATCH);
73 if (rowcount > 1) return(MR_NOT_UNIQUE);
77 int validate_fields(q, argv, vo, n)
79 register char *argv[];
80 register struct valobj *vo;
88 if (log_flags & LOG_VALID)
89 com_err(whoami, 0, "validating %s in %s: %s",
90 vo->namefield, table_name[vo->table], argv[vo->index]);
91 status = validate_name(argv, vo);
95 if (log_flags & LOG_VALID)
96 com_err(whoami, 0, "validating %s in %s: %s",
97 vo->idfield, table_name[vo->table], argv[vo->index]);
98 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);
110 if (log_flags & LOG_VALID)
111 com_err(whoami, 0, "validating %s type: %s",
112 table_name[vo->table], argv[vo->index]);
113 status = validate_type(argv, vo);
117 if (log_flags & LOG_VALID)
118 com_err(whoami, 0, "validating typed data (%s): %s",
119 argv[vo->index - 1], argv[vo->index]);
120 status = validate_typedata(q, argv, vo);
124 if (log_flags & LOG_VALID)
125 com_err(whoami, 0, "validating rename %s in %s",
126 argv[vo->index], table_name[vo->table]);
127 status = validate_rename(argv, vo);
131 if (log_flags & LOG_VALID)
132 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
133 status = validate_chars(argv, vo);
137 if (log_flags & LOG_VALID)
138 com_err(whoami, 0, "validating length: %s", argv[vo->index]);
139 status = validate_len(argv, vo);
147 status = lock_table(vo);
151 status = readlock_table(vo);
155 status = convert_wildcards(argv[vo->index]);
159 status = convert_wildcards_uppercase(argv[vo->index]);
164 if (status != MR_EXISTS) return(status);
168 if (dbms_errno) return(mr_errcode);
173 /* validate_chars: verify that there are no illegal characters in
174 * the string. Legal characters are printing chars other than
175 * ", *, ?, \, [ and ].
177 static int illegalchars[] = {
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
180 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
182 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
183 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
184 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
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,
189 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
190 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
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,
196 int validate_chars(argv, vo)
198 register struct valobj *vo;
200 char *s=argv[vo->index];
202 EXEC SQL BEGIN DECLARE SECTION;
205 EXEC SQL END DECLARE SECTION;
208 /* check for bad characters */
210 if (illegalchars[*s++])
213 /* check for length */
214 tname = table_name[vo->table];
215 cname = vo->namefield;
216 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
217 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
219 if((strlen(argv[vo->index]) > len) &&
220 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
221 argv[vo->index][len]='\0'; /* truncate */
227 int validate_id(q, argv, vo)
230 register struct valobj *vo;
232 EXEC SQL BEGIN DECLARE SECTION;
233 char *name, *namefield, *idfield;
234 int id, rowcount, tbl;
235 EXEC SQL END DECLARE SECTION;
239 name = argv[vo->index];
241 namefield = vo->namefield;
242 idfield = vo->idfield;
244 if ((tbl==USERS_TABLE && !strcmp(namefield, "login")) ||
245 tbl==MACHINE_TABLE || tbl==SUBNET_TABLE || tbl==FILESYS_TABLE ||
246 tbl==LIST_TABLE || tbl==CLUSTER_TABLE || tbl==STRINGS_TABLE) {
247 if (tbl==MACHINE_TABLE || tbl==SUBNET_TABLE)
248 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
249 status = name_to_id(name, tbl, &id);
251 *(int *)argv[vo->index] = id;
253 } else if (status == MR_NO_MATCH && tbl==STRINGS_TABLE &&
254 (q->type == APPEND || q->type == UPDATE)) {
256 cache_entry(name, STRINGS_TABLE, id);
257 *(int *)argv[vo->index] = id;
259 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
264 /* else, it's `dubu', which uses unix_uid from users */
265 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
266 WHERE unix_uid = :name;
267 if (dbms_errno) return(mr_errcode);
268 if (rowcount != 1) return(vo->error);
269 EXEC SQL SELECT users_id INTO :id FROM users
270 WHERE unix_uid = :name;
271 *(int *)argv[vo->index] = id;
276 int validate_name(argv, vo)
278 register struct valobj *vo;
280 char *name, *namefield;
283 name = argv[vo->index];
284 namefield = vo->namefield;
285 if (vo->table==SERVERS_TABLE && !strcmp(namefield, "name")) {
286 for (c = name; *c; c++)
290 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
291 table_name[vo->table],table_name[vo->table],namefield,name);
294 if (dbms_errno) return(mr_errcode);
295 return ((atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error);
299 validate_date(argv, vo)
303 EXEC SQL BEGIN DECLARE SECTION;
307 EXEC SQL END DECLARE SECTION;
309 idate = argv[vo->index];
310 EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
311 if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
316 int validate_rename(argv, vo)
320 EXEC SQL BEGIN DECLARE SECTION;
321 char *name, *namefield, *idfield;
323 EXEC SQL END DECLARE SECTION;
327 status = validate_chars(argv, vo);
328 if(status != MR_EXISTS) return status;
329 name=argv[vo->index];
330 /* minor kludge to upcasify machine names */
331 if (vo->table == MACHINE_TABLE)
332 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
333 namefield = vo->namefield;
334 idfield = vo->idfield;
337 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
339 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",
340 namefield,table_name[vo->table],namefield,name);
343 if (dbms_errno) return(mr_errcode);
344 if (sqlca.sqlcode==SQL_NO_MATCH)
345 return(MR_EXISTS); /* how's _that_ for intuitive? */
349 status = name_to_id(name, vo->table, &id);
350 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
357 int validate_type(argv, vo)
359 register struct valobj *vo;
361 EXEC SQL BEGIN DECLARE SECTION;
365 EXEC SQL END DECLARE SECTION;
368 typename = vo->namefield;
369 c = val = argv[vo->index];
371 if (illegalchars[*c++])
375 /* uppercase type fields */
376 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
378 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
379 WHERE name = :typename AND type='TYPE' AND trans = :val;
380 if (dbms_errno) return(mr_errcode);
381 return (cnt ? MR_EXISTS : vo->error);
384 /* validate member or type-specific data field */
386 int validate_typedata(q, argv, vo)
387 register struct query *q;
388 register char *argv[];
389 register struct valobj *vo;
391 EXEC SQL BEGIN DECLARE SECTION;
396 EXEC SQL END DECLARE SECTION;
400 /* get named object */
401 name = argv[vo->index];
403 /* get field type string (known to be at index-1) */
404 field_type = argv[vo->index-1];
406 /* get corresponding data type associated with field type name */
407 EXEC SQL SELECT trans INTO :data_type FROM alias
408 WHERE name = :field_type AND type='TYPEDATA';
409 if (dbms_errno) return(mr_errcode);
410 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
412 /* now retrieve the record id corresponding to the named object */
413 if (strchr(data_type, ' '))
414 *strchr(data_type, ' ') = 0;
415 if (!strcmp(data_type, "user")) {
417 if (strchr(name, '@'))
419 status = name_to_id(name, USERS_TABLE, &id);
420 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
422 if (status) return(status);
423 } else if (!strcmp(data_type, "list")) {
425 status = name_to_id(name, LIST_TABLE, &id);
426 if (status && status == MR_NOT_UNIQUE)
428 if (status == MR_NO_MATCH) {
429 /* if idfield is non-zero, then if argv[0] matches the string
430 * that we're trying to resolve, we should get the value of
431 * numvalues.[idfield] for the id.
433 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
434 set_next_object_id(q->validate->object_id, q->rtable, 0);
436 EXEC SQL SELECT value INTO :id FROM numvalues
438 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
441 } else if (status) return(status);
442 } else if (!strcmp(data_type, "machine")) {
444 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
445 status = name_to_id(name, MACHINE_TABLE, &id);
446 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
448 if (status) return(status);
449 } else if (!strcmp(data_type, "string")) {
451 status = name_to_id(name, STRINGS_TABLE, &id);
452 if (status && status == MR_NOT_UNIQUE)
454 if (status == MR_NO_MATCH) {
455 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
457 cache_entry(name, STRINGS_TABLE, id);
458 } else if (status) return(status);
459 } else if (!strcmp(data_type, "none")) {
465 /* now set value in argv */
466 *(int *)argv[vo->index] = id;
472 /* Make sure the data fits in the field */
473 int validate_len(argv, vo)
474 register char *argv[];
475 register struct valobj *vo;
477 EXEC SQL BEGIN DECLARE SECTION;
480 EXEC SQL END DECLARE SECTION;
482 tname = table_name[vo->table];
483 cname = vo->namefield;
484 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
485 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
487 if((strlen(argv[vo->index]) > len) &&
488 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
489 argv[vo->index][len]='\0'; /* truncate */
494 /* Lock the table named by the validation object */
500 sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
501 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
510 * Get a read lock on the table by accessing the magic lock
511 * record. Certain tables are constructed so that they contain
512 * an id field whose value is zero and a modtime field. We
513 * manipulate the modtime field of the id 0 record to effect
514 * locking of the table
517 int readlock_table(vo)
521 sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
522 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
529 return(MR_EXISTS); /* validate_fields expects us to return
530 * this value if everything went okay
534 /* Check the database at startup time. NOT! */
536 void sanity_check_database(void)
541 char *sqlbuffer[QMAXARGS];
543 /* Dynamic SQL support routines */
544 SQLDA *mr_alloc_sqlda()
549 it=sqlald(QMAXARGS, ARGLEN, 0);
551 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
555 for(j=0; j<QMAXARGS; j++) {
556 it->V[j]=sqlbuffer[j]=malloc(ARGLEN);
557 it->T[j]=97; /* 97 = CHARZ = null-terminated string */
565 /* Convert normal Unix-style wildcards to SQL voodoo */
566 int convert_wildcards(arg)
569 static char buffer[ARGLEN];
570 register char *s, *d;
572 for(d=buffer,s=arg;*s;s++) {
574 case '*': *d++='%'; *d++='%'; break;
575 case '?': *d++='_'; break;
578 case ']': *d++='*'; *d++ = *s; break;
579 case '%': *d++='*'; *d++='%'; *d++='%'; break;
580 default: *d++ = *s; break;
585 /* Copy back into argv */
591 /* This version includes uppercase conversion, for things like gmac.
592 * This is necessary because "LIKE" doesn't work with "uppercase()".
593 * Including it in a wildcard routine saves making two passes over
594 * the argument string.
596 int convert_wildcards_uppercase(arg)
599 static char buffer[ARGLEN];
600 register char *s, *d;
602 for(d=buffer,s=arg;*s;s++) {
604 case '*': *d++='%'; *d++='%'; break;
605 case '?': *d++='_'; break;
608 case ']': *d++='*'; *d++ = *s; break;
609 case '%': *d++='*'; *d++='%'; *d++='%'; break;
610 default: *d++=toupper(*s); break; /* This is the only diff. */
615 /* Copy back into argv */
622 /* Adds a string to the string table. Returns the id number.
626 EXEC SQL BEGIN DECLARE SECTION;
628 EXEC SQL END DECLARE SECTION;
630 EXEC SQL BEGIN DECLARE SECTION;
633 EXEC SQL END DECLARE SECTION;
635 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
637 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
639 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);