package iDatabase::Driver::pgSQL;
use strict;
use vars qw(@ISA $VERSION $CONN $TYPE $error);

#Inherit from the base class
require iDatabase::Driver::Base;
@ISA = qw(iDatabase::Driver::Base);

# Use the DBI suite of modules.
use DBI;

# Set the type
$TYPE = 'pgSQL';

$VERSION = 1.0;

#--------------------------------------
#
# Create a DB connection
#
#--------------------------------------

sub newSQL {
    my ($pkg, $args)  = @_;
    my $rPkg = ref $pkg || $pkg;
    
    my $obj = { 'error'    => '',
                'query'    => '',
                'query_c'  => 0,
                'PREFIX'   => $args->{'DB_PREFIX'},
                'base_dir' => $args->{'DB_DIR'}
              };

    # Create the mySQL class.
    bless $obj, $rPkg;
    
    # Connect, if we don't already have a live connection or
    # DB Handle
    
    $CONN ||= $obj->connect($args);
    $obj->{DB} = $CONN;

    # Return an error if something is wrong.
    unless ($CONN) {
        die ("Can't connect to $TYPE database. $DBI::errstr $!");
    }
    
    # Set the permissions for creating / dropping tables
    $obj->{_can_drop}   = $args->{ATTR}->{allow_drop}   || 0;
    $obj->{_can_create} = $args->{ATTR}->{allow_create} || 0;
    
    return $obj;
}

#--------------------------------------
#
# Override the base method, and connect
#
#--------------------------------------

sub connect {
    my ($obj, $args) = @_;
    if ($args->{'IP'} ne "localhost") { 
        $ENV{PGHOST} = $args->{'IP'} if($args->{'IP'});
        $ENV{PGPORT} = $args->{'PORT'} if($args->{'PORT'});
    }
    my $dsn  = "dbi:Pg:dbname=$args->{'DATABASE'}";

    my $connection ||= DBI->connect($dsn, $args->{'USERNAME'}, $args->{'PASSWORD'},{});
	
    return $connection;
}


#----------------------------------------------------------------
#
# Override the base method, disconnect
#
#----------------------------------------------------------------

sub disconnect {
    #Kill the connection from the DBI
    $CONN->disconnect() if ($CONN);
    undef $CONN;
    return "connection ended";
}

sub END {
    &disconnect();
}
sub DESTROY {
    &disconnect();
}



#----------------------------------------------------------------
# parse_raw: Fire a query straight into pgSQL, returning the $sth
#----------------------------------------------------------------

sub parse_raw {
    my ($obj, $db_query) = @_;
    $obj->{'error'} = undef;
    
    my $DB = $CONN;
    
    my $sth = $DB->prepare($db_query);
    $sth->execute() || ($obj->{'error'} = "Can't query the data: $DBI::errstr" and return);
    ++$obj->{query_c};
    # simply return the $sth for use in get_row
    return $sth;
}

#----------------------------------------------------------------
# get_row: get a hashref based on $sth
#----------------------------------------------------------------

sub get_row {
    my ($obj, $sth) = @_;
    
    unless ($sth) {
        $obj->{error} = "\$sth contains no value!";
        return undef;
    }
    #otherwise..
    return $obj->make_hash_ref($sth);
}

#----------------------------------------------------------------
# done: Tidy up and close the $sth
#----------------------------------------------------------------

sub done {
    my ($obj, $sth) = @_;
    
    unless ($sth) {
        $obj->{error} = "\$sth contains no value!";
        return undef;
    }
    
    #otherwise..
    $sth->finish();
    undef $sth;
    return 1;
}

#----------------------------------------------------------------
# select, overrides base class method
#----------------------------------------------------------------

