/* 
   Postgres95Adaptor.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
	      based on the Sybase Adaptor written by 
	   Ovidiu Predescu <ovidiu@bx.logicnet.ro>
   Date: December 1996

   This file is part of the GNUstep Database Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSString.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSAutoreleasePool.h>

#include <extensions/NSException.h>

#include <eoaccess/common.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EOExpressionArray.h>

#include <eoadaptors/Postgres95/Postgres95Adaptor.h>
#include <eoadaptors/Postgres95/Postgres95Context.h>
#include <eoadaptors/Postgres95/Postgres95Channel.h>
#include <eoadaptors/Postgres95/Postgres95Exceptions.h>
#include <eoadaptors/Postgres95/Postgres95SQLExpression.h>
#include <eoadaptors/Postgres95/Postgres95Values.h>

@implementation Postgres95Adaptor

- init
{
    return [self initWithName:@"Postgres95"];
}

- initWithName:(NSString*)_name
{
    [super initWithName:_name];
    pgConnPool = [NSMutableArray new];
    [self initValidTypeNamesForQualifier];
    
    return self;
}

- (void)dealloc
{
    NSEnumerator* enumerator;
    PGconn* pgConn;

    [validQualifierTypes release];

    enumerator = [pgConnPool objectEnumerator];
    while((pgConn = [[enumerator nextObject] pointerValue]))
	[self releasePGconn:pgConn force:YES];
    [pgConnPool release];

    [super dealloc];
}

- (void)privateReportError:(PGconn*)pgConn
{
    char* message = "NULL pgConn in privateReportError:";

    if (pgConn)
	message = PQerrorMessage(pgConn);
    
    [self reportError:[NSString stringWithCString:message]];
}

- (void)setTypeName:(NSString*)typeName validForQualifier:(BOOL)flag
{
    if (flag)
	[validQualifierTypes addObject:typeName];
    else
	[validQualifierTypes removeObject:typeName];
}

- (void)initValidTypeNamesForQualifier
{
    id typeNames[] = {
	@"bool", 
	@"char", @"char2", @"char4", @"char8", @"char16", @"filename", 
	@"date", @"reltime", @"time", @"tinterval", @"abstime", 
	@"float4", @"float8", 
	@"int4", @"int2", 
	@"oid", @"oid8", @"oidint2", @"oidint4", @"oidchar16",
	@"varchar",
	@"cid", @"tid", @"xid"
    };

    validQualifierTypes = [[NSMutableSet alloc] 
	initWithObjects:typeNames count:(sizeof(typeNames)/sizeof(id))];
}

- (void)setCachePGconn:(BOOL)flag	{ flags.cachePGconn = flag; }
- (BOOL)cachePGconn			{ return flags.cachePGconn; }
- (void)setPGconnPoolLimit:(int)value	{ pgConnPoolLimit = value; }
- (int)pgConnPoolLimit			{ return pgConnPoolLimit; }


/* Inherited methods */

- (Class)expressionClass
{
    return [Postgres95SQLExpression class];
}

- (Class)adaptorChannelClass
{
    return [Postgres95Channel class];
}

- (Class)adaptorContextClass
{
    return [Postgres95Context class];
}

- formatValue:value forAttribute:(EOAttribute*)attribute
{
    return [value stringValueForPostgres95Type:[attribute externalType] 
	    attribute:attribute];
}

- (BOOL)isValidQualifierType:(NSString*)typeName
{
    return [validQualifierTypes containsObject:typeName];
}

/* Private methods for Postgres Adaptor */

- (PGconn*)createPGconn
{
    char* pg_host;
    char* pg_database;
    char* pg_port;
    char* pg_options;
    char* pg_tty;
    char* pg_user;
    char* pg_pwd;
    PGconn* pgConn;
    NSString* str;
    
    str = [connectionDictionary objectForKey:@"databaseServer"]; 
    if (!str)
	str = [connectionDictionary objectForKey:@"hostName"];
    pg_host = (char*)[str cString];

    pg_database = (char*)[[connectionDictionary objectForKey:@"databaseName"]
				cString]; 
    pg_port = (char*)[[connectionDictionary objectForKey:@"port"] cString]; 
    pg_options = (char*)[[connectionDictionary objectForKey:@"options"]
			    cString]; 
    pg_tty = (char*)[[connectionDictionary objectForKey:@"debugTTY"] cString]; 
    pg_user = (char*)[[connectionDictionary objectForKey:@"userName"]
                                cString]; 
    pg_pwd = (char*)[[connectionDictionary objectForKey:@"password"]
                                cString]; 

    // Try to connect to the Postgres95 server
    if (pg_user)
      pgConn = PQsetdbLogin(pg_host, pg_port, pg_options, pg_tty, 
                            pg_database,pg_user,pg_pwd);
    else
      pgConn = PQsetdb(pg_host, pg_port, pg_options, pg_tty, pg_database);
    // Check connection
    if (PQstatus(pgConn) == CONNECTION_BAD) {
	[self privateReportError:pgConn];
	PQfinish(pgConn);
	pgConn = NULL;
    }

    return pgConn;
}

- (PGconn*)newPGconn
{
    PGconn* pgConn;

    if(flags.cachePGconn && [pgConnPool count]) {
	pgConn = [[pgConnPool lastObject] pointerValue];
	[pgConnPool removeLastObject];
	return pgConn;
    }
    else
	pgConn = [self createPGconn];

    return pgConn;
}

- (void)releasePGconn:(PGconn*)pgConn force:(BOOL)flag
{
    if (   !flag 
	&& flags.cachePGconn 
	&& (PQstatus(pgConn) == CONNECTION_OK)
	&& [pgConnPool count] < pgConnPoolLimit)
		[pgConnPool addObject:
		    [NSValue value:pgConn withObjCType:@encode(PGconn*)]];
    else
	PQfinish(pgConn);
}

- (BOOL)hasValidConnectionDictionary
{
  PGconn* pgConn = NULL;

  // Test by trying to create a connection
  NSLog(@"Check if valid connection dictionary\n");
  pgConn = [self newPGconn];
  if (pgConn)
    {
      [self releasePGconn: pgConn force: YES];
      return YES;
    }
  else
    return NO;
}

@end /* Postgres95Adaptor */
