/* Processed by ecpg (15.2 (Debian 15.2-2)) */
/* These include files are added by the preprocessor */
#include <ecpglib.h>
#include <ecpgerrno.h>
#include <sqlca.h>
/* End of automatic include section */

#line 1 "ECPG.pgm"
/* -*-objc-*- */

/** Implementation of SQLClientECPG for GNUStep
   Copyright (C) 2004 Free Software Foundation, Inc.
   
   Written by:  Richard Frith-Macdonald <rfm@gnu.org>
   Date:	April 2004
   
   This file is part of the SQLClient Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.

   $Date$ $Revision$
   */ 

#include	<Foundation/NSString.h>
#include	<Foundation/NSData.h>
#include	<Foundation/NSDate.h>
#include	<Foundation/NSCalendarDate.h>
#include	<Foundation/NSException.h>
#include	<Foundation/NSProcessInfo.h>
#include	<Foundation/NSNotification.h>
#include	<Foundation/NSUserDefaults.h>
#include	<Foundation/NSMapTable.h>
#include	<Foundation/NSLock.h>
#include	<Foundation/NSNull.h>
#include	<Foundation/NSValue.h>
#include	<Foundation/NSAutoreleasePool.h>

#include	"config.h"

#define SQLCLIENT_PRIVATE       @public

#include	"SQLClient.h"

@interface SQLClientECPG : SQLClient
@end

@interface	SQLClientECPG(Embedded)
- (const char *) blobFromData: (NSData*)data;
- (NSData *) dataFromBlob: (const char *)blob;
- (BOOL) dbFromDate: (NSDate*)d toBuffer: (char*)b length: (int)l;
- (BOOL) dbFromString: (NSString*)s toBuffer: (char*)b length: (int)l;
- (NSDate*) dbToDateFromBuffer: (char*)b length: (int)l;
- (NSString*) dbToStringFromBuffer: (char*)b length: (int)l;
@end

/* This looks like a Postgres specific issue/feature/bug - for some
 * reason, ':' inside a string often causes the string to be
 * horribly mutilated before being handed to the database
 * ... probably postgres is trying to replace variables or something
 * - avoid all these problems by replacing ':' with its octal code
 * \\072.
 */
static NSString	*
hackForPrepare(NSString *s)
{
  NSRange	r;

  r = [s rangeOfString: @":"];
  if (r.length > 0)
    {
      s = [s stringByReplacingString: @":" withString: @"\\072"];
    }
  return s;
}



#line 1 "/usr/include/postgresql/sql3types.h"
#ifndef _ECPG_SQL3TYPES_H
#define _ECPG_SQL3TYPES_H

/* SQL3 dynamic type codes */

/* chapter 13.1 table 2: Codes used for SQL data types in Dynamic SQL */

enum
{
	SQL3_CHARACTER = 1,
	SQL3_NUMERIC,
	SQL3_DECIMAL,
	SQL3_INTEGER,
	SQL3_SMALLINT,
	SQL3_FLOAT,
	SQL3_REAL,
	SQL3_DOUBLE_PRECISION,
	SQL3_DATE_TIME_TIMESTAMP,
	SQL3_INTERVAL,				/* 10 */
	SQL3_CHARACTER_VARYING = 12,
	SQL3_ENUMERATED,
	SQL3_BIT,
	SQL3_BIT_VARYING,
	SQL3_BOOLEAN,
	SQL3_abstract
	/* the rest is xLOB stuff */
};

/* chapter 13.1 table 3: Codes associated with datetime data types in Dynamic SQL */

enum
{
	SQL3_DDT_DATE = 1,
	SQL3_DDT_TIME,
	SQL3_DDT_TIMESTAMP,
	SQL3_DDT_TIME_WITH_TIME_ZONE,
	SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE,

	SQL3_DDT_ILLEGAL			/* not a datetime data type (not part of
								 * standard) */
};

#endif							/* !_ECPG_SQL3TYPES_H */

#line 81 "ECPG.pgm"


#line 1 "/usr/include/postgresql/sqlca.h"
#ifndef POSTGRES_SQLCA_H
#define POSTGRES_SQLCA_H

#ifndef PGDLLIMPORT
#if  defined(WIN32) || defined(__CYGWIN__)
#define PGDLLIMPORT __declspec (dllimport)
#else
#define PGDLLIMPORT
#endif							/* __CYGWIN__ */
#endif							/* PGDLLIMPORT */

#define SQLERRMC_LEN	150