sub select  {

    my $obj = shift;
    my ($data, $found);
    $obj->{'error'} = undef;
    my $IN = {  
                "TABLE"   => "",
                "COLUMNS" => [],
                "KEY"     => "",
                "DBID"    => "", # DEPRECIATED IN SQL
                "ID"      => "", # DEPRECIATED IN SQL
                @_
              };

    $obj->load_cfg($IN->{'TABLE'});

    my $DB = $CONN;
    my $db_query = "SELECT";

    if (scalar @{$IN->{'COLUMNS'}} > 0) {
        $db_query .= ' ' . join(', ',@{$IN->{'COLUMNS'}});
    } else {
        $db_query .= ' *';
    }

    $db_query .=" FROM $obj->{'PREFIX'}"."$IN->{'TABLE'} WHERE $obj->{'cur_p_key'}=".$DB->quote($IN->{'KEY'});
    my $sth = $DB->prepare($db_query);
#mylog("SELECT:    $db_query");
    # $DB->quote($IN->{'KEY'})
    $sth->execute() || ($obj->{'error'} = "Can't query the data from \"$IN->{'TABLE'}\". $DBI::errstr" and return);

    my $result = {};
    

    
    return $obj->make_hash_ref($sth);
}

#----------------------------------------------------------------
# query, overrides base class method
#----------------------------------------------------------------

sub query  {
	
    my $obj = shift;
       $obj->{'error'} = undef;
    my $IN = {  
                "TABLE"     => "",
                "COLUMNS"   => [],
                "SORT_KEY"  => "",
                "SORT_BY"   => "",
                "WHERE"     => "",
                "MATCH"     => "",
                "RANGE"     => "",
                "DBID"      => "", # DEPRECIATED IN SQL
                "ID"        => "", # DEPRECIATED IN SQL
                "COUNT"     => "",
                "POP"       => "",
                "INDEX"     => "",
                @_,
              };



    $obj->load_cfg($IN->{'TABLE'});

    my $DB = $obj->{'DB'};

    my $statement;

    my $db_query = "SELECT";

    if (scalar @{$IN->{'COLUMNS'}} > 0) {
        $db_query .= ' ' . join(', ',@{$IN->{'COLUMNS'}});
    } else {
        $db_query .= ' *';
    }

    $db_query .= ' FROM '.$obj->{'PREFIX'}.$IN->{'TABLE'};

    if ($IN->{'INDEX'}) {
		# $DB->quote($IN->{'INDEX'}->{'VALUE'}
        $db_query .= " WHERE "."$IN->{'INDEX'}->{'KEY'}=".$DB->quote($IN->{'INDEX'}->{'VALUE'});



        my $sth = $DB->prepare($db_query);
#mylog("QUERY:    $db_query");
        $sth->execute() || ($obj->{'error'} = "Can't query the data from \"$IN->{'TABLE'}\". $DBI::errstr" and return);

        my $return = {};
        $return = $obj->make_hash_ref($sth);
        ++$obj->{query_c};
		return $return || {};       
    }
    

    #Build the search query
    
    if ($IN->{'WHERE'}) {
        $db_query .= ' WHERE ';
    	$db_query .= parse_where($IN->{'WHERE'});
    }
    
    $obj->{'query'} = $IN->{'WHERE'} || $obj->{'cur_p_key'}.' <> 0';
    
    if ($IN->{'MATCH'} eq 'WITH COUNT') {
        my $count_query = 'SELECT COUNT(*) FROM '.$obj->{'PREFIX'}.$IN->{'TABLE'}.' WHERE '.$obj->{'query'};
        my $sub = $DB->prepare($count_query);

        $sub->execute() || ($obj->{'error'} = "Can't count data from '$obj->{'PREFIX'}$IN->{'DBID'}$IN->{'TABLE'}' table. $DBI::errstr" and return);
        $obj->{matched_records} = $sub->fetchrow_array();
    }

    $db_query .= " ORDER BY $IN->{'SORT_KEY'}" if $IN->{'SORT_KEY'};

    if ($IN->{'SORT_BY'} eq "Z-A") {
        $db_query .= " DESC";
    }


    if ($IN->{'RANGE'}) {
        my ($from, $to) = split " to ", $IN->{'RANGE'};
        my $total = $to - $from + 1;

        $db_query .= " OFFSET $from" if $from;
        $db_query .= " LIMIT $total";
    }

    $obj->{'query'} = $db_query;
    my $sth = $DB->prepare($db_query);
#mylog("QUERY:    $db_query");
	$sth->execute() || ($obj->{'error'} = "Can't query the data from '$IN->{'TABLE'}'\nReason: $DBI::errstr\nQuery: $db_query" and return);
    
    #Hmmm... I'd kill for a fetchall_hashref();
    
    if ($IN->{'MATCH'} eq 'ONE') {
        ++$obj->{query_c};
	
        return $obj->make_hash_ref($sth);
    }

    my @return;

    while (my $row = $obj->make_hash_ref($sth)){
        push (@return, $row);    
    }

	

    ++$obj->{query_c};
    return \@return;
}


