--- /dev/null
+/*
+ * $Source$
+ * $Author$
+ * $Header$
+ *
+ * Copyright (C) 1987 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char *rcsid_qsupport_dc = "$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+#include <unistd.h>
+#include "query.h"
+#include "mr_server.h"
+#include <ctype.h>
+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; j<QMAXARGS; j++) {
+ if((it->sqlvar[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; j<da->sqld; 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);
+}
+