# line 1 "IngresSql.c.sc"	
static char *sccs_id= (sccs_id, "@(#)IngresSql.c	3.6 7/28/92");

#ifndef NO_INGRES

#include "Types.h"
#include "stdio.h"

extern "C" {
#include <strings.h>
}
#include "IngresSql.h"

# include "/u/tnosoes/Ingres6/ingres/files/eqsqlca.h"
    extern IISQLCA sqlca;   /* SQL Communications Area */

# line 16 "IngresSql.c.sc"	/* host code */
static int n_ignore;

void HandleError()
{
  char errormsg[201];

# line 24 "IngresSql.c.sc"	/* copy */
  {
    IIeqiqio((short *)0,1,32,200,errormsg,"errortext");
  }

# line 26 "IngresSql.c.sc"	/* host code */
    cerr << "SQL ERROR: " << errormsg;
    if (n_ignore) {
	--n_ignore;
	cerr << " (Ignored, " << n_ignore << " left)\n";
	return;
    }
    exit(-1);
}

void IgnoreSqlError(unsigned n)
{
  n_ignore= n;
}

int SqlErrorsLeft()
{
  return n_ignore;
}

  extern char *dbname;
  extern char crsr[10];

//--- IngresSql ---------------------------------------------------------------

IngresSql::IngresSql(char *name)
{
    dbname = strdup(name);
# line 55 "IngresSql.c.sc"	/* connect */
  {
    IIsqInit(&sqlca);
    IIsqConnect(0,dbname,(char *)0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 56 "IngresSql.c.sc"	/* set */
  {
    IIsqInit(&sqlca);
    IIwritio(0,(short *)0,1,32,0,"set lockmode session where readlock=\
nolock");
    IIsyncup((char *)0,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 57 "IngresSql.c.sc"	/* host code */
}

IngresSql::~IngresSql()
{
# line 61 "IngresSql.c.sc"	/* disconnect */
  {
    IIsqInit(&sqlca);
    IIsqDisconnect();
  }
# line 62 "IngresSql.c.sc"	/* host code */
    free(dbname);
}

char* IngresSql::GetDbName()
{
    return dbname;
}

void IngresSql::CreateTable( char *tabname, Sql_Field *fs, int fs_size )
{
  char cmd[1000];

    // From 'fs' build the 'strct'-string for ingres
    char strct[1000], tmp[1000];
    strct[0]= '\0';

    for (int i=0 ; i < fs_size ; i++) {
	switch (fs[i].type) {
	case cUndefinedType:
	    break;
	case cStrType:
	case cVStrType:
	    if (fs[i].length <= 0 || fs[i].length > 2000) {
		cerr << "ERROR: IngresSql::CreateTable : invalid stringsize ("
		     << fs[i].length << ")!\n";
		exit(-1);
	    }
	    sprintf(tmp, "%s %s(%d)", fs[i].name,
		fs[i].type == cStrType ? "char": "vchar",
		fs[i].length);
	    break;
	case cCharType:
	    sprintf(tmp, "%s char(1)", fs[i].name);
	    break;
	case cShortType:
	    sprintf(tmp, "%s integer2", fs[i].name);
	    break;
	case cLongType:
	    sprintf(tmp, "%s integer4", fs[i].name);
	    break;
	case cFloatType:
	    sprintf(tmp, "%s float4", fs[i].name);
	    break;
	case cDoubleType:
	    sprintf(tmp, "%s float8", fs[i].name);
	    break;
	}
	// Add ',' except the first time
        if (strct[0])	
            strcat(strct, ", ");   
        strcat(strct, tmp);
    }

    sprintf(cmd, "CREATE TABLE %s (%s)", tabname, strct);
# line 118 "IngresSql.c.sc"	/* execute */
  {
    IIsqInit(&sqlca);
    IIsqExImmed(cmd);
    IIsyncup((char *)0,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 119 "IngresSql.c.sc"	/* host code */
}

void IngresSql::Insert( char *tabname, Sql_Field *fs, int fs_size )
{
    const int MaxCmd=	5000;
    const int MaxVal=	MaxCmd - 100;

  char cmd[MaxCmd];

    char val[MaxVal];
    char *lastp= val;

    for (int i=0 ; i < fs_size ; i++) {
        int tmplen = (fs[i].type==cStrType || fs[i].type==cVStrType) ?
		fs[i].length : 20;
	if (tmplen >= MaxVal-1) {
		cerr << "Insert: Field too long (" << tmplen << ")\n";
		return;
	}

	char tmp[MaxVal];

	switch (fs[i].type) {
	case cUndefinedType:
	    break;
	case cStrType:
	case cVStrType:
	    char *pch;
	    while (pch = (char*)strchr(fs[i].strval, '\''))
		*pch = '`';
            sprintf(tmp, "'%s'", fs[i].strval);
	    break;
	case cCharType:
	    if (fs[i].charval == '\'')
		fs[i].charval = '`';
	    sprintf(tmp, "'%c'", fs[i].charval);
	    break;
	case cShortType:
            sprintf(tmp, "%d", fs[i].shortval);
	    break;
	case cLongType:
            sprintf(tmp, "%ld", fs[i].longval);
	    break;
	case cFloatType:
            sprintf(tmp, "%f", fs[i].floatval);
	    break;
	case cDoubleType:
            sprintf(tmp, "%lf", fs[i].doubleval);
	    break;
	}

	int extra_len= strlen(tmp);
	if (lastp + extra_len >= val + MaxVal - 3) { // 3 = len('\0' + ',' + ' ')
		cerr << "Insert: Fields too long\n";
		return;
	}
        if (lastp != val) {
	    *lastp++= ','; *lastp++= ' ';
	}
        strcpy(lastp, tmp);
	lastp+= extra_len;
    }

    sprintf(cmd, "INSERT INTO %s VALUES (%s)", tabname, val);
# line 185 "IngresSql.c.sc"	/* execute */
  {
    IIsqInit(&sqlca);
    IIsqExImmed(cmd);
    IIsyncup((char *)0,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 186 "IngresSql.c.sc"	/* host code */
}

void IngresSql::DropTable( char *tabname )
{
  char cmd[100];

/*###    EXEC SQL COMMIT;*/

    sprintf(cmd, "DROP TABLE %s", tabname);
# line 197 "IngresSql.c.sc"	/* execute */
  {
    IIsqInit(&sqlca);
    IIsqExImmed(cmd);
    IIsyncup((char *)0,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 198 "IngresSql.c.sc"	/* host code */
}


//--- IngresSqlSelect ---------------------------------------------------------

int gCursorId = 0;

IngresSqlSelect::IngresSqlSelect( char *tabname, char *whstr, char *selstr, eIngresMode ingmode, char* fn )
{
    ingresmode = ingmode;
    Bad= 0;

    if (ingresmode == cWriteMode) {	// Open file for writing
	if (!(ingfp = fopen(fn, "w"))) {
	    fprintf(stderr, "ERROR: IngresSqlSelect: Can't open '%s' for writing!\n", fn);
	    exit(-1);
	}
    }
    if (ingresmode == cReadMode) { 	// Open file for reading
	if (!(ingfp = fopen(fn, "r"))) {
	    fprintf(stderr, "ERROR: IngresSqlSelect: Can't open '%s' for reading!\n", fn);
	    exit(-1);
	}
    }

    // Allocate memory for the table-description
    sqlda = (IISQLDA *)malloc(   IISQDA_HEAD_SIZE
			      + (IISQDA_VAR_SIZE * SQLDA_VARS));
    sqlda->sqln = SQLDA_VARS;
    IISQLVAR *sqlvar;

    if (ingresmode != cReadMode) {
  char cmd[1000];

# line 235 "IngresSql.c.sc"	/* host code */
	tablename = (char*)strdup(tabname);
	wherestr = (whstr) ? (char*)strdup(whstr) : NULL;
	if (wherestr)
	    sprintf(cmd, "SELECT %s FROM %s WHERE (%s)",
						selstr, tabname, wherestr);
	else
	    sprintf(cmd, "SELECT %s FROM %s", selstr, tabname);

	IgnoreSqlError(1);	// Handle non existent table
# line 244 "IngresSql.c.sc"	/* prepare */
  {
    IIsqInit(&sqlca);
    IIsqPrepare(0,"selcmd",(char *)0,0,cmd);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 245 "IngresSql.c.sc"	/* host code */
	if (SqlErrorsLeft() < 1) {
		Bad= 1;
		return;
	}
	IgnoreSqlError(0);

# line 251 "IngresSql.c.sc"	/* describe */
  {
    IIsqInit(&sqlca);
    IIsqDescribe(0,"selcmd",sqlda,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 252 "IngresSql.c.sc"	/* host code */
	if (sqlda->sqld > sqlda->sqln) {
	    cerr << "ERROR IngresSql: Not enough memory allocated\n";
	    exit(-1);
	}

	sqlvar = sqlda->sqlvar;
	if (ingresmode == cWriteMode) {		// write vital sqlda
	    fprintf(ingfp, "%8s %ld %d %d\n", sqlda->sqldaid, sqlda->sqldabc,
					      sqlda->sqln, sqlda->sqld);
	    for (int i=0 ; i < sqlda->sqld ; i++) {
		fprintf(ingfp, "%d %d %d %.*s\n",
					  sqlvar[i].sqltype, sqlvar[i].sqllen,
					  sqlvar[i].sqlname.sqlnamel,
					  sqlvar[i].sqlname.sqlnamel,
					  sqlvar[i].sqlname.sqlnamec);
	    }
	}
    }
    else {
	// initialize the sqlda-struct
	// read: sqlda->sqldaid, sqlda->sqldabc, sqlda->sqln, sqlda->sqld
	fscanf(ingfp, "%8s%ld%hd%hd",
					sqlda->sqldaid, &sqlda->sqldabc,
					&sqlda->sqln, &sqlda->sqld);
	sqlvar = sqlda->sqlvar;
	for (int i=0 ; i < sqlda->sqld ; i++) {
	    fscanf(ingfp, "%hd%hd%hd%s",
				      &sqlvar[i].sqltype, &sqlvar[i].sqllen,
				      &sqlvar[i].sqlname.sqlnamel,
				      sqlvar[i].sqlname.sqlnamec);
	}
    }

    long memsize;
    for (int i=0 ; i < sqlda->sqld ; i++) {
	switch(abs(sqlvar[i].sqltype)) {
	case IISQ_CHA_TYPE:
	    memsize = sqlvar[i].sqllen + 1;
	    break;
	case IISQ_VCH_TYPE:
	    memsize = sqlvar[i].sqllen + 3; // 2 bytes length info + '\0'
	    break;
	case IISQ_INT_TYPE:
	    switch (sqlvar[i].sqllen) {
	    case 1:
		memsize = sizeof(char);
		break;
	    case 2:
		memsize = sizeof(short);
		break;
	    case 4:
		memsize = sizeof(long);
		break;
	    default:
		cerr << "ERROR IngresSql: invalid 'int'-length\n";
		exit(-1);
	    }
	    break;
	case IISQ_FLT_TYPE:
	    switch (sqlvar[i].sqllen) {
	    case 4:
		memsize = sizeof(float);
		break;
	    case 8:
		memsize = sizeof(double);
		break;
	    default:
		cout << "WARNING: invalid 'int'-length\n";
		exit(-1);
	    }
	    break;
	default:
	    cerr << "ERROR IngresSqlSelect: invalid sqltype " << sqlvar[i].sqltype << '\n';
	    exit(-1);
	}  

	sqlvar[i].sqldata = (char *)malloc(memsize+5);
	if (sqlvar[i].sqltype < 0)
	    sqlvar[i].sqlind = (short *)malloc(sizeof(short));
    }

    if (ingresmode != cReadMode) {
	sprintf(crsr, "%4d",  gCursorId++);
# line 336 "IngresSql.c.sc"	/* open */
  {
    IIsqInit(&sqlca);
    IIcsOpen(crsr,0,0);
    IIwritio(0,(short *)0,1,32,0,"selcmd");
    IIwritio(0,(short *)0,1,32,0," for readonly ");
    IIcsQuery(crsr,0,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 337 "IngresSql.c.sc"	/* host code */
    }
}

IngresSqlSelect::~IngresSqlSelect()
{
    if (!IsBad()) {

      if (ingresmode != cReadMode) 
# line 345 "IngresSql.c.sc"	/* close */
  {
    IIsqInit(&sqlca);
    IIcsClose(crsr,0,0);
    if (sqlca.sqlcode < 0) 
      HandleError();
  }

# line 347 "IngresSql.c.sc"	/* host code */
      IISQLVAR *sqlvar = sqlda->sqlvar;

      for (int i=0 ; i < sqlda->sqld ; i++) {
	free(sqlvar[i].sqldata);
	if (sqlvar[i].sqltype < 0)
            free(sqlvar[i].sqlind);
      }
    }

    free(sqlda);
    if (ingresmode != cReadMode) {
	free(tablename);
	if (wherestr)
	    free(wherestr);
    }

    if (ingresmode == cWriteMode || ingresmode == cReadMode)
	fclose(ingfp);
}

void IngresSqlSelect::ShowSqlda()
{
    IISQLVAR *sqlvar = sqlda->sqlvar;

    for (int i=0 ; i < sqlda->sqld ; i++) {
	cout << "NAME: " << sqlvar[i].sqlname.sqlnamec << ", ";

	switch(abs(sqlvar[i].sqltype)) {
	case IISQ_CHA_TYPE:
	    cout << " TYPE: char, LEN: " << sqlvar[i].sqllen << '\n';
	    break;
	case IISQ_VCH_TYPE:
	    cout << " TYPE: vchar, LEN: " << sqlvar[i].sqllen << '\n';
	    break;
	case IISQ_INT_TYPE:
	    cout << " TYPE: int, LEN: " << sqlvar[i].sqllen << '\n';
	    break;
	case IISQ_FLT_TYPE:
	    cout << " TYPE: float, LEN: " << sqlvar[i].sqllen << '\n';
	    break;
	default:
	    cout << '\n';
	    cerr << " ERROR ShowSqlda: invalid sqltype (" << sqlvar[i].sqltype << '\n';
		exit(-1);
	    }
    }   
    cout << '\n';
}

int IngresSqlSelect::operator()()
{
    IISQLVAR *sqlvar = sqlda->sqlvar;
    int eof = 0;

    switch (ingresmode) {
    case cNormalMode:					// Nothing ingresmode
# line 403 "IngresSql.c.sc"	/* fetch */
  {
    IIsqInit(&sqlca);
    if (IIcsRetrieve(crsr,0,0) != 0) {
      IIcsDaGet(0,sqlda);
      IIcsERetrieve();
    } /* IIcsRetrieve */
    if (sqlca.sqlcode < 0) 
      HandleError();
  }
# line 404 "IngresSql.c.sc"	/* host code */
	break;
    case cWriteMode:					// Write to file
# line 406 "IngresSql.c.sc"	/* fetch */
  {
    IIsqInit(&sqlca);
    if (IIcsRetrieve(crsr,0,0) != 0) {
      IIcsDaGet(0,sqlda);
      IIcsERetrieve();
    } /* IIcsRetrieve */
    if (sqlca.sqlcode < 0) 
      HandleError();
  }

# line 408 "IngresSql.c.sc"	/* host code */
	// Now write data to file
	for (int i=0 ; !sqlca.sqlcode && i < sqlda->sqld ; i++) {
	    switch(abs(sqlvar[i].sqltype)) {
	    case IISQ_CHA_TYPE:
		fprintf(ingfp, "%.*s\n", sqlvar[i].sqllen,
				    (char*)sqlda->sqlvar[i].sqldata);
		break;
	    case IISQ_VCH_TYPE:
		char *data= sqlda->sqlvar[i].sqldata;
		short len= * (short*) data;
		fprintf(ingfp, "%.*s\n", len, data+2);
		break;
	    case IISQ_INT_TYPE:
		switch (sqlvar[i].sqllen) {
		case 1:
		    fprintf(ingfp, "%c\n", (*(char*)sqlda->sqlvar[i].sqldata));
		    break;
		case 2:
		    fprintf(ingfp, "%d\n",(*(short*)sqlda->sqlvar[i].sqldata));
		    break;
		case 4:
		    fprintf(ingfp, "%ld\n",(*(long*)sqlda->sqlvar[i].sqldata));
		    break;
		}
		break;
	    case IISQ_FLT_TYPE:
		switch (sqlvar[i].sqllen) {
		case 4:
		    fprintf(ingfp, "%f\n",(*(float*)sqlda->sqlvar[i].sqldata));
		    break;
		case 8:
		    fprintf(ingfp, "%lf\n", (*(double*)sqlda->sqlvar[i].sqldata));
		    break;
		}
		exit(-1);
		break;
	    default:
		cout << '\n';
		cerr << " ERROR ShowSqlda: invalid sqltype ("
		     << sqlvar[i].sqltype << '\n';     
		exit(-1);
	    }
	}
	break;
    case cReadMode:					// Read from file
	// Now read data from file
	for (i=0 ; i < sqlda->sqld ; i++) {
	    switch(abs(sqlvar[i].sqltype)) {
	    case IISQ_CHA_TYPE:
	    case IISQ_VCH_TYPE:
		if (fgets(sqlda->sqlvar[i].sqldata,
			  sqlda->sqlvar[i].sqllen+1, ingfp) == NULL)
		    eof = 1;
		getc(ingfp);
		break;
	    case IISQ_INT_TYPE:
		switch (sqlvar[i].sqllen) {
		case 1:
		    if (fscanf(ingfp, "%c\n", (char*)sqlda->sqlvar[i].sqldata) == EOF)
			eof = 1;
		    break;
		case 2:
		    if (fscanf(ingfp,"%hd\n", (short*)sqlda->sqlvar[i].sqldata) == EOF)
			eof = 1;
		    break;
		case 4:
		    if (fscanf(ingfp, "%ld\n",(long*)sqlda->sqlvar[i].sqldata) == EOF)
			eof = 1;
		    break;
		}
		break;
	    case IISQ_FLT_TYPE:
		switch (sqlvar[i].sqllen) {
		case 4:
		    if (fscanf(ingfp, "%f\n",(float*)sqlda->sqlvar[i].sqldata) == EOF)
			eof = 1;
		    break;
		case 8:
		    if (fscanf(ingfp, "%lf\n", (double*)sqlda->sqlvar[i].sqldata) == EOF)
			eof = 1;
		    break;
		}
		exit(-1);
		break;
	    default:
		cout << '\n';
		cerr << " ERROR ShowSqlda: invalid sqltype ("
		     << sqlvar[i].sqltype << '\n';     
		exit(-1);
	    }
	}
	break;
    }

    return (!sqlca.sqlcode && !eof);
}

int IngresSqlSelect::GetRecordCnt()
{
    if (wherematch == -1) {
	IngresSqlSelect countsel(tablename, wherestr, "COUNT(*)");
	if (!countsel()) {
	    cerr << "ERROR GetRecordCnt: no count-value returned\n";
	    exit(-1);
	}
	if (countsel.GetFieldType(0) != cLongType) {
	    cerr << "ERROR GetRecordCnt: type of count-value not 'int'\n";
	    exit(-1);
	}
	wherematch = *((int *)countsel.GetFieldValue(0));
    }

    return wherematch;
}

eFieldType IngresSqlSelect::GetFieldType(int fld)
{
    if (fld >= GetFieldCnt()) {
	cerr << "ERROR GetFieldType: wrong 'fld'-value (" << fld << ")\n";
	exit(-1);
    }

    eFieldType type = cUndefinedType;
    IISQLVAR *sqlvar = sqlda->sqlvar;

    switch(abs(sqlvar[fld].sqltype)) {
    case IISQ_CHA_TYPE:
	type = cStrType;
	break;
    case IISQ_VCH_TYPE:
	type = cVStrType;
	break;
    case IISQ_INT_TYPE:
	switch (sqlvar[fld].sqllen) {
	case 1:
	    type = cCharType;
	    break;
	case 2:
	    type = cShortType;
	    break;
	case 4:
	    type = cLongType;
	    break;
	}
	break;
    case IISQ_FLT_TYPE:
	switch (sqlvar[fld].sqllen) {
	case 4:
	    type = cFloatType;
	    break;
	case 8:
	    type = cDoubleType;
	    break;
	}
	break;
    }

    return type;
}

eFieldType IngresSqlSelect::GetFieldType(char* fldname)
{
    IISQLVAR *sqlvar = sqlda->sqlvar;

    int fld = -1;
    for (int i=0 ; i < sqlda->sqld && fld == -1 ; i++) {
	if (!strncmp(sqlvar[i].sqlname.sqlnamec, fldname, sqlvar[i].sqlname.sqlnamel))
	    fld = i;
    }

    if (fld == -1) {
	cerr << "ERROR GetFieldType: '" << fldname << "' not found.\n";
	exit(-1);
    }

    return GetFieldType(fld);
}

void* IngresSqlSelect::GetFieldValue(int fld)
{
    if (fld >= GetFieldCnt()) {
	cerr << "ERROR GetFieldValue: wrong 'fld'-value (" << fld << ")\n";
	exit(-1);
    }

    if (abs(sqlda->sqlvar[fld].sqltype) == IISQ_VCH_TYPE) {
	char *data= sqlda->sqlvar[fld].sqldata;
	short len= * (short*) data;
	data[2+len]= '\0';
	return data+2;
    } else
	return sqlda->sqlvar[fld].sqldata;
}

void* IngresSqlSelect::GetFieldValue(char *fldname)
{
    IISQLVAR *sqlvar = sqlda->sqlvar;

    for (int i=0 ; i < sqlda->sqld; i++) {
	if (!strncmp(sqlvar[i].sqlname.sqlnamec, fldname, sqlvar[i].sqlname.sqlnamel))
	    return GetFieldValue(i);
    }

    cerr << "ERROR GetFieldValue: '" << fldname << "' not found.\n";
    exit(-1);
}

int IngresSqlSelect::GetFieldSize(int fld)
{
    if (fld >= GetFieldCnt()) {
	cerr << "ERROR GetFieldSize: wrong 'fld'-value (" << fld << ")\n";
	exit(-1);
    }

    return (int)sqlda->sqlvar[fld].sqllen;
}

int IngresSqlSelect::GetFieldSize(char *fldname)
{
    IISQLVAR *sqlvar = sqlda->sqlvar;

    int fld = -1;
    for (int i=0 ; i < sqlda->sqld && fld == -1 ; i++) {
	if (!strncmp(sqlvar[i].sqlname.sqlnamec, fldname, sqlvar[i].sqlname.sqlnamel))
	    fld = i;
	}

    if (fld == -1) {
	cerr << "ERROR GetFieldSize: '" << fldname << "' not found.\n";
	exit(-1); 
    }

    return (int)sqlda->sqlvar[fld].sqllen;
}

#endif