#----------------------------------------------------------------
# insert, overrides base class method
#----------------------------------------------------------------

sub insert {

    my $obj = shift;
       $obj->{'error'} = undef;
    my $IN = {  
                "TABLE"    => "",
                "VALUES"   => {},
                "DBID"     => "", # DEPRECIATED IN SQL
                "ID"       => "", # DEPRECIATED IN SQL
                @_
              };  


    $obj->load_cfg($IN->{'TABLE'});

    my $DB = $obj->{'DB'};

    my $table = $obj->{'PREFIX'}.$IN->{'TABLE'};

    my (@fields, @values);

    for my $k (keys %{ $IN->{'VALUES'} }) {
        next unless exists $obj->{'all_cols'}->{$k};
        push @fields, $k;
        push @values, $DB->quote($IN->{'VALUES'}->{$k});
    }

    #This is slightly contrived... Must fix later.
    #I know there is an 'auto_increment' protocol on mySQL
    #But we need to return the new ID, so we'd have to query
    #twice anyway..

    if ($obj->{'all_cols'}->{$obj->{'cur_p_key'}}[1] eq 'update') {
        my $q = "SELECT $obj->{'cur_p_key'} FROM $table order by $obj->{'cur_p_key'} desc limit 1";
        my $sth = $DB->prepare($q);

        $sth->execute() || ($obj->{'error'} = "Can't query data to '$table' table. Query: $q. $DBI::errstr" and return);

        my $new_id = $sth->fetch();
        push @fields, $obj->{'cur_p_key'};
        push @values, $new_id->[0]+1;
        $IN->{'VALUES'}->{ $obj->{'cur_p_key'} } = $new_id->[0]+1;
    }


    my $db_query = "INSERT INTO $table (".join(", ", @fields).") VALUES (".join(", ", @values).")";

    $DB->do($db_query) || ($obj->{'error'} = "Can't insert data to '$table' table. $DBI::errstr" and return);
#mylog("INSERT:    $db_query");
    # Return the new ID (Incase we need it)
    ++$obj->{query_c};
    return $IN->{'VALUES'}->{ $obj->{'cur_p_key'} };
 
}

#----------------------------------------------------------------
# delete, overrides base class method
#----------------------------------------------------------------

sub delete  {
	
    my $obj = shift;
       $obj->{'error'} = undef;
    my $IN = {  
                "TABLE"   => "",
                "KEY"     => "",
                "DBID"    => "", #DEPRECIATED IN SQL
                "ID"      => "", #DEPRECIATED IN SQL
                "WHERE"   => "",
                @_
              };

    $obj->load_cfg($IN->{'TABLE'});

    my $DB = $obj->{'DB'};

    my $table = $obj->{'PREFIX'}.$IN->{'TABLE'};

    my $statement;

    if ($IN->{'WHERE'} ne '') {
        $statement = parse_where($IN->{'WHERE'});
    } else {
		if (ref($IN->{'KEY'}) ne 'ARRAY') {
			$statement = $obj->{'cur_p_key'}.'='.$DB->quote($IN->{'KEY'});
		} else {
            my @keys;
			for (@{$IN->{'KEY'}}) {
                $statement .= $obj->{'cur_p_key'}.'='.$DB->quote($_).' OR ';
            }
            # Remove last ' OR '
            $statement =~ s!\s{1,}OR\s*$!!;
		}
        
	}

    my $db_query = "DELETE FROM $table WHERE $statement";
    $DB->do($db_query) || ($obj->{'error'} = "Can't delete data from '$table' table. $DBI::errstr" and return);
#mylog("DELETE:    $db_query");
    ++$obj->{query_c};
    return 1;
}


