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(register struct query *q, char *argv[], register struct validate *v)
52 EXEC SQL BEGIN DECLARE SECTION;
55 EXEC SQL END DECLARE SECTION;
57 /* build where clause */
58 build_qual(v->qual, v->argc, argv, qual);
60 if (log_flags & LOG_VALID)
61 /* tell the logfile what we're doing */
62 com_err(whoami, 0, "validating row: %s", qual);
64 /* look for the record */
65 sprintf(stmt_buf, "SELECT COUNT (*) FROM %s WHERE %s",
66 table_name[q->rtable], qual);
71 rowcount = atoi(sqlbuffer[0]);
79 int validate_fields(struct query *q, register char *argv[],
80 register struct valobj *vo, register int n)
89 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]);
94 status = validate_name(argv, vo);
98 if (log_flags & LOG_VALID)
100 com_err(whoami, 0, "validating %s in %s: %s",
101 vo->idfield, table_name[vo->table], argv[vo->index]);
103 status = validate_id(q, argv, vo);
107 if (log_flags & LOG_VALID)
109 com_err(whoami, 0, "validating %s type: %s",
110 table_name[vo->table], argv[vo->index]);
112 status = validate_type(argv, vo);
116 if (log_flags & LOG_VALID)
118 com_err(whoami, 0, "validating typed data (%s): %s",
119 argv[vo->index - 1], argv[vo->index]);
121 status = validate_typedata(q, argv, vo);
125 if (log_flags & LOG_VALID)
127 com_err(whoami, 0, "validating rename %s in %s",
128 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);
150 status = lock_table(vo);
154 status = readlock_table(vo);
158 status = convert_wildcards(argv[vo->index]);
162 status = convert_wildcards_uppercase(argv[vo->index]);
167 if (status != MR_EXISTS)
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(char *argv[], register struct valobj *vo)
203 char *s = argv[vo->index];
205 EXEC SQL BEGIN DECLARE SECTION;
208 EXEC SQL END DECLARE SECTION;
210 /* check for bad characters */
213 if (illegalchars[*s++])
217 /* check for length */
218 tname = table_name[vo->table];
219 cname = vo->namefield;
220 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
221 WHERE table_name = UPPER(:tname) AND column_name = UPPER(:cname);
223 if ((strlen(argv[vo->index]) > len) &&
224 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
225 argv[vo->index][len] = '\0'; /* truncate */
231 int validate_id(struct query *q, char *argv[], register struct valobj *vo)
233 EXEC SQL BEGIN DECLARE SECTION;
234 char *name, *namefield, *idfield;
235 int id, rowcount, tbl;
236 EXEC SQL END DECLARE SECTION;
240 name = argv[vo->index];
242 namefield = vo->namefield;
243 idfield = vo->idfield;
245 if ((tbl == USERS_TABLE && !strcmp(namefield, "login")) ||
246 tbl == MACHINE_TABLE || tbl == SUBNET_TABLE || tbl == FILESYS_TABLE ||
247 tbl == LIST_TABLE || tbl == CLUSTER_TABLE || tbl == STRINGS_TABLE)
249 if (tbl == MACHINE_TABLE || tbl == SUBNET_TABLE)
251 for (c = name; *c; c++)
257 status = name_to_id(name, tbl, &id);
260 *(int *)argv[vo->index] = id;
263 else if (status == MR_NO_MATCH && tbl == STRINGS_TABLE &&
264 (q->type == APPEND || q->type == UPDATE))
266 id = add_string(name);
267 cache_entry(name, STRINGS_TABLE, id);
268 *(int *)argv[vo->index] = id;
271 else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
278 /* else, it's `dubu', which uses unix_uid from users */
279 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
280 WHERE unix_uid = :name;
285 EXEC SQL SELECT users_id INTO :id FROM users
286 WHERE unix_uid = :name;
287 *(int *)argv[vo->index] = id;
292 int validate_name(char *argv[], register struct valobj *vo)
294 char *name, *namefield;
297 name = argv[vo->index];
298 namefield = vo->namefield;
299 if (vo->table == SERVERS_TABLE && !strcmp(namefield, "name"))
301 for (c = name; *c; c++)
307 sprintf(stmt_buf, "SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
308 table_name[vo->table], table_name[vo->table], namefield, name);
313 return (atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error;
316 int validate_rename(char *argv[], struct valobj *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)
328 name = argv[vo->index];
329 /* minor kludge to upcasify machine names */
330 if (vo->table == MACHINE_TABLE)
332 for (c = name; *c; c++)
338 namefield = vo->namefield;
339 idfield = vo->idfield;
343 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
345 sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = '%s'",
346 namefield, table_name[vo->table], namefield, name);
351 if (sqlca.sqlcode == SQL_NO_MATCH)
352 return MR_EXISTS; /* how's _that_ for intuitive? */
356 status = name_to_id(name, vo->table, &id);
357 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
364 int validate_type(char *argv[], 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];
377 if (illegalchars[*c++])
381 /* uppercase type fields */
382 for (c = val; *c; c++)
388 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
389 WHERE name = :typename AND type = 'TYPE' AND trans = :val;
392 return cnt ? MR_EXISTS : vo->error;
395 /* validate member or type-specific data field */
397 int validate_typedata(register struct query *q, register char *argv[], register struct valobj *vo)
399 EXEC SQL BEGIN DECLARE SECTION;
404 EXEC SQL END DECLARE SECTION;
408 /* get named object */
409 name = argv[vo->index];
411 /* get field type string (known to be at index-1) */
412 field_type = argv[vo->index - 1];
414 /* get corresponding data type associated with field type name */
415 EXEC SQL SELECT trans INTO :data_type FROM alias
416 WHERE name = :field_type AND type = 'TYPEDATA';
419 if (sqlca.sqlerrd[2] != 1)
422 /* now retrieve the record id corresponding to the named object */
423 if (strchr(data_type, ' '))
424 *strchr(data_type, ' ') = '\0';
425 if (!strcmp(data_type, "user"))
428 if (strchr(name, '@'))
430 status = name_to_id(name, USERS_TABLE, &id);
431 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
436 else if (!strcmp(data_type, "list"))
439 status = name_to_id(name, LIST_TABLE, &id);
440 if (status && status == MR_NOT_UNIQUE)
442 if (status == MR_NO_MATCH)
444 /* if idfield is non-zero, then if argv[0] matches the string
445 * that we're trying to resolve, we should get the value of
446 * numvalues.[idfield] for the id.
448 if (vo->idfield && !strcmp(argv[0], argv[vo->index]))
450 set_next_object_id(q->validate->object_id, q->rtable, 0);
452 EXEC SQL SELECT value INTO :id FROM numvalues
454 if (sqlca.sqlerrd[2] != 1)
463 else if (!strcmp(data_type, "machine"))
466 for (c = name; *c; c++)
471 status = name_to_id(name, MACHINE_TABLE, &id);
472 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
477 else if (!strcmp(data_type, "string"))
480 status = name_to_id(name, STRINGS_TABLE, &id);
481 if (status && status == MR_NOT_UNIQUE)
483 if (status == MR_NO_MATCH)
485 if (q->type != APPEND && q->type != UPDATE)
487 id = add_string(name);
488 cache_entry(name, STRINGS_TABLE, id);
493 else if (!strcmp(data_type, "none"))
498 /* now set value in argv */
499 *(int *)argv[vo->index] = id;
505 /* Make sure the data fits in the field */
506 int validate_len(register char *argv[], register struct valobj *vo)
508 EXEC SQL BEGIN DECLARE SECTION;
511 EXEC SQL END DECLARE SECTION;
513 tname = table_name[vo->table];
514 cname = vo->namefield;
515 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
516 WHERE table_name = UPPER(:tname) AND column_name = UPPER(:cname);
518 if ((strlen(argv[vo->index]) > len) &&
519 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
520 argv[vo->index][len] = '\0'; /* truncate */
525 /* Lock the table named by the validation object */
527 int lock_table(struct valobj *vo)
530 sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
531 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
540 * Get a read lock on the table by accessing the magic lock
541 * record. Certain tables are constructed so that they contain
542 * an id field whose value is zero and a modtime field. We
543 * manipulate the modtime field of the id 0 record to effect
544 * locking of the table
547 int readlock_table(struct valobj *vo)
550 sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
551 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
558 return MR_EXISTS; /* validate_fields expects us to return
559 * this value if everything went okay
563 /* Check the database at startup time. */
565 void sanity_check_database(void)
567 EXEC SQL BEGIN DECLARE SECTION;
569 EXEC SQL END DECLARE SECTION;
571 /* Sometimes a crash can leave strings_id in numvalues in an
572 incorrect state. Check for that and fix it. */
574 EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name = 'strings_id';
576 for (id = oid + 1; sqlca.sqlcode == 0; id++)
578 EXEC SQL SELECT string_id INTO :id FROM strings
579 WHERE string_id = :id;
583 EXEC SQL UPDATE numvalues SET value = :id - 1 WHERE name = 'strings_id';
587 char *sqlbuffer[QMAXARGS];
589 /* Dynamic SQL support routines */
590 SQLDA *mr_alloc_sqlda()
595 it = sqlald(QMAXARGS, ARGLEN, 0);
598 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
602 for (j = 0; j < QMAXARGS; j++)
604 it->V[j] = sqlbuffer[j] = malloc(ARGLEN);
605 it->T[j] = 97; /* 97 = CHARZ = null-terminated string */
613 /* Convert normal Unix-style wildcards to SQL voodoo */
614 int convert_wildcards(char *arg)
616 static char buffer[ARGLEN];
617 register char *s, *d;
619 for (d = buffer, s = arg; *s; s++)
646 /* Copy back into argv */
652 /* This version includes uppercase conversion, for things like gmac.
653 * This is necessary because "LIKE" doesn't work with "uppercase()".
654 * Including it in a wildcard routine saves making two passes over
655 * the argument string.
657 int convert_wildcards_uppercase(char *arg)
659 static char buffer[ARGLEN];
660 register char *s, *d;
662 for (d = buffer, s = arg; *s; s++)
683 *d++ = toupper(*s); /* This is the only diff. */
689 /* Copy back into argv */
696 /* Adds a string to the string table. Returns the id number.
699 int add_string(char *nm)
701 EXEC SQL BEGIN DECLARE SECTION;
702 char buf[256], *name = nm;
704 EXEC SQL END DECLARE SECTION;
706 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
708 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
710 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :nm);