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 extern SQLDA *sqlald(int,int,int);
46 EXEC SQL WHENEVER SQLERROR DO dbmserr();
48 /* Validation Routines */
50 int validate_row(q, argv, v)
51 register struct query *q;
53 register struct validate *v;
55 EXEC SQL BEGIN DECLARE SECTION;
58 EXEC SQL END DECLARE SECTION;
60 /* build where clause */
61 build_qual(v->qual, v->argc, argv, qual);
63 if (log_flags & LOG_VALID)
64 /* tell the logfile what we're doing */
65 com_err(whoami, 0, "validating row: %s", qual);
67 /* look for the record */
68 sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",
69 table_name[q->rtable],qual);
71 if (dbms_errno) return(mr_errcode);
73 rowcount = atoi(sqlbuffer[0]);
74 if (rowcount == 0) return(MR_NO_MATCH);
75 if (rowcount > 1) return(MR_NOT_UNIQUE);
79 int validate_fields(q, argv, vo, n)
81 register char *argv[];
82 register struct valobj *vo;
90 if (log_flags & LOG_VALID)
91 com_err(whoami, 0, "validating %s in %s: %s",
92 vo->namefield, table_name[vo->table], argv[vo->index]);
93 status = validate_name(argv, vo);
97 if (log_flags & LOG_VALID)
98 com_err(whoami, 0, "validating %s in %s: %s",
99 vo->idfield, table_name[vo->table], argv[vo->index]);
100 status = validate_id(q, argv, vo);
105 if (log_flags & LOG_VALID)
106 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
107 status = validate_date(argv, vo);
112 if (log_flags & LOG_VALID)
113 com_err(whoami, 0, "validating %s type: %s",
114 table_name[vo->table], argv[vo->index]);
115 status = validate_type(argv, vo);
119 if (log_flags & LOG_VALID)
120 com_err(whoami, 0, "validating typed data (%s): %s",
121 argv[vo->index - 1], argv[vo->index]);
122 status = validate_typedata(q, argv, vo);
126 if (log_flags & LOG_VALID)
127 com_err(whoami, 0, "validating rename %s in %s",
128 argv[vo->index], table_name[vo->table]);
129 status = validate_rename(argv, vo);
133 if (log_flags & LOG_VALID)
134 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
135 status = validate_chars(argv, vo);
139 if (log_flags & LOG_VALID)
140 com_err(whoami, 0, "validating length: %s", argv[vo->index]);
141 status = validate_len(argv, vo);
149 status = lock_table(vo);
153 status = readlock_table(vo);
157 status = convert_wildcards(argv[vo->index]);
161 status = convert_wildcards_uppercase(argv[vo->index]);
166 if (status != MR_EXISTS) return(status);
170 if (dbms_errno) return(mr_errcode);
175 /* validate_chars: verify that there are no illegal characters in
176 * the string. Legal characters are printing chars other than
177 * ", *, ?, \, [ and ].
179 static int illegalchars[] = {
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
181 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
182 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
183 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
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, 1, 1, 1, 0, 0, /* P - _ */
186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
187 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
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,
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,
198 int validate_chars(argv, vo)
200 register struct valobj *vo;
202 char *s=argv[vo->index];
204 EXEC SQL BEGIN DECLARE SECTION;
207 EXEC SQL END DECLARE SECTION;
210 /* check for bad characters */
212 if (illegalchars[*s++])
215 /* check for length */
216 tname = table_name[vo->table];
217 cname = vo->namefield;
218 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
219 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
221 if((strlen(argv[vo->index]) > len) &&
222 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
223 argv[vo->index][len]='\0'; /* truncate */
229 int validate_id(q, argv, vo)
232 register struct valobj *vo;
234 EXEC SQL BEGIN DECLARE SECTION;
235 char *name, *namefield, *idfield;
236 int id, rowcount, tbl;
237 EXEC SQL END DECLARE SECTION;
241 name = argv[vo->index];
243 namefield = vo->namefield;
244 idfield = vo->idfield;
246 if ((tbl==USERS_TABLE && !strcmp(namefield, "login")) ||
247 tbl==MACHINE_TABLE || tbl==SUBNET_TABLE || tbl==FILESYS_TABLE ||
248 tbl==LIST_TABLE || tbl==CLUSTER_TABLE || tbl==STRINGS_TABLE) {
249 if (tbl==MACHINE_TABLE || tbl==SUBNET_TABLE)
250 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
251 status = name_to_id(name, tbl, &id);
253 *(int *)argv[vo->index] = id;
255 } else if (status == MR_NO_MATCH && tbl==STRINGS_TABLE &&
256 (q->type == APPEND || q->type == UPDATE)) {
258 cache_entry(name, STRINGS_TABLE, id);
259 *(int *)argv[vo->index] = id;
261 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
266 /* else, it's `dubu', which uses unix_uid from users */
267 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
268 WHERE unix_uid = :name;
269 if (dbms_errno) return(mr_errcode);
270 if (rowcount != 1) return(vo->error);
271 EXEC SQL SELECT users_id INTO :id FROM users
272 WHERE unix_uid = :name;
273 *(int *)argv[vo->index] = id;
278 int validate_name(argv, vo)
280 register struct valobj *vo;
282 char *name, *namefield;
285 name = argv[vo->index];
286 namefield = vo->namefield;
287 if (vo->table==SERVERS_TABLE && !strcmp(namefield, "name")) {
288 for (c = name; *c; c++)
292 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
293 table_name[vo->table],table_name[vo->table],namefield,name);
296 if (dbms_errno) return(mr_errcode);
297 return ((atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error);
301 validate_date(argv, vo)
305 EXEC SQL BEGIN DECLARE SECTION;
309 EXEC SQL END DECLARE SECTION;
311 idate = argv[vo->index];
312 EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
313 if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
318 int validate_rename(argv, vo)
322 EXEC SQL BEGIN DECLARE SECTION;
323 char *name, *namefield, *idfield;
325 EXEC SQL END DECLARE SECTION;
329 status = validate_chars(argv, vo);
330 if(status != MR_EXISTS) return status;
331 name=argv[vo->index];
332 /* minor kludge to upcasify machine names */
333 if (vo->table == MACHINE_TABLE)
334 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
335 namefield = vo->namefield;
336 idfield = vo->idfield;
339 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
341 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",
342 namefield,table_name[vo->table],namefield,name);
345 if (dbms_errno) return(mr_errcode);
346 if (sqlca.sqlcode==SQL_NO_MATCH)
347 return(MR_EXISTS); /* how's _that_ for intuitive? */
351 status = name_to_id(name, vo->table, &id);
352 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
359 int validate_type(argv, vo)
361 register struct valobj *vo;
363 EXEC SQL BEGIN DECLARE SECTION;
367 EXEC SQL END DECLARE SECTION;
370 typename = vo->namefield;
371 c = val = argv[vo->index];
373 if (illegalchars[*c++])
377 /* uppercase type fields */
378 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
380 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
381 WHERE name = :typename AND type='TYPE' AND trans = :val;
382 if (dbms_errno) return(mr_errcode);
383 return (cnt ? MR_EXISTS : vo->error);
386 /* validate member or type-specific data field */
388 int validate_typedata(q, argv, vo)
389 register struct query *q;
390 register char *argv[];
391 register struct valobj *vo;
393 EXEC SQL BEGIN DECLARE SECTION;
398 EXEC SQL END DECLARE SECTION;
402 /* get named object */
403 name = argv[vo->index];
405 /* get field type string (known to be at index-1) */
406 field_type = argv[vo->index-1];
408 /* get corresponding data type associated with field type name */
409 EXEC SQL SELECT trans INTO :data_type FROM alias
410 WHERE name = :field_type AND type='TYPEDATA';
411 if (dbms_errno) return(mr_errcode);
412 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
414 /* now retrieve the record id corresponding to the named object */
415 if (strchr(data_type, ' '))
416 *strchr(data_type, ' ') = 0;
417 if (!strcmp(data_type, "user")) {
419 if (strchr(name, '@'))
421 status = name_to_id(name, USERS_TABLE, &id);
422 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
424 if (status) return(status);
425 } else if (!strcmp(data_type, "list")) {
427 status = name_to_id(name, LIST_TABLE, &id);
428 if (status && status == MR_NOT_UNIQUE)
430 if (status == MR_NO_MATCH) {
431 /* if idfield is non-zero, then if argv[0] matches the string
432 * that we're trying to resolve, we should get the value of
433 * numvalues.[idfield] for the id.
435 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
436 set_next_object_id(q->validate->object_id, q->rtable, 0);
438 EXEC SQL SELECT value INTO :id FROM numvalues
440 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
443 } else if (status) return(status);
444 } else if (!strcmp(data_type, "machine")) {
446 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
447 status = name_to_id(name, MACHINE_TABLE, &id);
448 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
450 if (status) return(status);
451 } else if (!strcmp(data_type, "string")) {
453 status = name_to_id(name, STRINGS_TABLE, &id);
454 if (status && status == MR_NOT_UNIQUE)
456 if (status == MR_NO_MATCH) {
457 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
459 cache_entry(name, STRINGS_TABLE, id);
460 } else if (status) return(status);
461 } else if (!strcmp(data_type, "none")) {
467 /* now set value in argv */
468 *(int *)argv[vo->index] = id;
474 /* Make sure the data fits in the field */
475 int validate_len(argv, vo)
476 register char *argv[];
477 register struct valobj *vo;
479 EXEC SQL BEGIN DECLARE SECTION;
482 EXEC SQL END DECLARE SECTION;
484 tname = table_name[vo->table];
485 cname = vo->namefield;
486 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
487 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
489 if((strlen(argv[vo->index]) > len) &&
490 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
491 argv[vo->index][len]='\0'; /* truncate */
496 /* Lock the table named by the validation object */
502 sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
503 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
512 * Get a read lock on the table by accessing the magic lock
513 * record. Certain tables are constructed so that they contain
514 * an id field whose value is zero and a modtime field. We
515 * manipulate the modtime field of the id 0 record to effect
516 * locking of the table
519 int readlock_table(vo)
523 sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
524 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
531 return(MR_EXISTS); /* validate_fields expects us to return
532 * this value if everything went okay
536 /* Check the database at startup time. NOT! */
538 void sanity_check_database(void)
543 char *sqlbuffer[QMAXARGS];
545 /* Dynamic SQL support routines */
546 SQLDA *mr_alloc_sqlda()
551 it=sqlald(QMAXARGS, ARGLEN, 0);
553 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
557 for(j=0; j<QMAXARGS; j++) {
558 it->V[j]=sqlbuffer[j]=malloc(ARGLEN);
559 it->T[j]=97; /* 97 = CHARZ = null-terminated string */
567 /* Convert normal Unix-style wildcards to SQL voodoo */
568 int convert_wildcards(arg)
571 static char buffer[ARGLEN];
572 register char *s, *d;
574 for(d=buffer,s=arg;*s;s++) {
576 case '*': *d++='%'; *d++='%'; break;
577 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;
606 case '_': *d++='*'; *d++ = *s; break;
607 case '%': *d++='*'; *d++='%'; *d++='%'; break;
608 default: *d++=toupper(*s); break; /* This is the only diff. */
613 /* Copy back into argv */
620 /* Adds a string to the string table. Returns the id number.
624 EXEC SQL BEGIN DECLARE SECTION;
626 EXEC SQL END DECLARE SECTION;
628 EXEC SQL BEGIN DECLARE SECTION;
631 EXEC SQL END DECLARE SECTION;
633 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
635 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
637 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);