#----------------------------------------------------------------
# update, overrides base class method
#----------------------------------------------------------------

sub update  {
	
    my $obj = shift;
       $obj->{'error'} = undef;
    my $IN = {  
                "TABLE"   => "",
                "VALUES"  => "",
                "KEY"     => "",
                "WHERE"   => "",
                "DBID"    => "", #DEPRECIATED IN SQL
                "ID"      => "", #DEPRECIATED IN SQL
                @_
              };


    $obj->load_cfg($IN->{'TABLE'});

    my $DB = $obj->{'DB'};

    my $table = $obj->{'PREFIX'}.$IN->{'TABLE'};

    my (@fields, $statement);

    for my $k (keys %{ $IN->{'VALUES'} }) {
        next unless exists $obj->{'all_cols'}->{$k};
        push @fields, $k.'='.$DB->quote($IN->{'VALUES'}->{$k});
    }

    if ($IN->{'WHERE'} ne '') {
        $statement = parse_where($IN->{'WHERE'});
    } else {
		if (ref($IN->{'KEY'}) ne 'ARRAY') {
			$statement = $obj->{'cur_p_key'}.'='.$DB->quote($IN->{'KEY'});
        } else {
		    my @keys;
			for (@{$IN->{'KEY'}}) {
                $statement .= $obj->{'cur_p_key'}.'='.$DB->quote($_).' OR ';
            }
            # Remove last ' OR '
            $statement =~ s!\s{1,}OR\s*$!!;
		}
        
	}

    my $db_query = "UPDATE $table SET ".join(", ", @fields)." WHERE $statement";
#mylog("UPDATE:    $db_query");
    $DB->do($db_query) || ($obj->{'error'} = "Can't update data to '$table' table. $DBI::errstr" and return);

    ++$obj->{query_c};
}

#----------------------------------------------------------------
# count, overrides base class method
#----------------------------------------------------------------


sub count {
    my $obj = shift;
    my $IN = {  
                "TABLE"    => "",
                "DBID"     => "",
                "ID"       => "",
                @_,
              };

    $obj->load_cfg($IN->{'TABLE'});

    my $DB = $obj->{'DB'};
    my $count = 0;
    ++$obj->{query_c};
    my $db_query = 'SELECT COUNT(*) FROM '.$obj->{'PREFIX'}.$IN->{'TABLE'};
    my $sth = $DB->prepare($db_query);
    $sth->execute() || ($obj->{'error'} = "Can't count data from '$obj->{'PREFIX'}$IN->{'DBID'}$IN->{'TABLE'}' table. $DBI::errstr" and return);
    return $sth->fetchrow_array();
}

#----------------------------------------------------------------
# table import, overrides base class method
#----------------------------------------------------------------
      
