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);
145 status = lock_table(vo);
149 status = readlock_table(vo);
153 status = convert_wildcards(argv[vo->index]);
157 status = convert_wildcards_uppercase(argv[vo->index]);
162 if (status != MR_EXISTS) return(status);
166 if (dbms_errno) return(mr_errcode);
171 /* validate_chars: verify that there are no illegal characters in
172 * the string. Legal characters are printing chars other than
173 * ", *, ?, \, [ and ].
175 static int illegalchars[] = {
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
177 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
178 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
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, 0, 0, 0, 0, 1, /* p - ^? */
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,
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,
194 int validate_chars(argv, vo)
196 register struct valobj *vo;
198 char *s=argv[vo->index];
200 EXEC SQL BEGIN DECLARE SECTION;
203 EXEC SQL END DECLARE SECTION;
206 /* check for bad characters */
208 if (illegalchars[*s++])
211 /* check for length */
212 tname = table_name[vo->table];
213 cname = vo->namefield;
214 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
215 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
217 if((strlen(argv[vo->index]) > len) &&
218 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
219 argv[vo->index][len]='\0'; /* truncate */
225 int validate_id(q, argv, vo)
228 register struct valobj *vo;
230 EXEC SQL BEGIN DECLARE SECTION;
231 char *name, *namefield, *idfield;
232 int id, rowcount, tbl;
233 EXEC SQL END DECLARE SECTION;
237 name = argv[vo->index];
239 namefield = vo->namefield;
240 idfield = vo->idfield;
242 if ((tbl==USERS_TABLE && !strcmp(namefield, "login")) ||
243 tbl==MACHINE_TABLE || tbl==SUBNET_TABLE || tbl==FILESYS_TABLE ||
244 tbl==LIST_TABLE || tbl==CLUSTER_TABLE || tbl==STRINGS_TABLE) {
245 if (tbl==MACHINE_TABLE || tbl==SUBNET_TABLE)
246 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
247 status = name_to_id(name, tbl, &id);
249 *(int *)argv[vo->index] = id;
251 } else if (status == MR_NO_MATCH && tbl==STRINGS_TABLE &&
252 (q->type == APPEND || q->type == UPDATE)) {
254 cache_entry(name, STRINGS_TABLE, id);
255 *(int *)argv[vo->index] = id;
257 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
262 /* else, it's `dubu', which uses unix_uid from users */
263 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
264 WHERE unix_uid = :name;
265 if (dbms_errno) return(mr_errcode);
266 if (rowcount != 1) return(vo->error);
267 EXEC SQL SELECT users_id INTO :id FROM users
268 WHERE unix_uid = :name;
269 *(int *)argv[vo->index] = id;
274 int validate_name(argv, vo)
276 register struct valobj *vo;
278 char *name, *namefield;
281 name = argv[vo->index];
282 namefield = vo->namefield;
283 if (vo->table==SERVERS_TABLE && !strcmp(namefield, "name")) {
284 for (c = name; *c; c++)
288 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
289 table_name[vo->table],table_name[vo->table],namefield,name);
292 if (dbms_errno) return(mr_errcode);
293 return ((atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error);
297 validate_date(argv, vo)
301 EXEC SQL BEGIN DECLARE SECTION;
305 EXEC SQL END DECLARE SECTION;
307 idate = argv[vo->index];
308 EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
309 if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
314 int validate_rename(argv, vo)
318 EXEC SQL BEGIN DECLARE SECTION;
319 char *name, *namefield, *idfield;
321 EXEC SQL END DECLARE SECTION;
325 status = validate_chars(argv, vo);
326 if(status != MR_EXISTS) return status;
327 name=argv[vo->index];
328 /* minor kludge to upcasify machine names */
329 if (vo->table == MACHINE_TABLE)
330 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
331 namefield = vo->namefield;
332 idfield = vo->idfield;
335 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
337 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",
338 namefield,table_name[vo->table],namefield,name);
341 if (dbms_errno) return(mr_errcode);
342 if (sqlca.sqlcode==SQL_NO_MATCH)
343 return(MR_EXISTS); /* how's _that_ for intuitive? */
347 status = name_to_id(name, vo->table, &id);
348 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
355 int validate_type(argv, vo)
357 register struct valobj *vo;
359 EXEC SQL BEGIN DECLARE SECTION;
363 EXEC SQL END DECLARE SECTION;
366 typename = vo->namefield;
367 c = val = argv[vo->index];
369 if (illegalchars[*c++])
373 /* uppercase type fields */
374 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
376 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
377 WHERE name = :typename AND type='TYPE' AND trans = :val;
378 if (dbms_errno) return(mr_errcode);
379 return (cnt ? MR_EXISTS : vo->error);
382 /* validate member or type-specific data field */
384 int validate_typedata(q, argv, vo)
385 register struct query *q;
386 register char *argv[];
387 register struct valobj *vo;
389 EXEC SQL BEGIN DECLARE SECTION;
394 EXEC SQL END DECLARE SECTION;
398 /* get named object */
399 name = argv[vo->index];
401 /* get field type string (known to be at index-1) */
402 field_type = argv[vo->index-1];
404 /* get corresponding data type associated with field type name */
405 EXEC SQL SELECT trans INTO :data_type FROM alias
406 WHERE name = :field_type AND type='TYPEDATA';
407 if (dbms_errno) return(mr_errcode);
408 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
410 /* now retrieve the record id corresponding to the named object */
411 if (strchr(data_type, ' '))
412 *strchr(data_type, ' ') = 0;
413 if (!strcmp(data_type, "user")) {
415 if (strchr(name, '@'))
417 status = name_to_id(name, USERS_TABLE, &id);
418 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
420 if (status) return(status);
421 } else if (!strcmp(data_type, "list")) {
423 status = name_to_id(name, LIST_TABLE, &id);
424 if (status && status == MR_NOT_UNIQUE)
426 if (status == MR_NO_MATCH) {
427 /* if idfield is non-zero, then if argv[0] matches the string
428 * that we're trying to resolve, we should get the value of
429 * numvalues.[idfield] for the id.
431 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
432 set_next_object_id(q->validate->object_id, q->rtable, 0);
434 EXEC SQL SELECT value INTO :id FROM numvalues
436 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
439 } else if (status) return(status);
440 } else if (!strcmp(data_type, "machine")) {
442 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
443 status = name_to_id(name, MACHINE_TABLE, &id);
444 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
446 if (status) return(status);
447 } else if (!strcmp(data_type, "string")) {
449 status = name_to_id(name, STRINGS_TABLE, &id);
450 if (status && status == MR_NOT_UNIQUE)
452 if (status == MR_NO_MATCH) {
453 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
455 cache_entry(name, STRINGS_TABLE, id);
456 } else if (status) return(status);
457 } else if (!strcmp(data_type, "none")) {
463 /* now set value in argv */
464 *(int *)argv[vo->index] = id;
470 /* Make sure the data fits in the field */
471 int validate_len(argv, vo)
472 register char *argv[];
473 register struct valobj *vo;
475 EXEC SQL BEGIN DECLARE SECTION;
478 EXEC SQL END DECLARE SECTION;
480 tname = table_name[vo->table];
481 cname = vo->namefield;
482 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
483 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
485 if((strlen(argv[vo->index]) > len) &&
486 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
487 argv[vo->index][len]='\0'; /* truncate */
492 /* Lock the table named by the validation object */
498 sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
499 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
508 * Get a read lock on the table by accessing the magic lock
509 * record. Certain tables are constructed so that they contain
510 * an id field whose value is zero and a modtime field. We
511 * manipulate the modtime field of the id 0 record to effect
512 * locking of the table
515 int readlock_table(vo)
519 sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
520 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
527 return(MR_EXISTS); /* validate_fields expects us to return
528 * this value if everything went okay
532 /* Check the database at startup time. */
534 void sanity_check_database(void)
536 EXEC SQL BEGIN DECLARE SECTION;
538 EXEC SQL END DECLARE SECTION;
540 /* Sometimes a crash can leave strings_id in numvalues in an
541 incorrect state. Check for that and fix it. */
543 EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name='strings_id';
545 for (id=oid+1; sqlca.sqlcode==0; id++)
546 EXEC SQL SELECT string_id INTO :id FROM strings
550 EXEC SQL UPDATE numvalues SET value=:id-1 WHERE name='strings_id';
554 char *sqlbuffer[QMAXARGS];
556 /* Dynamic SQL support routines */
557 SQLDA *mr_alloc_sqlda()
562 it=sqlald(QMAXARGS, ARGLEN, 0);
564 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
568 for(j=0; j<QMAXARGS; j++) {
569 it->V[j]=sqlbuffer[j]=malloc(ARGLEN);
570 it->T[j]=97; /* 97 = CHARZ = null-terminated string */
578 /* Convert normal Unix-style wildcards to SQL voodoo */
579 int convert_wildcards(arg)
582 static char buffer[ARGLEN];
583 register char *s, *d;
585 for(d=buffer,s=arg;*s;s++) {
587 case '*': *d++='%'; *d++='%'; break;
588 case '?': *d++='_'; break;
589 case '_': *d++='*'; *d++ = *s; break;
590 case '%': *d++='*'; *d++='%'; *d++='%'; break;
591 default: *d++ = *s; break;
596 /* Copy back into argv */
602 /* This version includes uppercase conversion, for things like gmac.
603 * This is necessary because "LIKE" doesn't work with "uppercase()".
604 * Including it in a wildcard routine saves making two passes over
605 * the argument string.
607 int convert_wildcards_uppercase(arg)
610 static char buffer[ARGLEN];
611 register char *s, *d;
613 for(d=buffer,s=arg;*s;s++) {
615 case '*': *d++='%'; *d++='%'; break;
616 case '?': *d++='_'; break;
617 case '_': *d++='*'; *d++ = *s; break;
618 case '%': *d++='*'; *d++='%'; *d++='%'; break;
619 default: *d++=toupper(*s); break; /* This is the only diff. */
624 /* Copy back into argv */
631 /* Adds a string to the string table. Returns the id number.
635 EXEC SQL BEGIN DECLARE SECTION;
637 EXEC SQL END DECLARE SECTION;
639 EXEC SQL BEGIN DECLARE SECTION;
642 EXEC SQL END DECLARE SECTION;
644 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
646 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
648 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);