/* SQLUPD.C - implement SHSQL UPDATE
 * Copyright 1998-2001 Stephen C. Grubb  (www.sgpr.net) .
 * This code is covered under the GNU General Public License (GPL);
 * see the file ./Copyright for details. */


/* New alg:

   set ixflag = 1 if any updated fields are indexed
   submit select command
   for each physloc that comes back {
	read rec, parse into data array
	substitute new values into data array
	compare new length against old length
	if new length > old length || ixflag
	   do a rubout+append
	else
	   do an overwrite plus rubout to EOR
        }

 */


#include "tdhkit.h"
#include "shsql.h"

int
SHSQL_update( frag, nfrag, wherefrag )
char *frag[];
int nfrag;
int wherefrag;
{
int k;

char data[MAXITEMS][DATAMAXLEN+1];
char data2[MAXITEMS][DATAMAXLEN+1];
FILE *fp;
int i, j, ix, stat;
char tok[DATAMAXLEN+1];
char buf[ MAXRECORDLEN ]; 
char table[ MAXPATH ];
int nfdf;
char fldname[80];
int len;
int fld;
int npairs;
int orinsert;
char changeflag[MAXITEMS];
int do_append;
char **rows;
struct selectparms sp;
long physloc;
int reclen, newlen, nextlen;
char indexedfields[512];
int ilt, ilo, doing_recordlock, recordlock_error, errorflag;


strcpy( table, "" );
orinsert = 0;
do_append = 0;

for( i = 0; i < MAXITEMS; i++ ) {
	changeflag[i] = '0';
	strcpy( data[i], "" );
	}

k = 0;

/* UPDATE.. */
ix = 0;
GL_getok( frag[k], &ix );  /* skip over 'update' */

strcpy( table, GL_getok( frag[k], &ix ) );   /* table */

/* nfdf = TDH_readfdf( recordid, NULL, NULL, NULL );
 * if( nfdf < 1 ) return( SHSQL_err( 250, "sql update: unrecognized table name", recordid ) );
 */

TDH_altfmap( 1 );
stat = SHSQL_loadfieldmap( table );
if( stat != 0 ) return( stat );
nfdf = fieldmap( "" );
TDH_altfmap( 0 );


/* get next token, if any (could be NEWROW).. */
strcpy( tok, GL_getok( frag[k], &ix ) );  
if( stricmp( tok, "orinsert" )==0 ) orinsert = 1;

/* if an index field list file exists, get it.. */
SHSQL_getixfields( table, indexedfields );
/* fprintf( stderr, "[fields in table %s having indexes are: %s]\n", table, indexedfields ); */


/* do the SET line.. */
k++;
if( k >= nfrag ) return( SHSQL_err( 251, "sql update: 'set' expected", "" ) );

ix = 0;
strcpy( tok, GL_getok( frag[k], &ix ) );
if( strcmp( tok, "set" ) != 0 ) return( SHSQL_err( 252, "sql update: 'set' expected", "" ) );

TDH_altfmap( 1 );
ilt = fieldmap( "_locktime" );
ilo = fieldmap( "_lockowner" );
TDH_altfmap( 0 );
if( ilt >= 0 && ilo >= 0 ) doing_recordlock = 1;
else doing_recordlock = 0;
recordlock_error = 0;


/* get fieldname value pairs.. */
npairs = 0;
while( 1 ) {
	if( k == wherefrag || k >= nfrag ) break;

	ix = 0;
	strcpy( fldname, GL_getok( frag[k], &ix ) );


	/* skip over "set" in 1st line.. */
	if( npairs == 0 && stricmp( fldname, "set" )==0 ) strcpy( fldname, GL_getok( frag[k], &ix ) );

	/* check to see if modified field is indexed.. if so force new record to be appended */
	if( GL_smember( fldname, indexedfields )) { /* fprintf( stderr, "[field is indexed!]" ); */ do_append = 1; }

	GL_getok( frag[k], &ix ); /* skip = */
	GL_getchunk( tok, frag[k], &ix, " ," );
	if( tok[0] == '\0' ) return( SHSQL_err( 252, "sql update: at least one 'fieldname = value' pair expected", "" ) );
	TDH_altfmap( 1 );
	fld = fieldmap( fldname );
	TDH_altfmap( 0 );
	if( fld < 0 ) return( SHSQL_err( 253, "sql update: unrecognized field name given", fldname ) );
	if( strncmp( tok, "@_QS", 4 ) == 0 ) TDH_getvar( &tok[1], tok );
	if( strcmp( tok, "" )==0 || stricmp( tok, TDH_dbnull )==0 ) strcpy( tok, TDH_dbnull ); /* avoid case probs w/ null */
	for( j = 0, len = strlen( tok ); j < len; j++ ) if( isdelim( tok[j] ) ) tok[j] = '_';  /* datadelim */

	strcpy( data[ fld ], tok );
	changeflag[ fld ] = '1';

	GL_getchunk( tok, frag[k], &ix, " ," ); /* check for additional junk on line..  */
	if( tok[0] != '\0' ) return( SHSQL_err( 254, "sql update: error parsing the 'fieldname = value' pairs (missing comma?)", tok ));
	npairs++;
	k++;
	}
if( npairs == 0 ) return( SHSQL_err( 255, "sql update: at least one 'fieldname = value' pair expected", table ) );


stat = SHSQL_lock( table );
if( stat != 0 ) return( SHSQL_err( SHSQL_TABLELOCKED, "update refused.. try again in a few minutes", table ) );



/* get WHERE clause.. */
if( wherefrag < 0 ) return( SHSQL_err( 256, "sql update: 'where' expected", "" ) );

/* build retrieval command.. */
sprintf( buf, "select * from %s %s %s", table, frag[ wherefrag ], ((nfrag-1)>wherefrag)? frag[wherefrag+1]:"" );
SHSQL_loclistmode( 1 ); /* turn on loclist mode */
stat = SHSQL_command( buf );
if( stat != 0 ) return( SHSQL_err( stat, "sql update: error on retrieval command", table ));
stat = SHSQL_fetchrows( &rows, SHSQL_maxrows_update, &sp );   
if( stat != 0 ) return( SHSQL_err( stat, "sql update: error on fetch", table )); 
SHSQL_free( rows, &sp ); /* we can free the rows right away because all we are after is the physloc list.. */
SHSQL_loclistmode( 0 ); /* turn off loclist mode */

sprintf( buf, "%s/data/%s", SHSQL_projdir, table );
fp = fopen( buf, "r+" );
if( fp == NULL ) return( SHSQL_err( 256, "sql update: cannot open table file", table ) );

if( sp.nrows == 0 ) { /* no row found */
	if( orinsert ) {
		for( i = 0; i < nfdf; i++ ) {
			if( data[i][0] == '\0' ) strcpy( data[i], TDH_dbnull );
			}
		stat = SHSQL_newrec( table, fp, data, nfdf, buf, 1 );
		if( stat != 0 ) {
			fclose( fp );
			return( SHSQL_err( stat, "sql update: write error on 'update orinsert' new record", table ) );
			}	
		SHSQL_nrows = 1;
		}
	else SHSQL_nrows = 0;

	fclose( fp );
	SHSQL_unlock( );
	return( 0 );   /* no matching rows found is not an error, even if ORINSERT not specified */
	}
else if( stat != 0 ) { fclose( fp ); SHSQL_unlock(); return( stat ); }  /* any retrieval error causes update to be cancelled */




/* ====== loop across all records found.. ======== */

errorflag = 0;
for( i = 0; ; i++ ) {
	stat = SHSQL_nextloc( &physloc, &reclen );
	if( stat ) break;
	stat = fseek( fp, physloc, SEEK_SET );
	if( stat ) { errorflag = 257; continue; } /* SHSQL_err - sql update: write seek error on data file */

	fgets( buf, MAXRECORDLEN-1, fp );

	/* parse into fields - data2 array.. */
	for( j = 0, ix = 0; j < nfdf; j++ ) SHSQL_getfld( data2[j], buf, &ix ); 	/* datadelim */

	if( doing_recordlock ) {
		stat = SHSQL_check_rlock( data2[ ilt ], data2[ ilo ] );
		if( stat != 0 ) {
			recordlock_error = 1;
			continue;  /* record is locked */
			}
		else strcpy( data2[ilt], TDH_dbnull ); /* null out _locktime; leave _lockowner */
		}

	SHSQL_log( "update-old", table, NULL, 0, buf );  /* size isn't used when supplying via buf */

	/* copy new fields from data -> data2 */
	for( j = 0; j < nfdf; j++ ) {
		if( changeflag[j] == '1' ) strcpy( data2[j], data[j] );
		}

	/* convert any data2 "" fields to dbnull.. */
	for( j = 0; j < nfdf; j++ ) if( data2[j][0] == '\0' ) strcpy( data2[j], TDH_dbnull );



	/* ========= now update/replace data record .. ========= */

	/* build new record (keep track of total length) (data2) */
        for( j = 0, newlen = 0; j < nfdf; j++ ) {
                nextlen =  newlen + strlen( data2[j] );
                if( nextlen >= (MAXRECORDLEN-2) ) { errorflag = 258; goto NEXT; }
                strcpy( &buf[ newlen ], data2[j] );
                newlen = nextlen;
                strcpy( &buf[ newlen++ ], SHSQL_delims );
                }

	SHSQL_log( "update-new", table, NULL, newlen, buf );

	if (newlen >= reclen) do_append = 1;

        if( !do_append ) {
                /* pad remainder of record.. */
                for( j = newlen; j < reclen-1; j++ ) buf[j] = SHSQL_delim;
                buf[j] = '\0';

                /* write out the record content - but not the final newline */
                fseek( fp, physloc, SEEK_SET );
                fputs( buf, fp );
                fflush( fp );
                }

        else    {
                stat = SHSQL_newrec( table, fp, NULL, newlen, buf, 1 );
                if( stat != 0 ) { errorflag = stat; continue; }

                /* rub out old record */
                for( j = 0; j < reclen-1; j++ ) buf[j] = SHSQL_delim;
                buf[j] = '\0';

                fseek( fp, physloc, SEEK_SET );
                fputs( buf, fp );
                fflush( fp );
		}

	NEXT:;
	} 


SHSQL_freeloclist();

SHSQL_nrows = i;

fclose( fp );
SHSQL_unlock( );

if( recordlock_error ) return( SHSQL_RECORDLOCKED );
return( errorflag );
}