sub table_import {
	my $obj = shift;

    my $DB = $CONN;
    
    my $IN = {  
                "TABLE"   => "",
                "SOURCE"  => "",
                "DBID"    => "",
                "ID"      => "",
                @_
             };
    $obj->load_cfg($IN->{'TABLE'});

    # Ok, we A.S.S.U.ME that these files may be potentially large.
    # So, the first step is to identify which entries we're going to need.
    # Then, copy these entries into a text file to work with.
    # If we are not fussed on ID's and DBIDs, then we just suck it all up.
    
    # Open up the source file..
    open SOURCE, "$IN->{'SOURCE'}/$IN->{'TABLE'}.txt" or die "Cannot find $IN->{'SOURCE'}/$IN->{'TABLE'}.txt to open";
    
    # Open up the temp file...
    open TEMP, ">$IN->{'SOURCE'}/$IN->{'TABLE'}.tmp" or die "Cannot create $IN->{'SOURCE'}/$IN->{'TABLE'}.tmp";

    # Start the loop..
    while (<SOURCE>) {
        if ($IN->{DBID}) {
            next unless m#^$IN->{'DBID'}-#;
        }
        if ($IN->{ID}) {
            next unless m#^(.+?)-$IN->{ID}\|\*\|#;
        }
        #chomp;
        /^(.+?)\|\*\|(.*)$/;
        my $data = $2;
        print TEMP $data."\n";
    }
    close SOURCE;
    close TEMP;

    # Now we have our needed file to import, lets do so...
    my $table = $obj->{'PREFIX'}.$IN->{'TABLE'};
    # Open the text file..
    open TEMP2, "$IN->{'SOURCE'}/$IN->{'TABLE'}.tmp" or die "Cannot find the $IN->{'TABLE'} temp file $!";
    while (<TEMP2>) {
        chomp;
        next unless $_;
        
        my $data = $obj->decode_record($_);
        next unless $data->{ $obj->{'cur_p_key'} };
        my (@fields, @values);
    
        for my $k (keys %{ $data }) {
            push @fields, $k;
            push @values, $DB->quote($data->{$k});
        }
    
        my $db_query = "INSERT INTO ".$table." (".join(", ", @fields).") VALUES (".join(", ", @values).")";
        $DB->do($db_query); # || die "SQL error $db_query in table '$table'. $DBI::errstr";

    }
    close TEMP2;

    # Remove the work file...
    unlink ("$IN->{'SOURCE'}/$IN->{'TABLE'}.tmp");

}

#----------------------------------------------------------------
# backup, overrides base class method
#----------------------------------------------------------------

sub back_up {
    my $obj = shift;
    
    # EXPORTING DATABASE
    #
    # This will export all the information to a single text file per table

    # The format for each entry is:
    
    # DBID-ID|*|values


    my $IN = {  
                "TABLE"          => "",   #Provide table name
                "DESTINATION"    => "",   #Root directory for destination
                @_
              };
              
    my $DB = $CONN;
    my $table = $obj->{'PREFIX'}.$IN->{'TABLE'};
    $obj->load_cfg($IN->{'TABLE'});
    #Ensure the destination directory is trailing slash free.

    $IN->{'DESTINATION'} =~ s!/$!!;

    unless (-e $IN->{'DESTINATION'}) {
        $obj->{'error'} = "Could not find the destination directory to back-up into";
        return;
    }
    unless (-w $IN->{'DESTINATION'}) {
        $obj->{'error'} = "Permission Denied: Cannot write into the destination directory.";
        return;
    }
    
    my $destination = "$IN->{'DESTINATION'}/$IN->{'TABLE'}.txt";


    my $sth = $DB->prepare("SELECT * FROM $table");
    $sth->execute() || ($obj->{'error'} = "Can't query the data from '$IN->{'TABLE'}'\nReason: $DBI::errstr" and return);
    # Open up our text file..
    open F, ">".$destination;
    while (my $row = $sth->fetchrow_hashref()) {
        my ($id, $dbid);
        if ($obj->{'cur_ID'}) {
            $id   = $row->{ $obj->{'cur_ID'}   };
        }
        if ($obj->{'cur_DBID'}) {
            $dbid = $row->{ $obj->{'cur_DBID'} };
        }
        my $c_row = $obj->encode_record( $row );
        print F "$dbid-$id|*|".$c_row."\n";   
    }
    # Close the text file
    close F;
    
    # Export the index if needed
    if ($obj->{'cur_INDEX'}) {
        for my $key ( keys %{ $obj->{'cur_INDEX'} } ) {
            my $sth = $DB->prepare("SELECT $key, $obj->{'cur_INDEX'}->{ $key } FROM $table");
            $sth->execute();
            open IND, ">$IN->{'DESTINATION'}/$IN->{'TABLE'}-$key.index";
            while (my $row = $sth->fetchrow_hashref()) {
                print IND "$row->{ $key }|*|$row->{ $obj->{'cur_INDEX'}->{ $key } }\n";
            }
            close IND;
        }
    }
    return "0 but true";
}

#----------------------------------------------------------------
# Create Table: Creates a table, all 
# errors to $obj->{'error'}, returns 1 on success.
#----------------------------------------------------------------

