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 convert_wildcards_uppercase(char *arg);
43 extern SQLDA *sqlald(int,int,int);
45 EXEC SQL WHENEVER SQLERROR DO dbmserr();
47 /* Validation Routines */
49 int validate_row(q, argv, v)
50 register struct query *q;
52 register struct validate *v;
54 EXEC SQL BEGIN DECLARE SECTION;
57 EXEC SQL END DECLARE SECTION;
59 /* build where clause */
60 build_qual(v->qual, v->argc, argv, qual);
62 if (log_flags & LOG_VALID)
63 /* tell the logfile what we're doing */
64 com_err(whoami, 0, "validating row: %s", qual);
66 /* look for the record */
67 sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",
68 table_name[q->rtable],qual);
70 if (dbms_errno) return(mr_errcode);
72 rowcount = atoi(sqlbuffer[0]);
73 if (rowcount == 0) return(MR_NO_MATCH);
74 if (rowcount > 1) return(MR_NOT_UNIQUE);
78 int 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, table_name[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, table_name[vo->table], argv[vo->index]);
99 status = validate_id(q, argv, vo);
103 if (log_flags & LOG_VALID)
104 com_err(whoami, 0, "validating %s type: %s",
105 table_name[vo->table], argv[vo->index]);
106 status = validate_type(argv, vo);
110 if (log_flags & LOG_VALID)
111 com_err(whoami, 0, "validating typed data (%s): %s",
112 argv[vo->index - 1], argv[vo->index]);
113 status = validate_typedata(q, argv, vo);
117 if (log_flags & LOG_VALID)
118 com_err(whoami, 0, "validating rename %s in %s",
119 argv[vo->index], table_name[vo->table]);
120 status = validate_rename(argv, vo);
124 if (log_flags & LOG_VALID)
125 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
126 status = validate_chars(argv, vo);
130 if (log_flags & LOG_VALID)
131 com_err(whoami, 0, "validating length: %s", argv[vo->index]);
132 status = validate_len(argv, vo);
136 if (log_flags & LOG_VALID)
137 com_err(whoami, 0, "validating number: %s", argv[vo->index]);
138 status = validate_num(argv, vo);
142 status = convert_wildcards(argv[vo->index]);
146 status = convert_wildcards_uppercase(argv[vo->index]);
151 if (status != MR_EXISTS) return(status);
155 if (dbms_errno) return(mr_errcode);
160 /* validate_chars: verify that there are no illegal characters in
161 * the string. Legal characters are printing chars other than
162 * ", *, ?, \, [ and ].
164 static int illegalchars[] = {
165 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
166 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
167 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
173 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
175 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
183 int validate_chars(argv, vo)
185 register struct valobj *vo;
187 char *s=argv[vo->index];
189 EXEC SQL BEGIN DECLARE SECTION;
192 EXEC SQL END DECLARE SECTION;
195 /* check for bad characters */
197 if (illegalchars[*s++])
200 /* check for length */
201 tname = table_name[vo->table];
202 cname = vo->namefield;
203 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
204 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
206 if((strlen(argv[vo->index]) > len) &&
207 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
208 argv[vo->index][len]='\0'; /* truncate */
214 int validate_id(q, argv, vo)
217 register struct valobj *vo;
219 EXEC SQL BEGIN DECLARE SECTION;
220 char *name, *namefield, *idfield;
221 int id, rowcount, tbl;
222 EXEC SQL END DECLARE SECTION;
226 name = argv[vo->index];
228 namefield = vo->namefield;
229 idfield = vo->idfield;
231 if ((tbl==USERS_TABLE && !strcmp(namefield, "login")) ||
232 tbl==MACHINE_TABLE || tbl==SUBNET_TABLE || tbl==FILESYS_TABLE ||
233 tbl==LIST_TABLE || tbl==CLUSTER_TABLE || tbl==STRINGS_TABLE) {
234 if (tbl==MACHINE_TABLE || tbl==SUBNET_TABLE)
235 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
236 status = name_to_id(name, tbl, &id);
238 *(int *)argv[vo->index] = id;
240 } else if (status == MR_NO_MATCH && tbl==STRINGS_TABLE &&
241 (q->type == APPEND || q->type == UPDATE)) {
243 cache_entry(name, STRINGS_TABLE, id);
244 *(int *)argv[vo->index] = id;
246 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
251 /* else, it's `dubu', which uses unix_uid from users */
252 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
253 WHERE unix_uid = :name;
254 if (dbms_errno) return(mr_errcode);
255 if (rowcount != 1) return(vo->error);
256 EXEC SQL SELECT users_id INTO :id FROM users
257 WHERE unix_uid = :name;
258 *(int *)argv[vo->index] = id;
263 int validate_name(argv, vo)
265 register struct valobj *vo;
267 char *name, *namefield;
270 name = argv[vo->index];
271 namefield = vo->namefield;
272 if (vo->table==SERVERS_TABLE && !strcmp(namefield, "name")) {
273 for (c = name; *c; c++)
277 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
278 table_name[vo->table],table_name[vo->table],namefield,name);
281 if (dbms_errno) return(mr_errcode);
282 return ((atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error);
285 int validate_rename(argv, vo)
289 EXEC SQL BEGIN DECLARE SECTION;
290 char *name, *namefield, *idfield;
292 EXEC SQL END DECLARE SECTION;
296 status = validate_chars(argv, vo);
297 if(status != MR_EXISTS) return status;
298 name=argv[vo->index];
299 /* minor kludge to upcasify machine names */
300 if (vo->table == MACHINE_TABLE)
301 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
302 namefield = vo->namefield;
303 idfield = vo->idfield;
306 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
308 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",
309 namefield,table_name[vo->table],namefield,name);
312 if (dbms_errno) return(mr_errcode);
313 if (sqlca.sqlcode==SQL_NO_MATCH)
314 return(MR_EXISTS); /* how's _that_ for intuitive? */
318 status = name_to_id(name, vo->table, &id);
319 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
326 int validate_type(argv, vo)
328 register struct valobj *vo;
330 EXEC SQL BEGIN DECLARE SECTION;
334 EXEC SQL END DECLARE SECTION;
337 typename = vo->namefield;
338 c = val = argv[vo->index];
340 if (illegalchars[*c++])
344 /* uppercase type fields */
345 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
347 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
348 WHERE name = :typename AND type='TYPE' AND trans = :val;
349 if (dbms_errno) return(mr_errcode);
350 return (cnt ? MR_EXISTS : vo->error);
353 /* validate member or type-specific data field */
355 int validate_typedata(q, argv, vo)
356 register struct query *q;
357 register char *argv[];
358 register struct valobj *vo;
360 EXEC SQL BEGIN DECLARE SECTION;
365 EXEC SQL END DECLARE SECTION;
369 /* get named object */
370 name = argv[vo->index];
372 /* get field type string (known to be at index-1) */
373 field_type = argv[vo->index-1];
375 /* get corresponding data type associated with field type name */
376 EXEC SQL SELECT trans INTO :data_type FROM alias
377 WHERE name = :field_type AND type='TYPEDATA';
378 if (dbms_errno) return(mr_errcode);
379 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
381 /* now retrieve the record id corresponding to the named object */
382 if (strchr(data_type, ' '))
383 *strchr(data_type, ' ') = 0;
384 if (!strcmp(data_type, "user")) {
386 if (strchr(name, '@'))
388 status = name_to_id(name, USERS_TABLE, &id);
389 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
391 if (status) return(status);
392 } else if (!strcmp(data_type, "list")) {
394 status = name_to_id(name, LIST_TABLE, &id);
395 if (status && status == MR_NOT_UNIQUE)
397 if (status == MR_NO_MATCH) {
398 /* if idfield is non-zero, then if argv[0] matches the string
399 * that we're trying to resolve, we should get the value of
400 * numvalues.[idfield] for the id.
402 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
403 set_next_object_id(q->validate->object_id, q->rtable, 0);
405 EXEC SQL SELECT value INTO :id FROM numvalues
407 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
410 } else if (status) return(status);
411 } else if (!strcmp(data_type, "machine")) {
413 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
414 status = name_to_id(name, MACHINE_TABLE, &id);
415 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
417 if (status) return(status);
418 } else if (!strcmp(data_type, "string")) {
420 status = name_to_id(name, STRINGS_TABLE, &id);
421 if (status && status == MR_NOT_UNIQUE)
423 if (status == MR_NO_MATCH) {
424 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
426 cache_entry(name, STRINGS_TABLE, id);
427 } else if (status) return(status);
428 } else if (!strcmp(data_type, "none")) {
434 /* now set value in argv */
435 *(int *)argv[vo->index] = id;
441 /* Make sure the data fits in the field */
442 int validate_len(argv, vo)
443 register char *argv[];
444 register struct valobj *vo;
446 EXEC SQL BEGIN DECLARE SECTION;
449 EXEC SQL END DECLARE SECTION;
451 tname = table_name[vo->table];
452 cname = vo->namefield;
453 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
454 WHERE table_name=UPPER(:tname) AND column_name=UPPER(:cname);
456 if((strlen(argv[vo->index]) > len) &&
457 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
458 argv[vo->index][len]='\0'; /* truncate */
463 /* Make sure the data is numeric */
464 int validate_num(argv, vo)
465 register char *argv[];
466 register struct valobj *vo;
468 char *p = argv[vo->index];
472 if (*p < '0' || *p > '9') return MR_INTEGER;
477 /* Check the database at startup time. */
479 void sanity_check_database(void)
481 EXEC SQL BEGIN DECLARE SECTION;
483 EXEC SQL END DECLARE SECTION;
485 /* Sometimes a crash can leave strings_id in numvalues in an
486 incorrect state. Check for that and fix it. */
488 EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name='strings_id';
490 for (id=oid+1; sqlca.sqlcode==0; id++)
491 EXEC SQL SELECT string_id INTO :id FROM strings
495 EXEC SQL UPDATE numvalues SET value=:id-1 WHERE name='strings_id';
499 char *sqlbuffer[QMAXARGS];
501 /* Dynamic SQL support routines */
502 SQLDA *mr_alloc_sqlda()
507 it=sqlald(QMAXARGS, ARGLEN, 0);
509 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
513 for(j=0; j<QMAXARGS; j++) {
514 it->V[j]=sqlbuffer[j]=malloc(ARGLEN);
515 it->T[j]=97; /* 97 = CHARZ = null-terminated string */
523 /* Convert normal Unix-style wildcards to SQL voodoo */
524 int convert_wildcards(arg)
527 static char buffer[ARGLEN];
528 register char *s, *d;
530 for(d=buffer,s=arg;*s;s++) {
532 case '*': *d++='%'; *d++='%'; break;
533 case '?': *d++='_'; break;
534 case '_': *d++='*'; *d++ = *s; break;
535 case '%': *d++='*'; *d++='%'; *d++='%'; break;
536 default: *d++ = *s; break;
541 /* Copy back into argv */
547 /* This version includes uppercase conversion, for things like gmac.
548 * This is necessary because "LIKE" doesn't work with "uppercase()".
549 * Including it in a wildcard routine saves making two passes over
550 * the argument string.
552 int convert_wildcards_uppercase(arg)
555 static char buffer[ARGLEN];
556 register char *s, *d;
558 for(d=buffer,s=arg;*s;s++) {
560 case '*': *d++='%'; *d++='%'; break;
561 case '?': *d++='_'; break;
562 case '_': *d++='*'; *d++ = *s; break;
563 case '%': *d++='*'; *d++='%'; *d++='%'; break;
564 default: *d++=toupper(*s); break; /* This is the only diff. */
569 /* Copy back into argv */
576 /* Adds a string to the string table. Returns the id number.
580 EXEC SQL BEGIN DECLARE SECTION;
582 EXEC SQL END DECLARE SECTION;
584 EXEC SQL BEGIN DECLARE SECTION;
587 EXEC SQL END DECLARE SECTION;
589 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
591 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
593 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);