/* SHSQL API
 * Copyright 1998-2002 Stephen C. Grubb  (ploticus.sourceforge.net) .
 * This code is covered under the GNU General Public License (GPL);
 * see the file ./Copyright for details. 
*/

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

static char **rows[ MAXCONNECTS ];
static struct selectparms sp[ MAXCONNECTS ];
static int firsttime[ MAXCONNECTS ] = { 1, 1, 1, 1 };
static int rowcount[ MAXCONNECTS ];			/* changed from 2 to MAXCONNECTS 2/24/04 */
static void *holdf[ MAXCONNECTS ];			/* added scg 2/24/04 */
static int pushflag[ MAXCONNECTS ] = { 0, 0, 0, 0 };	/* added scg 2/24/04 */

extern int unlink();



/* =============================================================================
   SHSQL_sql - submit an sql command and return its execution status (0 = normal). 
 */

int
SHSQL_sql( dbc, sql )
int dbc; 	/* channel identifier */
char *sql; 	/* sql command */
{
int stat;
char tok1[MAXPATH], tok2[MAXPATH];

if( dbc < 0 || dbc > MAXCONNECTS-1 ) return( err( 7945, "shsql: invalid db channel identifier", "" ) );

if( !firsttime[ dbc ] ) SHSQL_free( rows[dbc], &sp[ dbc ] ); /* free results from previous retrieval */
else 	{
	sp[ dbc ].nrows = 0;
	firsttime[ dbc ] = 0;
	}
/* intercept "create sequence" and "select _sequence" */
sscanf( sql, "%s %s", tok1, tok2 );
if( stricmp( tok1, "create" )==0 && stricmp( tok2, "sequence" )==0 ) return( SHSQL_sequence( dbc, sql, 0, tok1 ) );
else if( stricmp( tok1, "select" )==0 && stricmp( tok2, "_sequence" )==0 ) {
	stat = SHSQL_sequence( dbc, sql, 1, tok1 ); /* recursive; passes back generated 'select' to produce seq value */
	if( stat ) return( stat ); /* added scg 4/12/04 */
	stat = SHSQL_command( tok1 );
	SHSQL_unlock();  /* must be done after the last select command is processed */
	}
else stat = SHSQL_command( sql );

stat += SHSQL_fetchrows( &rows[ dbc], 0, &sp[ dbc ] ); 

rowcount[ dbc ] = SHSQL_rowcount(); /* preliminary row count */
return( stat );
}

/* ========================================================================== 
   SHSQL_getrow - get one row of results from most recent SQL SELECT.
   Return 0 if row fetched, 1 if not (no more rows), or an error code. 
   All result fields should be character strings, including "null".
   FIELDS is an array of char pointers; each will point to a result field.
   N will be set to the number of fields.
*/

int
SHSQL_getrow( dbc, fields, n )
int dbc;
char *fields[];  
int *n;
{
int stat;

if( dbc < 0 || dbc > MAXCONNECTS-1 ) return( err( 7945, "shsql getrow: invalid db channel identifier", "" ) );

*n = sp[ dbc ].nitems;
if( pushflag[ dbc ] ) {
	fields = holdf[ dbc ];
	pushflag[ dbc ] = 0;
	stat = 0;
	}
else	{
	stat = SHSQL_row( fields, rows[ dbc ], &sp[ dbc ] );
	holdf[dbc ] = (void *) fields;
	}
if( stat != 0 ) rowcount[ dbc ] = sp[ dbc ].finalrowcount;
return( stat );
}

/* =========================================================================
   SHSQL_pushrow - cause the next SHSQL_getrow() call to access the same row again.
	Added scg 2/24/04.
 */
int
SHSQL_pushrow( dbc )
int dbc;
{
if( dbc < 0 || dbc > MAXCONNECTS-1 ) return( err( 7945, "shsql seek: invalid db channel identifier", "" ) );
pushflag[ dbc ] = 1;
return( 0 );
}


/* ========================================================================= 
   SHSQL_getnames - fetch names of result fields from most recent SQL SELECT.
   Return 0 or an error code.  
   FIELDS is an array of char pointers; each will point to a name.
   N will be set to the number of names (same as number of fields).
 */
int
SHSQL_getnames( dbc, fields, n )
int dbc;
char *fields[]; 
int *n;
{
int i;
if( dbc < 0 || dbc > MAXCONNECTS-1 ) return( err( 7945, "shsql getnames: invalid db channel identifier", "" ) );

for( i = 0; i < sp[ dbc ].nitems; i++ ) fields[i] = sp[ dbc ].itemlist[i];
*n = sp[ dbc ].nitems;
return( 0 );
}

/* ==========================================================================
   SHSQL_tabdef - fetch fieldnames for a table or ordinary file.
   Returns 0 or an error code.

   Caution - 
	This is an undocumented API function; it must be used with care.
	Calls to this routine must be bracketed with TDH_altfmap( 1 ); ... TDH_altfmap( 0 );
	to avoid collision with script processor variable names.

	Even so, field pointers are only valid until the next SQL command is processed on any channel!
 */
int
SHSQL_tabdef( table, fields, n )
char *table;	/* table name or file name */
char *fields[]; 
int *n;
{
int i, stat;
char *TDH_fieldn();

stat = SHSQL_loadfieldmap( table );
if( stat != 0 ) return( stat );

*n = fieldmap( "" );
for( i = 0; i < *n; i++ ) fields[i] = TDH_fieldn( i );
return( 0 );
}



/* ==========================================================================
   SHSQL_getnrows - return # of rows presented or affected by last sql command 
 */
int
SHSQL_getnrows( dbc )
int dbc;
{
if( dbc < 0 || dbc > MAXCONNECTS-1 ) return( err( 7945, "shsql nrows: invalid db channel identifier", "" ) );
return( rowcount[ dbc ] );
}


/* ==========================================================================
   SHSQL_writable - return 0 if current process can write to database, otherwise an error code.
 */
int
SHSQL_writable()
{
FILE *fp;
char buf[256];
if( SHSQL_readonly ) return( 1 ); /* database set to read only */
else	{
	sprintf( buf, "%s/data/__testwrite", SHSQL_projdir );
	fp = fopen( buf, "w" );
	if( fp != NULL ) {
		fclose( fp );
		unlink( buf );
		return( 0 );
		}
	else return( 2 );  /* unable to perform a test write */
	}
}



/* ======== The following are convenience routines that call the above routines.  ====== */

/* ============================= */
/* SHSQL_getitem - convenience routine to retrieve one field using default db channel.  
   Return 0 or an error code. */
int
SHSQL_getitem( sql, result )
char *sql;
char *result;
{
int stat, n;
char *f[10];

strcpy( result, "" );

stat = SHSQL_sql( 0, sql );
if( stat != 0 ) return( stat );

stat = SHSQL_getrow( 0, f, &n );
if( stat != 0 ) return( stat );
if( n != 1 ) return( 5 );

strcpy( result, f[0] );
return( 0 );
}

/* =============================== */
/* SHSQL_getitems - convenience routine to retrieve multiple fields.  Return 0 or an error code. */
int
SHSQL_getitems( sql, fields )
char *sql;
char *fields[];
{
int stat, n;

stat = SHSQL_sql( 0, sql );
if( stat != 0 ) return( stat );

stat = SHSQL_getrow( 0, fields, &n );
if( stat != 0 ) return( stat );

return( 0 );
}