sub create_table {


    my $obj = shift;
       $obj->{'error'} = undef;

    my $IN = {  
                "TABLE"   => "",
                "DBID"    => "",
                @_,
              };

    my $DB = $obj->{'DB'};

    $obj->load_cfg($IN->{'TABLE'});

    $IN->{'DBID'} = $IN->{'DBID'} ? $IN->{'DBID'}.'_' : '';

    # Start the query with Example: CREATE TABLE ib_f2_forum_posts (

    my $db_query = "CREATE TABLE ".$obj->{'PREFIX'}.$IN->{'DBID'}.$IN->{'TABLE'}." (";

    foreach my $entry (sort { $obj->{'all_cols'}->{$a}[0] <=> $obj->{'all_cols'}->{$b}[0] } keys %{$obj->{'all_cols'}} ) {

        my $field = $entry;

        if ($obj->{'all_cols'}->{$entry}[1] eq 'update') {

          if ($obj->{'all_cols'}->{$entry}[2] <= 3) {
                $field .= " smallint";
          } elsif ($obj->{'all_cols'}->{$entry}[1] <= 5) {
                $field .= " integer";
          } elsif ($obj->{'all_cols'}->{$entry}[1] <= 7) {
                $field .= " integer";
          } elsif ($obj->{'all_cols'}->{$entry}[1] <= 10) {
                $field .= " bigint";
          } elsif ($obj->{'all_cols'}->{$entry}[1] <= 19) {
                $field .= " numeric($obj->{'all_cols'}->{$entry}[2])";
          } else {
                $field .= " integer";
          }

        } elsif ($obj->{'all_cols'}->{$entry}[1] eq 'num') {

          if ($obj->{'all_cols'}->{$entry}[2] <= 3) {
                $field .= " smallint";
          } elsif ($obj->{'all_cols'}->{$entry}[2] <= 5) {
                $field .= " integer";
          } elsif ($obj->{'all_cols'}->{$entry}[2] <= 7) {
                $field .= " integer";
          } elsif ($obj->{'all_cols'}->{$entry}[2] <= 10) {
                $field .= " bigint";
          } elsif ($obj->{'all_cols'}->{$entry}[2] <= 32) {
                $field .= " numeric($obj->{'all_cols'}->{$entry}[2])";
          } else {
                $field .= " integer";
          }

        } elsif ($obj->{'all_cols'}->{$entry}[1] eq 'string') {

          if ($obj->{'all_cols'}->{$entry}[2] <= 128) {
                $field .= " VARCHAR($obj->{'all_cols'}->{$entry}[2])";
          } elsif ($obj->{'all_cols'}->{$entry}[2] < 256) {
                $field .= " VARCHAR($obj->{'all_cols'}->{$entry}[2])";
          } elsif ($obj->{'all_cols'}->{$entry}[2] <= 1024) {
                $field .= " TEXT";
          } else {
                $field .= " VARCHAR(255)";
          }

        } elsif ($obj->{'all_cols'}->{$entry}[1] eq 'text') {

          if ($obj->{'all_cols'}->{$entry}[2] <= 65535) {
                $field .= " TEXT";
          } elsif ($obj->{'all_cols'}->{$entry}[2] <= 4294967295) {
                $field .= " TEXT";
          } else {
                $field .= " TEXT";
          }

        } else {
          $field .= " VARCHAR(255)";
        }

        if ($obj->{'all_cols'}->{$entry}[3]) {
          $field .= " NOT NULL";
        }

        
        $db_query .= $field.',';
    }

    $db_query .= "PRIMARY KEY ($obj->{'cur_p_key'})";
    $db_query .= ");";

    my $sth = $DB->prepare($db_query);
    $sth->execute() || ($obj->{'error'} = "Can't execute $db_query. $DBI::errstr $!" and return);

    return 1;

}

#----------------------------------------------------------------
# drop table
#----------------------------------------------------------------

