From: delgado Date: Thu, 3 Nov 1994 13:50:15 +0000 (+0000) Subject: Initial revision X-Git-Tag: OPSSRC_BASE~24 X-Git-Url: http://andersk.mit.edu/gitweb/moira.git/commitdiff_plain/f7e925bbbb1daa33e2d199ca2a1633ffcbcc0cfe Initial revision --- diff --git a/server/qvalidate.c b/server/qvalidate.c new file mode 100644 index 00000000..8cdf76a3 --- /dev/null +++ b/server/qvalidate.c @@ -0,0 +1,730 @@ +/* + * $Source$ + * $Author$ + * $Header$ + * + * Copyright (C) 1987 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . + * + */ + +#ifndef lint +static char *rcsid_qsupport_dc = "$Header$"; +#endif lint + +#include +#include +#include "query.h" +#include "mr_server.h" +#include +EXEC SQL INCLUDE sqlca; +EXEC SQL INCLUDE sqlda; +#include "qrtn.h" + +extern char *whoami; +extern int ingres_errno, mr_errcode; + +EXEC SQL BEGIN DECLARE SECTION; +extern char stmt_buf[]; +EXEC SQL END DECLARE SECTION; + +EXEC SQL WHENEVER SQLERROR CALL ingerr; + +#ifdef _DEBUG_MALLOC_INC +#undef index +#define dbg_index(str1,c) DBindex(__FILE__, __LINE__, str1, c) +#else +#define dbg_index index +#endif + +/* Validation Routines */ + +validate_row(q, argv, v) + register struct query *q; + char *argv[]; + register struct validate *v; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *name; + char qual[128]; + int rowcount; + EXEC SQL END DECLARE SECTION; + + /* build where clause */ + build_qual(v->qual, v->argc, argv, qual); + + if (log_flags & LOG_VALID) + /* tell the logfile what we're doing */ + com_err(whoami, 0, "validating row: %s", qual); + + /* look for the record */ + sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",q->rtable,qual); + EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; + if(sqlca.sqlcode) + return(MR_INTERNAL); + EXEC SQL DECLARE csr126 CURSOR FOR stmt; + EXEC SQL OPEN csr126; + EXEC SQL FETCH csr126 USING DESCRIPTOR :SQLDA; + EXEC SQL CLOSE csr126; + rowcount = *(int *)SQLDA->sqlvar[0].sqldata; + + if (ingres_errno) return(mr_errcode); + if (rowcount == 0) return(MR_NO_MATCH); + if (rowcount > 1) return(MR_NOT_UNIQUE); + return(MR_EXISTS); +} + +validate_fields(q, argv, vo, n) + struct query *q; + register char *argv[]; + register struct valobj *vo; + register int n; +{ + register int status; + + while (--n >= 0) { + switch (vo->type) { + case V_NAME: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating %s in %s: %s", + vo->namefield, vo->table, argv[vo->index]); + status = validate_name(argv, vo); + break; + + case V_ID: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating %s in %s: %s", + vo->idfield, vo->table, argv[vo->index]); + status = validate_id(q, argv, vo); + break; + + case V_DATE: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating date: %s", argv[vo->index]); + status = validate_date(argv, vo); + break; + + case V_TYPE: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating %s type: %s", + vo->table, argv[vo->index]); + status = validate_type(argv, vo); + break; + + case V_TYPEDATA: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating typed data (%s): %s", + argv[vo->index - 1], argv[vo->index]); + status = validate_typedata(q, argv, vo); + break; + + case V_RENAME: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating rename %s in %s", + argv[vo->index], vo->table); + status = validate_rename(argv, vo); + break; + + case V_CHAR: + if (log_flags & LOG_VALID) + com_err(whoami, 0, "validating chars: %s", argv[vo->index]); + status = validate_chars(argv[vo->index]); + break; + + case V_SORT: + status = MR_EXISTS; + break; + + case V_LOCK: + status = lock_table(vo); + break; + + case V_RLOCK: + status = readlock_table(vo); + break; + case V_WILD: + status = convert_wildcards(argv[vo->index]); + break; + + case V_UPWILD: + status = convert_wildcards_uppercase(argv[vo->index]); + break; + + } + + if (status != MR_EXISTS){ + com_err(whoami,0,"validation failed type=%ld, code=%ld\n",vo->type, status); + return(status); + } + vo++; + } + + if (ingres_errno) return(mr_errcode); + return(MR_SUCCESS); +} + + +/* validate_chars: verify that there are no illegal characters in + * the string. Legal characters are printing chars other than + * ", *, ?, \, [ and ]. + */ +static int illegalchars[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */ + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +validate_chars(s) +register char *s; +{ + while (*s) + if (illegalchars[*s++]) + return(MR_BAD_CHAR); + return(MR_EXISTS); +} + + +validate_id(q, argv, vo) + struct query *q; + char *argv[]; + register struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *name, *tbl, *namefield, *idfield; + int id, rowcount; + EXEC SQL END DECLARE SECTION; + int status; + register char *c; + + name = argv[vo->index]; + tbl = vo->table; + namefield = vo->namefield; + idfield = vo->idfield; + + if ((!strcmp(tbl, "users") && !strcmp(namefield, "login")) || + !strcmp(tbl, "machine") || + !strcmp(tbl, "subnet") || + !strcmp(tbl, "filesys") || + !strcmp(tbl, "list") || + !strcmp(tbl, "cluster") || + !strcmp(tbl, "strings")) { + if (!strcmp(tbl, "machine") || !strcmp(tbl, "subnet")) + for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c); + status = name_to_id(name, tbl, &id); + if (status == 0) { + *(int *)argv[vo->index] = id; + return(MR_EXISTS); + } else if (status == MR_NO_MATCH && !strcmp(tbl, "strings") && + (q->type == APPEND || q->type == UPDATE)) { + id=add_string(name); + cache_entry(name, "STRING", id); + *(int *)argv[vo->index] = id; + return(MR_EXISTS); + } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE) + return(vo->error); + else + return(status); + } + + if (!strcmp(namefield, "uid")) { + sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = %s",idfield,tbl,namefield,name); + } else { + sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",idfield,tbl,namefield,name); + } + EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; + if(sqlca.sqlcode) + return(MR_INTERNAL); + EXEC SQL DECLARE csr127 CURSOR FOR stmt; + EXEC SQL OPEN csr127; + rowcount=0; + EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA; + if(sqlca.sqlcode == 0) { + rowcount++; + EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA; + if(sqlca.sqlcode == 0) rowcount++; + } + EXEC SQL CLOSE csr127; + if (ingres_errno) + return(mr_errcode); + + if (rowcount != 1) return(vo->error); + bcopy(SQLDA->sqlvar[0].sqldata,argv[vo->index],sizeof(int)); + return(MR_EXISTS); +} + +validate_name(argv, vo) + char *argv[]; + register struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *name, *tbl, *namefield; + int rowcount; + EXEC SQL END DECLARE SECTION; + register char *c; + + name = argv[vo->index]; + tbl = vo->table; + namefield = vo->namefield; + if (!strcmp(tbl, "servers") && !strcmp(namefield, "name")) { + for (c = name; *c; c++) + if (islower(*c)) + *c = toupper(*c); + } + sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'", + tbl,tbl,namefield,name); + EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; + if(sqlca.sqlcode) + return(MR_INTERNAL); + EXEC SQL DECLARE csr128 CURSOR FOR stmt; + EXEC SQL OPEN csr128; + EXEC SQL FETCH csr128 USING DESCRIPTOR :SQLDA; + rowcount = *(int *)SQLDA->sqlvar[0].sqldata; + EXEC SQL CLOSE csr128; + + if (ingres_errno) return(mr_errcode); + return ((rowcount == 1) ? MR_EXISTS : vo->error); +} + +validate_date(argv, vo) + char *argv[]; + struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *idate; + double dd; + int errorno; + EXEC SQL END DECLARE SECTION; + + idate = argv[vo->index]; + EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd; + + if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE); + return(MR_EXISTS); +} + + +validate_rename(argv, vo) +char *argv[]; +struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *name, *tbl, *namefield, *idfield; + int id; + EXEC SQL END DECLARE SECTION; + int status; + register char *c; + + c = name = argv[vo->index]; + while (*c) + if (illegalchars[*c++]) + return(MR_BAD_CHAR); + tbl = vo->table; + /* minor kludge to upcasify machine names */ + if (!strcmp(tbl, "machine")) + for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c); + namefield = vo->namefield; + idfield = vo->idfield; + id = -1; + if (idfield == 0) { + if (!strcmp(argv[vo->index], argv[vo->index - 1])) + return(MR_EXISTS); + sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = LEFT('%s',SIZE(%s))", + namefield,tbl,namefield,name,namefield); + EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; + if(sqlca.sqlcode) + return(MR_INTERNAL); + EXEC SQL DECLARE csr129 CURSOR FOR stmt; + EXEC SQL OPEN csr129; + EXEC SQL FETCH csr129 USING DESCRIPTOR :SQLDA; + if(sqlca.sqlcode == 0) id=1; else id=0; + EXEC SQL CLOSE csr129; + + if (ingres_errno) return(mr_errcode); + if (id) + return(vo->error); + else + return(MR_EXISTS); + } + status = name_to_id(name, tbl, &id); + if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1]) + return(MR_EXISTS); + else + return(vo->error); +} + + +validate_type(argv, vo) + char *argv[]; + register struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *typename; + char *val; + int cnt; + EXEC SQL END DECLARE SECTION; + register char *c; + + typename = vo->table; + c = val = argv[vo->index]; + while (*c) { + if (illegalchars[*c++]) + return(MR_BAD_CHAR); + } + + /* uppercase type fields */ + for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c); + + EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias + WHERE name = :typename AND type='TYPE' AND trans = :val; + if (ingres_errno) return(mr_errcode); + return (cnt ? MR_EXISTS : vo->error); +} + +/* validate member or type-specific data field */ + +validate_typedata(q, argv, vo) + register struct query *q; + register char *argv[]; + register struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + char *name; + char *field_type; + char data_type[129]; + int id; + EXEC SQL END DECLARE SECTION; + int status; + register char *c; + + /* get named object */ + name = argv[vo->index]; + + /* get field type string (known to be at index-1) */ + field_type = argv[vo->index-1]; + + /* get corresponding data type associated with field type name */ + EXEC SQL SELECT trans INTO :data_type FROM alias + WHERE name = :field_type AND type='TYPEDATA'; + if (ingres_errno) return(mr_errcode); + if (sqlca.sqlerrd[2] != 1) return(MR_TYPE); + + /* now retrieve the record id corresponding to the named object */ + if (dbg_index(data_type, ' ')) + *dbg_index(data_type, ' ') = 0; + if (!strcmp(data_type, "user")) { + /* USER */ + if (dbg_index(name, '@')) + return(MR_USER); + status = name_to_id(name, data_type, &id); + if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)) + return(MR_USER); + if (status) return(status); + } else if (!strcmp(data_type, "list")) { + /* LIST */ + status = name_to_id(name, data_type, &id); + if (status && status == MR_NOT_UNIQUE) + return(MR_LIST); + if (status == MR_NO_MATCH) { + /* if idfield is non-zero, then if argv[0] matches the string + * that we're trying to resolve, we should get the value of + * numvalues.[idfield] for the id. + */ + if (vo->idfield && !strcmp(argv[0], argv[vo->index])) { + set_next_object_id(q->validate->object_id, q->rtable, 0); + name = vo->idfield; + EXEC SQL REPEATED SELECT value INTO :id FROM numvalues + WHERE name = :name; + if (sqlca.sqlerrd[2] != 1) return(MR_LIST); + } else + return(MR_LIST); + } else if (status) return(status); + } else if (!strcmp(data_type, "machine")) { + /* MACHINE */ + for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c); + status = name_to_id(name, data_type, &id); + if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)) + return(MR_MACHINE); + if (status) return(status); + } else if (!strcmp(data_type, "string")) { + /* STRING */ + status = name_to_id(name, data_type, &id); + if (status && status == MR_NOT_UNIQUE) + return(MR_STRING); + if (status == MR_NO_MATCH) { + if (q->type != APPEND && q->type != UPDATE) return(MR_STRING); + id=add_string(name); + cache_entry(name, "STRING", id); + } else if (status) return(status); + } else if (!strcmp(data_type, "none")) { + id = 0; + } else { + return(MR_TYPE); + } + + /* now set value in argv */ + *(int *)argv[vo->index] = id; + + return (MR_EXISTS); +} + + +/* Lock the table named by the validation object */ + +lock_table(vo) +struct valobj *vo; +{ + sprintf(stmt_buf,"UPDATE %s SET modtime='now' WHERE %s.%s = 0", + vo->table,vo->table,vo->idfield); + EXEC SQL EXECUTE IMMEDIATE :stmt_buf; + if (sqlca.sqlcode == 100){ + fprintf(stderr,"readlock_table: no matching rows found for %s\n", + stmt_buf); + return(MR_INTERNAL); + } + if (ingres_errno) + return(mr_errcode); + if (sqlca.sqlerrd[2] != 1) + return(vo->error); + else + return(MR_EXISTS); +} + +/* + * Get a read lock on the table by accessing the magic lock + * record. Certain tables are constructed so that they contain + * an id field whose value is zero and a modtime field. We + * manipulate the modtime field of the id 0 record to effect + * locking of the table + */ + +readlock_table(vo) + struct valobj *vo; +{ + EXEC SQL BEGIN DECLARE SECTION; + int id; + char buf[256]; + char *tbl, *idfield; + EXEC SQL END DECLARE SECTION; + + tbl=vo->table; + idfield=vo->idfield; + sprintf(buf,"SELECT %s FROM %s WHERE %s.%s = 0", + vo->idfield, vo->table, vo->table, vo->idfield); + EXEC SQL PREPARE stmt FROM :buf; + EXEC SQL DESCRIBE stmt INTO SQLDA; + EXEC SQL DECLARE rcsr CURSOR FOR stmt; + EXEC SQL OPEN rcsr; + EXEC SQL FETCH rcsr USING DESCRIPTOR :SQLDA; + /* Check for no matching rows found - this is + * flagged as an internal error since the table should + * have a magic lock record. + */ + if (sqlca.sqlcode == 100){ + EXEC SQL CLOSE rcsr; + com_err(whoami,0,"readlock_table: no matching rows found for %s\n", + buf); + return(MR_INTERNAL); + } + EXEC SQL CLOSE rcsr; + if (ingres_errno) + return(mr_errcode); + if (sqlca.sqlcode) + return(vo->error); + return(MR_EXISTS); /* validate_fields expects us to return + * this value if everything went okay + */ +} + +/* Check the database at startup time. For now this just resets the + * inprogress flags that the DCM uses. + */ + +sanity_check_database() +{ +} + + +/* Dynamic SQL support routines */ +MR_SQLDA_T *mr_alloc_SQLDA() +{ + MR_SQLDA_T *it; + short *null_indicators; + register int j; + + if((it=(MR_SQLDA_T *)malloc(sizeof(MR_SQLDA_T)))==NULL) { + com_err(whoami, MR_NO_MEM, "setting up SQLDA"); + exit(1); + } + + if((null_indicators=(short *)calloc(QMAXARGS,sizeof(short)))==NULL) { + com_err(whoami, MR_NO_MEM, "setting up SQLDA null indicators"); + exit(1); + } + + for(j=0; jsqlvar[j].sqldata=(char *)malloc(sizeof(short)+ARGLEN))==NULL) { + com_err(whoami, MR_NO_MEM, "setting up SQLDA variables"); + exit(1); + } + it->sqlvar[j].sqllen=ARGLEN; + it->sqlvar[j].sqlind=null_indicators+j; + null_indicators[j]=0; + } + it->sqln=QMAXARGS; + return it; +} + + +/* Use this after FETCH USING DESCRIPTOR one or more + * result columns may contain NULLs. This routine is + * not currently needed, since db/schema creates all + * columns with a NOT NULL WITH DEFAULT clause. + * + * This is currently dead flesh, since no Moira columns + * allow null values; all use default values. + */ +mr_fix_nulls_in_SQLDA(da) + MR_SQLDA_T *da; +{ + register IISQLVAR *var; + register int j; + int *intp; + + for(j=0, var=da->sqlvar; jsqld; j++, var++) { + switch(var->sqltype) { + case -IISQ_CHA_TYPE: + if(*var->sqlind) + *var->sqldata='\0'; + break; + case -IISQ_INT_TYPE: + if(*var->sqlind) { + intp=(int *)var->sqldata; + *intp=0; + } + break; + } + } +} + +/* Convert normal Unix-style wildcards to SQL voodoo */ +convert_wildcards(arg) + char *arg; +{ + static char buffer[ARGLEN]; + register char *s, *d; + + for(d=buffer,s=arg;*s;s++) { + switch(*s) { + case '*': *d++='%'; *d++='%'; break; + case '?': *d++='_'; break; + case '_': + case '[': + case ']': *d++='*'; *d++ = *s; break; + case '%': *d++='*'; *d++='%'; *d++='%'; break; + default: *d++ = *s; break; + } + } + *d='\0'; + + /* Copy back into argv */ + strcpy(arg,buffer); + + return(MR_EXISTS); +} + +/* This version includes uppercase conversion, for things like gmac. + * This is necessary because "LIKE" doesn't work with "uppercase()". + * Including it in a wildcard routine saves making two passes over + * the argument string. + */ +convert_wildcards_uppercase(arg) + char *arg; +{ + static char buffer[ARGLEN]; + register char *s, *d; + + for(d=buffer,s=arg;*s;s++) { + switch(*s) { + case '*': *d++='%'; *d++='%'; break; + case '?': *d++='_'; break; + case '_': + case '[': + case ']': *d++='*'; *d++ = *s; break; + case '%': *d++='*'; *d++='%'; *d++='%'; break; + default: *d++=toupper(*s); break; /* This is the only diff. */ + } + } + *d='\0'; + + /* Copy back into argv */ + strcpy(arg,buffer); + + return(MR_EXISTS); +} + + +/* Looks like it's time to build an abstraction barrier, Yogi */ +mr_select_any(stmt) + EXEC SQL BEGIN DECLARE SECTION; + char *stmt; + EXEC SQL END DECLARE SECTION; +{ + int result=0; + + EXEC SQL PREPARE stmt FROM :stmt; + EXEC SQL DESCRIBE stmt INTO :SQLDA; + if(SQLDA->sqld==0) /* Not a SELECT */ + return(MR_INTERNAL); + EXEC SQL DECLARE csr CURSOR FOR stmt; + EXEC SQL OPEN csr; + EXEC SQL FETCH csr USING DESCRIPTOR :SQLDA; + if(sqlca.sqlcode==0) + result=MR_EXISTS; + else if((sqlca.sqlcode<0) && mr_errcode) + result=mr_errcode; + else + result=0; + EXEC SQL CLOSE csr; + return(result); +} + + + +/* Adds a string to the string table. Returns the id number. + * + */ +int add_string(name) + EXEC SQL BEGIN DECLARE SECTION; + char *name; + EXEC SQL END DECLARE SECTION; +{ + EXEC SQL BEGIN DECLARE SECTION; + char buf[256]; + int id; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id'; + id++; + EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id'; + + /* Use sprintf to get around problem with doubled single quotes */ + sprintf(buf,"INSERT INTO strings (string_id, string) VALUES (%d, '%s')",id,name); + EXEC SQL EXECUTE IMMEDIATE :buf; + + return(id); +} +