#ifdef __cplusplus
extern "C"
{
#endif

struct sqlca_t
{
	char		sqlcaid[8];
	long		sqlabc;
	long		sqlcode;
	struct
	{
		int			sqlerrml;
		char		sqlerrmc[SQLERRMC_LEN];
	}			sqlerrm;
	char		sqlerrp[8];
	long		sqlerrd[6];
	/* Element 0: empty						*/
	/* 1: OID of processed tuple if applicable			*/
	/* 2: number of rows processed				*/
	/* after an INSERT, UPDATE or				*/
	/* DELETE statement					*/
	/* 3: empty						*/
	/* 4: empty						*/
	/* 5: empty						*/
	char		sqlwarn[8];
	/* Element 0: set to 'W' if at least one other is 'W'	*/
	/* 1: if 'W' at least one character string		*/
	/* value was truncated when it was			*/
	/* stored into a host variable.             */

	/*
	 * 2: if 'W' a (hopefully) non-fatal notice occurred
	 */	/* 3: empty */
	/* 4: empty						*/
	/* 5: empty						*/
	/* 6: empty						*/
	/* 7: empty						*/

	char		sqlstate[5];
};

struct sqlca_t *ECPGget_sqlca(void);

#ifndef POSTGRES_ECPG_INTERNAL
#define sqlca (*ECPGget_sqlca())
#endif

#ifdef __cplusplus
}
#endif

#endif

#line 82 "ECPG.pgm"

/* exec sql whenever sqlerror  call SQLErrorHandler ( ) ; */
#line 83 "ECPG.pgm"


/**
 * Return YES of the last SQL error indicated we are out of data,
 * NO otherwise.
 */
BOOL SQLOutOfData()
{
  if (sqlca.sqlcode == 100)
    {
      return YES;
    }
  else
    {
      return NO;
    }
}


/**
 * This error handler is called for most errors ... so we can get it to
 * raise an exception for us.
 */
void SQLErrorHandler()
{
  int		code = sqlca.sqlcode;
  const char	*ptr = sqlca.sqlerrm.sqlerrmc;
  const char	*e0 = "'no connection to the server'";
  const char	*e1 = "Error in transaction processing";

  sqlca.sqlcode = 0;	// Reset error code
  NSLog (@"Raising an exception, %d, %s", code, sqlca.sqlerrm.sqlerrmc);
  
  if (strncmp(ptr, e0, strlen(e0)) == 0
    || strncmp(ptr, e1, strlen(e1)) == 0)
    {
      [NSException raise: SQLConnectionException
		  format: @"SQL Error: SQLCODE=(%d): %s", code, ptr];
    }
  else
    {
      [NSException raise: SQLException
		  format: @"SQL Error: SQLCODE=(%d): %s", code, ptr];
    }
}

@implementation	SQLClientECPG

static NSDate	*future = nil;

+ (void) initialize
{
  if (future == nil)
    {
      future = [NSCalendarDate dateWithString: @"9999-01-01 00:00:00 +0000"
			       calendarFormat: @"%Y-%m-%d %H:%M:%S %z"
				       locale: nil];
      [future retain];
    }
}