sub drop_table {

    my $obj = shift;
    # Return unless we can create tables
    return unless $obj->{_can_drop};

    my %IN = ( TABLE    => "",
               DBID     => "",
               @_,
             );

    $obj->load_cfg($IN{'TABLE'});

    my $DB    = $CONN;
    my $table = $obj->{'PREFIX'}.$IN{'DBID'}.$IN{'TABLE'};

    my $db_query = "DROP TABLE IF EXISTS $table";
    my $sth= $DB->prepare($db_query);
	   $sth->execute() || ($obj->{'error'} = "Can't drop table $table (Query: $db_query). $DBI::errstr $!" and return);
    return 1;
}

#----------------------------------------------------------------
# drop tables
#----------------------------------------------------------------


sub drop_tables {
    my $obj = shift;
    my %IN = ( TABLES => [], @_, );
    foreach my $table (@{$IN{'TABLES'}}) {
        $obj->drop_table( TABLE => $table->{'TABLE'},
                          DBID  => $table->{'DBID'},
                        );
    }
    return 1;
}

#----------------------------------------------------------------
# drop database
#----------------------------------------------------------------

sub drop_database {

    my $obj = shift;

    my $DB = $CONN;
    
    # Grab the table names.
    opendir DIR, $obj->{'base_dir'}.'config';
    my @files = grep { !/^\./ && !/^\.html/ } readdir DIR;
    closedir DIR;
    
    for my $table (@files) {
        $table =~ s/\.cfg//i;
        $table = $obj->{'PREFIX'}.$table;

        $DB->prepare("DELETE FROM $table");
    }
}   

#----------------------------------------------------------------
# rebuild records
#----------------------------------------------------------------


sub rebuild_record {
    my $obj = shift;
    my $IN = {  
                "TABLE"   => "",
                "STRING"  => "",
                "VALUES"  => {},
                @_
             };
             

    $obj->load_cfg($IN->{'TABLE'});
    
    my $record = $obj->decode_record($IN->{STRING});
    # Override the column values from the string with those
    # given to us by the accessing script.
    if ($IN->{VALUES}) {
        for my $k (keys %{ $IN->{VALUES} }) {
            $record->{$k} = $IN->{VALUES}->{$k};
        }
    }
    return $record;
}


#---------------------------------------------------------
# Parse where statememnt
#---------------------------------------------------------
# Allows you to fine tune your where statement for your
# driver. Default behaviour suits PostgreSQL.

sub parse_where {
    my ($obj, $where) = @_;
        my %ops = ( 'eq' => '=',
                    'ne' => '<>',
                    '==' => '=',
                    '!=' => '<>',
                    '=~' => '~*',
                    '!~' => '!~*'
                  );
        my %bln = ( 'and' => 'AND',
                    'or'  => 'OR',
                    '&&'  => 'AND'
                  );

		# delete all \ escapes - not supported by postgres
		$_[0] =~ s#\\.##g;
        $_[0] =~ s#(eq|ne|==|!=|=~)#$ops{$1}#ig;
        $_[0] =~ s#\s{1,}(and|or|&&)\s{1,}# $bln{$1} #ig;
        # Convert perl / into single quotes...
        $_[0] =~ s#/#'#g;
		# case-insensivity 
		$_[0] =~ s/ ('.*?')i/\* $1/g;

    	$_[0]=~s/"/'/g;
    	return $_[0];
}

#----------------------------------------------------------------
# SQL client:
#----------------------------------------------------------------

sub client_do_do {
    my $obj = shift;
    $obj->{'error'} = undef;
    my $IN = {  
                "QUERY"   => "",
                @_
              };
    
    my $DB = $obj->{'DB'};
    my $query = parse($IN->{'QUERY'});

    my $sth = $DB->do($query) || ($obj->{'error'} = "Can't handle your request: $DBI::errstr" and return);
    ++$obj->{query_c};

    return $sth;
}

sub client_exec {
    my $obj = shift;
    $obj->{'error'} = undef;
    my $IN = {  
                "QUERY"   => "",
                @_
              };
    
    my $DB = $obj->{'DB'};
    my $query = parse($IN->{'QUERY'});

    my $sth = $DB->prepare($query);
    $sth->execute() || ($obj->{'error'} = "Can't handle your request: $DBI::errstr" and return);
    ++$obj->{query_c};

    return $sth;
}

