/*-------------------------------------------------------------------------
*
* Copyright (c) 2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
*   $PostgreSQL: pgjdbc/org/postgresql/test/jdbc3/Jdbc3BlobTest.java,v 1.3 2005/11/24 02:31:44 oliver Exp $
*
*-------------------------------------------------------------------------
*/
package org.postgresql.test.jdbc3;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;

import junit.framework.TestCase;

import org.postgresql.test.TestUtil;

/**
 * @author Michael Barker <mailto:mike@middlesoft.co.uk>
 *
 */
public class Jdbc3BlobTest extends TestCase
{
    Connection _conn;
    private final static String TABLE = "blobtest";
    private final static String INSERT = "INSERT INTO " + TABLE + " VALUES (1, lo_creat(-1))";
    private final static String SELECT = "SELECT ID, DATA FROM " + TABLE + " WHERE ID = 1";

    public Jdbc3BlobTest(String name)
    {
        super(name);
    }

    protected void setUp() throws Exception
    {
        _conn = TestUtil.openDB();
        TestUtil.createTable(_conn, TABLE, "ID INT PRIMARY KEY, DATA OID");
        _conn.setAutoCommit(false);
    }

    protected void tearDown() throws SQLException
    {
        _conn.setAutoCommit(true);
        TestUtil.dropTable(_conn, TABLE);
        TestUtil.closeDB(_conn);
    }

    /**
     * Test the writing and reading of a single byte.
     * 
     * @throws SQLException
     */
    public void test1Byte() throws SQLException
    {
        byte[] data = { (byte) 'a' };
        readWrite(data);
    }

    /**
     * Test the writing and reading of a few bytes.
     * @throws SQLException
     */
    public void testManyBytes() throws SQLException
    {
        byte[] data = "aaaaaaaaaa".getBytes();
        readWrite(data);
    }

    /**
     * Test writing a single byte with an offset.
     * 
     * @throws SQLException
     */
    public void test1ByteOffset() throws SQLException
    {
        byte[] data = { (byte) 'a' };
        readWrite(10, data);
    }

    /**
     * Test the writing and reading of a few bytes with an offset.
     * 
     * @throws SQLException
     */
    public void testManyBytesOffset() throws SQLException
    {
        byte[] data = "aaaaaaaaaa".getBytes();
        readWrite(10, data);
    }

    /**
     * Tests all of the byte values from 0 - 255.
     * 
     * @throws SQLException
     */
    public void testAllBytes() throws SQLException
    {
        byte[] data = new byte[256];
        for (int i = 0; i < data.length; i++)
        {
           data[i] = (byte) i;
        }
        readWrite(data);
    }


    /**
     * 
     * @param data
     * @throws SQLException
     */
    public void readWrite(byte[] data) throws SQLException
    {
        readWrite(1, data);
    }

    /**
     * 
     * @param offset
     * @param data
     * @throws SQLException
     */
    public void readWrite(int offset, byte[] data) throws SQLException
    {

        PreparedStatement ps = _conn.prepareStatement(INSERT);
        ps.executeUpdate();
        ps.close();

        ps = _conn.prepareStatement(SELECT);
        ResultSet rs = ps.executeQuery();

        assertTrue(rs.next());
        Blob b = rs.getBlob("DATA");
        b.setBytes(offset, data);

        rs.close();
        ps.close();

        ps = _conn.prepareStatement(SELECT);
        rs = ps.executeQuery();

        assertTrue(rs.next());
        b = rs.getBlob("DATA");
        byte[] rspData = b.getBytes(offset, data.length);
        assertTrue("Request should be the same as the response", Arrays.equals(data, rspData));
 
        rs.close();
        ps.close();
    }


    /**
     * Test the writing and reading of a single byte.
     * 
     * @throws SQLException
     * @throws IOException 
     */
    public void test1ByteStream() throws SQLException, IOException
    {
        byte[] data = { (byte) 'a' };
        readWriteStream(data);
    }

    /**
     * Test the writing and reading of a few bytes.
     * @throws SQLException
     * @throws IOException 
     */
    public void testManyBytesStream() throws SQLException, IOException
    {
        byte[] data = "aaaaaaaaaa".getBytes();
        readWriteStream(data);
    }

    /**
     * Test writing a single byte with an offset.
     * 
     * @throws SQLException
     * @throws IOException 
     */
    public void test1ByteOffsetStream() throws SQLException, IOException
    {
        byte[] data = { (byte) 'a' };
        readWriteStream(10, data);
    }

    /**
     * Test the writing and reading of a few bytes with an offset.
     * 
     * @throws SQLException
     * @throws IOException 
     */
    public void testManyBytesOffsetStream() throws SQLException, IOException
    {
        byte[] data = "aaaaaaaaaa".getBytes();
        readWriteStream(10, data);
    }

    /**
     * Tests all of the byte values from 0 - 255.
     * 
     * @throws SQLException
     * @throws IOException 
     */
    public void testAllBytesStream() throws SQLException, IOException
    {
        byte[] data = new byte[256];
        for (int i = 0; i < data.length; i++)
        {
           data[i] = (byte) i;
        }
        readWriteStream(data);
    }

    public void readWriteStream(byte[] data) throws SQLException, IOException
    {
        readWriteStream(1, data);
    }


    /**
     * Reads then writes data to the blob via a stream.
     * 
     * @param offset
     * @param data
     * @throws SQLException
     * @throws IOException 
     */
    public void readWriteStream(int offset, byte[] data) throws SQLException, IOException
    {

        PreparedStatement ps = _conn.prepareStatement(INSERT);
        ps.executeUpdate();
        ps.close();
       
        ps = _conn.prepareStatement(SELECT);
        ResultSet rs = ps.executeQuery();

        assertTrue(rs.next());
        Blob b = rs.getBlob("DATA");
        OutputStream out = b.setBinaryStream(offset);
        out.write(data);
        out.flush();
        out.close();

        rs.close();
        ps.close();

        ps = _conn.prepareStatement(SELECT);
        rs = ps.executeQuery();

        assertTrue(rs.next());
        b = rs.getBlob("DATA");
        InputStream in = b.getBinaryStream();
        byte[] rspData = new byte[data.length];
        in.skip(offset-1);
        in.read(rspData);
        in.close();

        assertTrue("Request should be the same as the response", Arrays.equals(data, rspData));
 
        rs.close();
        ps.close();
    }

    public void testPattern() throws SQLException
    {
        byte[] data = "abcdefghijklmnopqrstuvwxyx0123456789".getBytes();
        byte[] pattern = "def".getBytes();

        String insert = "INSERT INTO " + TABLE + " VALUES (1, lo_creat(-1))";

        PreparedStatement ps = _conn.prepareStatement(INSERT);
        ps.executeUpdate();
        ps.close();

        ps = _conn.prepareStatement(SELECT);
        ResultSet rs = ps.executeQuery();

        assertTrue(rs.next());
        Blob b = rs.getBlob("DATA");
        b.setBytes(1, data);

        rs.close();
        ps.close();

        ps = _conn.prepareStatement(SELECT);
        rs = ps.executeQuery();

        assertTrue(rs.next());
        b = rs.getBlob("DATA");
        long position = b.position(pattern, 1);
        byte[] rspData = b.getBytes(position, pattern.length);
        assertTrue("Request should be the same as the response", Arrays.equals(pattern, rspData));

        rs.close();
        ps.close();

    }
}