- (BOOL) backendConnect
{
  if (connected == NO)
    {
      if ([self database] != nil
	&& [self user] != nil
	&& [self password] != nil)
	{
	  Class		c = NSClassFromString(@"CmdClient");

	  [[self class] purgeConnections: nil];

	  NS_DURING
	    {
	      /* exec sql begin declare section */
	        
	        
	        
	        
	      
#line 159 "ECPG.pgm"
 const char * database_c ;
 
#line 160 "ECPG.pgm"
 const char * user_c ;
 
#line 161 "ECPG.pgm"
 const char * password_c ;
 
#line 162 "ECPG.pgm"
 const char * client_c ;
/* exec sql end declare section */
#line 163 "ECPG.pgm"


	      database_c = [[self database] UTF8String];
	      user_c = [[self user] UTF8String];
	      password_c = [[self password] UTF8String];
	      client_c = [[self clientName] UTF8String];
	      
	      if (c != 0)
		{
		  if ([self debugging] > 0)
		    {
		      [self debug:
			@"Connect to '%@' database %s user %s as %s", 
			[self name], database_c, user_c, client_c];
		    }
	        }
	      { ECPGconnect(__LINE__, 0, database_c , user_c , password_c , client_c, 0); 
#line 182 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 182 "ECPG.pgm"


	      connected = YES;
	      if (c != 0)
		{
		  if ([self debugging] > 0)
		    {
		      [self debug: @"Connected to '%@' (%s)",
			[self name], client_c];
		    }
		}
	      { ECPGsetcommit(__LINE__, "on", client_c);
#line 193 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 193 "ECPG.pgm"

	      // For backwards compat, make this the default
	      { ECPGsetconn(__LINE__, client_c);
#line 195 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 195 "ECPG.pgm"

	    }
	  NS_HANDLER
	    {
	      [self debug: @"Error connecting to '%@' database: %@",
		[self name], localException];
	    }
	  NS_ENDHANDLER
	}
      else
	{
	  [self debug:
	    @"Connect to '%@' with no user/password/database configured",
	    [self name]];
	}
    }
  return connected;
}

- (void) backendDisconnect
{
  if (connected == YES)
    {
      NS_DURING
	{
	  /* exec sql begin declare section */
	   	
	  
#line 221 "ECPG.pgm"
 const char * client_c ;
/* exec sql end declare section */
#line 222 "ECPG.pgm"


	  if ([self isInTransaction] == YES)
	    {
	      [self rollback];
	    }

	  client_c = [[self clientName] UTF8String];
	  
	  if ([self debugging] > 0)
	    {
	      [self debug: @"Disconnecting client %@", [self clientName]];
	    }
	  { ECPGdisconnect(__LINE__, client_c);
#line 235 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 235 "ECPG.pgm"
  
	  if ([self debugging] > 0)
	    {
	      [self debug: @"Disconnected client %@", [self clientName]];
	    }
	}
      NS_HANDLER
	{
	  [self debug: @"Error disconnecting from database (%@): %@",
	    [self clientName], localException];
	}
      NS_ENDHANDLER
      connected = NO;
    }
}

- (NSInteger) backendExecute: (NSArray*)info
{
  /* exec sql begin declare section */
  	
  	
  
#line 254 "ECPG.pgm"
 char * statement ;
 
#line 255 "ECPG.pgm"
 char * handle ;
/* exec sql end declare section */
#line 256 "ECPG.pgm"

  NSAutoreleasePool     *arp = [NSAutoreleasePool new];
  unsigned int		length;
  NSString		*stmt = [info objectAtIndex: 0];

  length = [stmt length];
  if (length == 0)
    {
      [arp release];
      [NSException raise: NSInternalInconsistencyException
		  format: @"Statement produced null string"];
    }

  statement = (char*)[hackForPrepare(stmt) UTF8String];
  length = strlen(statement);
  statement = (char*)[self insertBLOBs: info
			 intoStatement: statement
				length: length
			    withMarker: "'''"
				length: 3
				giving: &length];
  handle = (char*)[[self clientName] UTF8String];

  /*
   * Ensure we have a working connection.
   */
  if ([self connect] == NO)
    {
      [arp release];
      [NSException raise: SQLException
	format: @"Unable to connect to '%@' to execute statement %@",
	[self name], stmt];
    } 

  NS_DURING
    {
      { ECPGprepare(__LINE__, NULL, 0, "command", statement);
#line 292 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 292 "ECPG.pgm"

      { ECPGdo(__LINE__, 0, 1, handle, 0, ECPGst_execute, "command", ECPGt_EOIT, ECPGt_EORT);
#line 293 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 293 "ECPG.pgm"

    }
  NS_HANDLER
    {
      NSString	*n = [localException name];
      NSString	*msg = [localException reason];

      if ([n isEqual: SQLConnectionException] == YES) 
	{
	  [self disconnect];
	}
      /*
       * remove line number information from database exception message
       * since it's meaningless to the developer as it's the line number
       * in this file rather than the code which is calling us.
       */
      if ([n isEqual: SQLException] == YES
	|| [n isEqual: SQLConnectionException] == YES) 
	{
	  NSRange	r;

	  r = [msg rangeOfString: @" in line " options: NSBackwardsSearch];
	  if (r.length > 0)
	    {
	      msg = [msg substringToIndex: r.location];
	      localException = [NSException exceptionWithName: n
						       reason: msg
						     userInfo: nil];
	    }
	}
      if ([self debugging] > 0)
	{
	  [self debug: @"Error executing statement:\n%@\n%@",
	    stmt, localException];
	}
      [localException retain];
      [arp release];
      [localException autorelease];
      [localException raise];
    }
  NS_ENDHANDLER
  [arp release];
  return -1;
}

static unsigned int trim(char *str)
{
  char	*start = str;

  while (isspace(*str))
    {
      str++;
    }
  if (str != start)
    {
      strcpy(start, str);
    }
  str = start;
  while (*str != '\0')
    {
      str++;
    }
  while (str > start && isspace(str[-1]))
    {
      *--str = '\0';
    }
  return (str - start);
}

- (NSMutableArray*) backendQuery: (NSString*)stmt
		      recordType: (id)rtype
		        listType: (id)ltype
{
  /* exec sql begin declare section */
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
   
  	
  	
  
#line 367 "ECPG.pgm"
 bool aBool ;
 
#line 368 "ECPG.pgm"
 int anInt ;
 
#line 369 "ECPG.pgm"
 int count ;
 
#line 370 "ECPG.pgm"
 int index ;
 
#line 371 "ECPG.pgm"
 int indicator ;
 
#line 372 "ECPG.pgm"
 int type ;
 
#line 373 "ECPG.pgm"
 int length ;
 
#line 374 "ECPG.pgm"
 int octetLength ;
 
#line 375 "ECPG.pgm"
 int precision ;
 
#line 376 "ECPG.pgm"
 int scale ;
 
#line 377 "ECPG.pgm"
 int returnedOctetLength ;
 
#line 378 "ECPG.pgm"
 int dtiCode ;
 
#line 379 "ECPG.pgm"
 char fieldName [ 120 ] ;
 
#line 380 "ECPG.pgm"
 char * aString ;
 
#line 381 "ECPG.pgm"
 float aFloat ;
 
#line 382 "ECPG.pgm"
 double aDouble ;
 
#line 383 "ECPG.pgm"
 char * query ;
 
#line 384 "ECPG.pgm"
 char * handle ;
/* exec sql end declare section */
#line 385 "ECPG.pgm"

  NSAutoreleasePool     *arp = [NSAutoreleasePool new];
  NSMutableArray	*records;
  BOOL			isOpen = NO;
  BOOL			localTransaction = NO;

  length = [stmt length];
  if (length == 0)
    {
      [arp release];
      [NSException raise: NSInternalInconsistencyException
		  format: @"Statement produced null string"];
    }

  query = (char*)[hackForPrepare(stmt) UTF8String];
  handle = (char*)[[self clientName] UTF8String];
  records = [[ltype alloc] initWithCapacity: 32];

  /*
   * Ensure we have a working connection.
   */
  if ([self connect] == NO)
    {
      [arp release];
      [NSException raise: SQLException
	format: @"Unable to connect to '%@' to run query %@",
	[self name], stmt];
    } 

  NS_DURING
    {
      ECPGallocate_desc(__LINE__, "myDesc");
#line 416 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );
#line 416 "ECPG.pgm"

      { ECPGprepare(__LINE__, NULL, 0, "myquery", query);
#line 417 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 417 "ECPG.pgm"

      if ([self isInTransaction] == NO)
	{
	  { ECPGtrans(__LINE__, handle, "begin");
#line 420 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 420 "ECPG.pgm"

	  localTransaction = YES;
	}
      /* declare myCursor cursor for $1 */
#line 423 "ECPG.pgm"

      { ECPGdo(__LINE__, 0, 1, handle, 0, ECPGst_normal, "declare myCursor cursor for $1", 
	ECPGt_char_variable,(ECPGprepared_statement(handle, "myquery", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
#line 424 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 424 "ECPG.pgm"

      isOpen = YES;
      
      while (1)
	{
	  { ECPGdo(__LINE__, 0, 1, handle, 0, ECPGst_normal, "fetch in myCursor", ECPGt_EOIT, 
	ECPGt_descriptor, "myDesc", 1L, 1L, 1L, 
	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
#line 429 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 429 "ECPG.pgm"

	   
	  if (sqlca.sqlcode)
	    {
	      break;
	    } 
	  { ECPGget_desc_header(__LINE__, "myDesc", &(count));

#line 435 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 435 "ECPG.pgm"

	   
	  if (count > 0)
	    {
	      SQLRecord	*record;
	      id	keys[count];
	      id	values[count];

	      for (index = 1; index <= count; ++index)
		{
		  id	v;

		  { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_type,
	ECPGt_int,&(type),(long)1,(long)1,sizeof(int), ECPGd_scale,
	ECPGt_int,&(scale),(long)1,(long)1,sizeof(int), ECPGd_ret_octet,
	ECPGt_int,&(returnedOctetLength),(long)1,(long)1,sizeof(int), ECPGd_precision,
	ECPGt_int,&(precision),(long)1,(long)1,sizeof(int), ECPGd_octet,
	ECPGt_int,&(octetLength),(long)1,(long)1,sizeof(int), ECPGd_name,
	ECPGt_char,(fieldName),(long)120,(long)1,(120)*sizeof(char), ECPGd_length,
	ECPGt_int,&(length),(long)1,(long)1,sizeof(int), ECPGd_indicator,
	ECPGt_int,&(indicator),(long)1,(long)1,sizeof(int), ECPGd_EODT);

#line 455 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 455 "ECPG.pgm"


// printf("%s type:%d scale:%d\n", fieldName, type, scale);

		  if (indicator == -1)
		    {
		      v = [NSNull null];
		    }
		  else
		    {
		      /*
		       * HACK ... for some reason date/time data seems to
		       * get a negative type returned, so we check any
		       * negative time to see if it is really date/time
		       * and bodg the type code to fit.
		       */
		      if (type < 0)
			{
			  { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_di_code,
	ECPGt_int,&(dtiCode),(long)1,(long)1,sizeof(int), ECPGd_EODT);

#line 474 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 474 "ECPG.pgm"

			  if (dtiCode != SQL3_DDT_ILLEGAL)
			    {
			      type = SQL3_DATE_TIME_TIMESTAMP;
			    }
			}

		      aString = 0;
		      switch (type)
			{
			  case SQL3_BOOLEAN: 
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_bool,&(aBool),(long)1,(long)1,sizeof(bool), ECPGd_EODT);

#line 486 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 486 "ECPG.pgm"

			    if (aBool == 1)
			      {
				v = [NSNumber numberWithBool: YES];
			      }
			    else if (aBool == 0)
			      {
				v = [NSNumber numberWithBool: NO];
			      }
			    else
			      {
				[NSException raise: NSGenericException
					    format: @"Bad bool for '%s' - '%d'",
				  fieldName, aBool];
			      }
			    break;

			  case SQL3_NUMERIC:
			  case SQL3_DECIMAL:
			    if (scale == 0) 
			      {
				{ ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_int,&(anInt),(long)1,(long)1,sizeof(int), ECPGd_EODT);

#line 508 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 508 "ECPG.pgm"

				v = [NSNumber numberWithInt: anInt];
			      }
			    else
			      {
				{ ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_float,&(aFloat),(long)1,(long)1,sizeof(float), ECPGd_EODT);

#line 514 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 514 "ECPG.pgm"

				v = [NSNumber numberWithFloat: aFloat];
			      }
			    break;

			  case SQL3_INTEGER:
			  case SQL3_SMALLINT: 
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_int,&(anInt),(long)1,(long)1,sizeof(int), ECPGd_EODT);

#line 522 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 522 "ECPG.pgm"

			    v = [NSNumber numberWithInt: anInt];
			    break;

			  case SQL3_FLOAT:
			  case SQL3_REAL:
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_float,&(aFloat),(long)1,(long)1,sizeof(float), ECPGd_EODT);

#line 529 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 529 "ECPG.pgm"

			    v = [NSNumber numberWithFloat: aFloat];
			    break;

			  case SQL3_DOUBLE_PRECISION:
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_double,&(aDouble),(long)1,(long)1,sizeof(double), ECPGd_EODT);

#line 535 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 535 "ECPG.pgm"

			    v = [NSNumber numberWithDouble: aDouble];
			    break;

			  case SQL3_DATE_TIME_TIMESTAMP:
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_char,&(aString),(long)0,(long)1,(1)*sizeof(char), ECPGd_di_code,
	ECPGt_int,&(dtiCode),(long)1,(long)1,sizeof(int), ECPGd_EODT);

#line 542 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 542 "ECPG.pgm"

			    v = [self dbToDateFromBuffer: aString
			      length: trim(aString)];
			    free(aString);
			    break;

			  case SQL3_INTERVAL:
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_char,&(aString),(long)0,(long)1,(1)*sizeof(char), ECPGd_EODT);

#line 550 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 550 "ECPG.pgm"

			    trim(aString);
			    v = [NSString stringWithUTF8String: aString];
			    free(aString);
			    break;

			  case SQL3_CHARACTER:
			  case SQL3_CHARACTER_VARYING:
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_char,&(aString),(long)0,(long)1,(1)*sizeof(char), ECPGd_EODT);

#line 559 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 559 "ECPG.pgm"

                            if (_shouldTrim)
                              {
                                trim(aString);
                              }
			    v = [NSString stringWithUTF8String: aString];
			    free(aString);
			    break;

			  case -17:
			    /*
			     * HACK ... BYTEA type determined by experiment.
			     * who knows how/why this might change.
			     */
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_char,&(aString),(long)0,(long)1,(1)*sizeof(char), ECPGd_EODT);

#line 574 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 574 "ECPG.pgm"

			    v = [self dataFromBlob: aString];
			    free(aString);
			    break;

			  case -20:
			    /*
			     * HACK ... by experiment this seems to be an
			     * integer returned by a function.
			     */
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_int,&(anInt),(long)1,(long)1,sizeof(int), ECPGd_EODT);

#line 585 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 585 "ECPG.pgm"

			    v = [NSNumber numberWithInt: anInt];
			    break;

			  default:
			    { ECPGget_desc(__LINE__, "myDesc", index,ECPGd_data,
	ECPGt_char,&(aString),(long)0,(long)1,(1)*sizeof(char), ECPGd_EODT);

#line 591 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 591 "ECPG.pgm"

                            if (_shouldTrim)
                              {
                                trim(aString);
                              }
			    v = [NSString stringWithUTF8String: aString];
			    free(aString);
			    if ([self debugging] > 0)
			      { 
				[self debug:
				  @"Unknown data type (%d) for '%s' ... '%@'",
				  type, fieldName, v];
			      }
			    break;
			}
		    }

		  values[index-1] = v;
		  keys[index-1] = [NSString stringWithUTF8String:
		    fieldName];
		}
	      record = [rtype newWithValues: values
				       keys: keys
			              count: count];
	      [records addObject: record];
	      [record release];
	    }
	}
      
      isOpen = NO;
      { ECPGdo(__LINE__, 0, 1, handle, 0, ECPGst_normal, "close myCursor", ECPGt_EOIT, ECPGt_EORT);
#line 621 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 621 "ECPG.pgm"

      if (localTransaction == YES)
	{
	  { ECPGtrans(__LINE__, handle, "commit");
#line 624 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 624 "ECPG.pgm"

	  localTransaction = NO;
	}
      ECPGdeallocate_desc(__LINE__, "myDesc");
#line 627 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );
#line 627 "ECPG.pgm"

    }
  NS_HANDLER
    {
      NSString	*n = [localException name];
      NSString	*msg = [localException reason];

      [records release];
      records = nil;

      NS_DURING
	{
	  if (isOpen == YES)
	    {
	      { ECPGdo(__LINE__, 0, 1, handle, 0, ECPGst_normal, "close myCursor", ECPGt_EOIT, ECPGt_EORT);
#line 641 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 641 "ECPG.pgm"

	    }
	  if (localTransaction == YES)
	    {
	      { ECPGtrans(__LINE__, handle, "rollback");
#line 645 "ECPG.pgm"

if (sqlca.sqlcode < 0) SQLErrorHandler ( );}
#line 645 "ECPG.pgm"

	    }
	}
      NS_HANDLER
	{
	  NSString	*e = [localException name];

	  if ([e isEqual: SQLConnectionException] == YES) 
	    {
	      [self disconnect];
	    }
	}
      NS_ENDHANDLER

      if ([n isEqual: SQLConnectionException] == YES) 
	{
	  [self disconnect];
	}

      /*
       * remove line number information from database exception message
       * since it's meaningless to the developer as it's the line number
       * in this file rather than the code which is calling us.
       */
      if ([n isEqual: SQLException] == YES
	|| [n isEqual: SQLConnectionException] == YES) 
	{
	  NSRange	r;

	  r = [msg rangeOfString: @" in line " options: NSBackwardsSearch];
	  if (r.length > 0)
	    {
	      msg = [msg substringToIndex: r.location];
	      localException = [NSException exceptionWithName: n
						       reason: msg
						     userInfo: nil];
	    }
	}
      [localException retain];
      [arp release];
      [localException autorelease];
      [localException raise];
    }
  NS_ENDHANDLER
  [arp release];

  return [records autorelease];
}

- (unsigned) copyEscapedBLOB: (NSData*)blob into: (void*)buf
{
  const unsigned char	*src = [blob bytes];
  unsigned		sLen = [blob length];
  unsigned char		*ptr = (unsigned char*)buf;
  unsigned		length = 0;
  unsigned		i;

  ptr[length++] = '\'';
  for (i = 0; i < sLen; i++)
    {
      unsigned char	c = src[i];

      if (c < 32 || c > 126 || c == ':')
	{
	  ptr[length] = '\\';
	  ptr[length+1] = '\\';
	  ptr[length + 4] = (c & 7) + '0';
	  c >>= 3;
	  ptr[length + 3] = (c & 7) + '0';
	  c >>= 3;
	  ptr[length + 2] = (c & 7) + '0';
	  length += 5;
	}
      else if (c == '\\')
	{
	  ptr[length++] = '\\';
	  ptr[length++] = '\\';
	  ptr[length++] = '\\';
	  ptr[length++] = '\\';
	}
      else if (c == '\'')
	{
	  ptr[length++] = '\\';
	  ptr[length++] = '\'';
	}
      else
	{
	  ptr[length++] = c;
	}
    }
  ptr[length++] = '\'';
  return length;
}

- (unsigned) lengthOfEscapedBLOB: (NSData*)blob
{
  unsigned int	sLen = [blob length];
  unsigned char	*src = (unsigned char*)[blob bytes];
  unsigned int	length = 2;
  unsigned int	i;

  for (i = 0; i < sLen; i++)
    {
      unsigned char	c = src[i];

      if (c < 32 || c > 126 || c == ':')
	{
	  length += 5;
	}
      else if (c == '\\')
	{
	  length += 4;
	}
      else if (c == '\'')
	{
	  length += 2;
	}
      else
	{
	  length += 1;
	}
    }
  return length;
}

- (const char *) blobFromData: (NSData*)data
{
  NSMutableData	*md;
  unsigned	sLen = [data length];
  unsigned char	*src = (unsigned char*)[data bytes];
  unsigned	dLen = 0;
  unsigned char	*dst;
  unsigned	i;

  for (i = 0; i < sLen; i++)
    {
      unsigned char	c = src[i];

      if (c < 32 || c > 126)
	{
	  dLen += 4;
	}
      else if (c == 92)
	{
	  dLen += 2;
	}
      else
	{
	  dLen += 1;
	}
    }
  md = [NSMutableData dataWithLength: dLen + 1];
  dst = (unsigned char*)[md mutableBytes];

  dLen = 0;
  for (i = 0; i < sLen; i++)
    {
      unsigned char	c = src[i];

      if (c < 32 || c > 126)
	{
	  dst[dLen] = '\\';
	  dst[dLen + 3] = (c & 7) + '0';
	  c >>= 3;
	  dst[dLen + 2] = (c & 7) + '0';
	  c >>= 3;
	  dst[dLen + 1] = (c & 7) + '0';
	  dLen += 4;
	}
      else if (c == 92)
	{
	  dst[dLen++] = '\\';
	  dst[dLen++] = '\\';
	}
      else
	{
	  dst[dLen++] = c;
	}
    }
  dst[dLen] = '\0';
  return (const char*)dst;	// Owned by autoreleased NSMutableData
}

- (NSData *) dataFromBlob: (const char *)blob
{
  NSMutableData	*md;
  unsigned	sLen = strlen(blob == 0 ? "" : blob);
  unsigned	dLen = 0;
  unsigned char	*dst;
  unsigned	i;

  for (i = 0; i < sLen; i++)
    {
      unsigned	c = blob[i];

      dLen++;
      if (c == '\\')
	{
	  c = blob[++i];
	  if (c != '\\')
	    {
	      i += 2;	// Skip 2 digits octal
	    }
	}
    }
  md = [NSMutableData dataWithLength: dLen];
  dst = (unsigned char*)[md mutableBytes];

  dLen = 0;
  for (i = 0; i < sLen; i++)
    {
      unsigned	c = blob[i];

      if (c == '\\')
	{
	  c = blob[++i];
	  if (c != '\\')
	    {
	      c = c - '0';
	      c <<= 3;
	      c += blob[++i] - '0';
	      c <<= 3;
	      c += blob[++i] - '0';
	    }
	}
      dst[dLen++] = c;
    }
  return md;
}

- (BOOL) dbFromDate: (NSDate*)d toBuffer: (char*)b length: (int)l
{
  NSString	*s;

  /*
   * Ensure we have a four digit year.
   */
  if ([d timeIntervalSinceDate: future] > 0)
    {
      d = future;
    }
  s = [d descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"
			      timeZone: nil
				locale: nil];
  return [self dbFromString: s toBuffer: b length: l];
}

- (BOOL) dbFromString: (NSString*)s toBuffer: (char*)b length: (int)l
{
  NSData	*d;
  BOOL		ok = YES;
  unsigned	size = l;

  if (l <= 0)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"-%@: length too small (%d)",
	NSStringFromSelector(_cmd), l];
    }
  if (b == 0)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"-%@: buffer is null",
	NSStringFromSelector(_cmd)];
    }
  if (s == nil)
    {
      s = @"";
    }
  d = [s dataUsingEncoding: NSUTF8StringEncoding];
  if (l < (int)[d length])
    {
      /*
       * As the data is UTF8, we need to avoid truncating in the
       * middle of a multibyte character, so we shorten the
       * original string and reconvert to UTF8 until we find a
       * string that fits.
       */
      if ((int)[s length] > l)
	{
	  s = [s substringToIndex: l];
	  d = [s dataUsingEncoding: NSUTF8StringEncoding];
	}
      while ((int)[d length] > l)
	{
	  s = [s substringToIndex: [s length] - 1];
	  d = [s dataUsingEncoding: NSUTF8StringEncoding];
	}
      ok = NO;
    }
  size = [d length];
  memcpy(b, (const char*)[d bytes], size);
  /*
   * Pad with nuls and ensure there is a nul terminator.
   */
  while ((int)size <= l)
    {
      b[size++] = '\0';
    }
  return ok;
}

- (NSDate*) dbToDateFromBuffer: (char*)b length: (int)l
{
  char		        buf[l+32];	/* Allow space to expand buffer. */
  NSCalendarDate	*d;
  BOOL		        milliseconds = NO;
  BOOL                  timezone = NO;
  NSString	        *s;
  int		        i;
  int	                e;

  memcpy(buf, b, l);
  b = buf;
  /*
   * Find end of string.
   */
  for (i = 0; i < l; i++)
    {
      if (b[i] == '\0')
	{
	  l = i;
	  break;
	}
    }
  while (l > 0 && isspace(b[l-1]))
    {
      l--;
    }
  b[l] = '\0';

  if (l == 10)
    {
      s = [NSString stringWithUTF8String: b];
      return [NSCalendarDate dateWithString: s
			     calendarFormat: @"%Y-%m-%d"
				     locale: nil];
    }

  i = l;

  /* Convert +/-HH:SS timezone to +/-HHSS
   */
  if (i > 5 && b[i-3] == ':' && (b[i-6] == '+' || b[i-6] == '-'))
    {
      b[i-3] = b[i-2];
      b[i-2] = b[i-1];
      b[--i] = '\0';
    }

  while (i-- > 0)
    {
      if (b[i] == '+' || b[i] == '-')
        {
          break;
        }
      if (b[i] == ':' || b[i] == ' ')
        {
          i = 0;
          break;	/* No time zone found */
        }
    }

  if (i == 0)
    {
      e = l;
    }
  else
    {
      timezone = YES;
      e = i;
      if (isdigit(b[i-1]))
        {
          /*
           * Make space between seconds and timezone.
           */
          memmove(&b[i+1], &b[i], l - i);
          b[i++] = ' ';
          b[++l] = '\0';
        }

      /*
       * Ensure we have a four digit timezone value.
       */
      if (isdigit(b[i+1]) && isdigit(b[i+2]))
        {
          if (b[i+3] == '\0')
            {
              // Two digit time zone ... append zero minutes
              b[l++] = '0';
              b[l++] = '0';
              b[l] = '\0';
            }
          else if (b[i+3] == ':')
            {
              // Zone with colon before minutes ... remove it
              b[i+3] = b[i+4];
              b[i+4] = b[i+5];
              b[--l] = '\0';
            }
        }
    }

  /* kludge for timestamps with fractional second information.
   * Force it to 3 digit millisecond */
  while (i-- > 0)
    {
      if (b[i] == '.')
        {
          milliseconds = YES;
          i++;
          if (!isdigit(b[i]))
            {
              memmove(&b[i+3], &b[i], e-i);
              l += 3;
              memcpy(&b[i], "000", 3);
            }
          i++;
          if (!isdigit(b[i]))
            {
              memmove(&b[i+2], &b[i], e-i);
              l += 2;
              memcpy(&b[i], "00", 2);
            }
          i++;
          if (!isdigit(b[i]))
            {
              memmove(&b[i+1], &b[i], e-i);
              l += 1;
              memcpy(&b[i], "0", 1);
            }
          i++;
          break;
        }
    }
  if (i > 0 && i < e)
    {
      memmove(&b[i], &b[e], l - e);
      l -= (e - i);
    }
  b[l] = '\0';
  if (l == 0)
    {
      return nil;
    }
  
  s = [NSString stringWithUTF8String: b];

  if (YES == timezone)
    {
      if (milliseconds == YES)
        {
          d = [NSCalendarDate dateWithString: s
                              calendarFormat: @"%Y-%m-%d %H:%M:%S.%F %z"
                                      locale: nil];
        }
      else
        {
          d = [NSCalendarDate dateWithString: s
                              calendarFormat: @"%Y-%m-%d %H:%M:%S %z"
                                      locale: nil];
        }
    }
  else
    {
      if (milliseconds == YES)
        {
          d = [NSCalendarDate dateWithString: s
                              calendarFormat: @"%Y-%m-%d %H:%M:%S.%F"
                                      locale: nil];
        }
      else
        {
          d = [NSCalendarDate dateWithString: s
                              calendarFormat: @"%Y-%m-%d %H:%M:%S"
                                      locale: nil];
        }
    }
  [d setCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"];
  return d;
}

- (NSString*) dbToStringFromBuffer: (char*)b length: (int)l
{
  NSData	*d;
  NSString	*s;

  /*
   * Database fields are padded to the full field size with spaces or nuls ...
   * we need to remove that padding before placing in a string.
   */
  while (l > 0 && b[l-1] <= ' ')
    {
      l--;
    }
  d = [[NSData alloc] initWithBytes: b length: l];
  s = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
  [d release];
  return [s autorelease];
}

@end