sub client_do {
    my $obj = shift;
    $obj->{'error'} = undef;
    my $IN = {  
                "QUERY"   => "",
                @_
              };
    
    my $DB = $obj->{'DB'};
    my $query = parse($IN->{'QUERY'});

    my $sth = $DB->prepare($query);

    $sth->execute() || ($obj->{'error'} = "Can't handle your request: $DBI::errstr" and return);

    my @return;

    while (my $row = $obj->make_hash_ref($sth)){
        push (@return, $row);    
    }

    ++$obj->{query_c};
    return \@return;  
}

sub tables {
    my $obj = shift;

    my $db = $obj->{'DB'};

    my @tables = $db->tables;

    return \@tables;

}

sub table_attr {
    my $obj = shift;
    my $db = $obj->{'DB'};
    my $IN = {  
                "TABLE"   => "",
                @_
             };

    my $attr = $db->func($IN->{TABLE}, 'table_attributes');
    return $attr;
}


sub parse {
    my ($obj, $where) = @_;

    $_[0] =~ s|&#33;|!|g;
    $_[0] =~ s|&amp;|&|g;
    $_[0] =~ s|&gt;|>|g;
    $_[0] =~ s|&lt;|<|g;
    $_[0] =~ s|&quot;|"|g;
    $_[0] =~ s!&#124;!\|!g;
    $_[0] =~ s|\n||g;
    $_[0] =~ s|&#036;|\$|g;
    $_[0] =~ s|&#92;|\\|g;
    $_[0] =~ s|&#039;|\'|g;

  	return $_[0];

}
#----------------------------------------------------------------
# Infection's Log File :-)
#----------------------------------------------------------------

sub mylog{
	open(LOG,">>/tmp/pgSQL.pm.log");
	print LOG "$_[0]\n";
	close(LOG);
}

#+---------------------------------------------------------------------------------

package iDatabase::Driver::pgSQL::Bind;

# Uses a tiearray method to tie an array of results to an array or array ref.
# this increases efficiency as we don't have to return the whole array in one
# go, we can use SQL's $sth->fetchrow() command to return a row when it's requested
# by the script.

# This has little use in the mySQL implementation as the LIMIT clause is good
# enough to reduce the amount of data being returned.

# Usage: tie @return, iDatabase::Driver::mySQL::Bind, $obj, $table_name;

use strict;
use vars qw($OBJ $IDX); # for mod_perl
# Use arrays for speed, $OBJ is an easy way to indentify an array
# index with that blessed value
$OBJ   = 0; 
$IDX   = 1;

sub TIEARRAY {
    my ($pkg, $obj) = @_;
    bless[ $obj, [] ], $pkg;
}

sub FETCH {
    my ($self, $index) = @_;
    # Use arrays for speed, $OBJ is an easy way to 
    # indentify an array index with that blessed value
    return $self->[$IDX]->[$index];
    # Same as $self-[1]->[$index];
}

sub STORE {
    my ($self, $index, $value) = @_;
    # Add limit clauses here, for example
    # return undef unless $index >= $self->[$OBJ]->{limit_from};
    # return undef if $index > $self->[$OBJ]->{limit_from};
    $self->[$IDX]->[$index] = $value;
}

  
sub FETCHSIZE { my $self = shift; scalar @{$self->[$IDX]}               } 
sub DESTROY   { my $self = shift; undef $self                           }
sub EXISTS    { my $self = shift; return $self->[$IDX]->[$_[1]] ? 1 : 0 }
sub DELETE    { my $self = shift; undef  $self->[$IDX]->[$_[1]]         }
sub CLEAR     { my $self = shift; @{$self->[$IDX]} = ()          }
sub POP       { my $self = shift; pop(@{$self->[$IDX]})          } 
sub PUSH      { my $self = shift; push(@{$self->[$IDX]},@_)      }
sub SHIFT     { my $self = shift; shift(@{$self->[$IDX]})        } 
sub UNSHIFT   { my $self = shift; unshift(@{$self->[$IDX]},@_)   } 

#+---------------------------------------------------------------------------------------------
1;       # End of Module
#+---------------------------------------------------------------------------------------------