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

import java.sql.*;

import junit.framework.TestCase;
import org.postgresql.test.TestUtil;

public class BenchTest extends TestCase {

    /**
     * Number of times the table is queried.
     */
    private static final int QUERY_LOOP = 20;

    /**
     * Number of columns in each row.
     */
    private static final int COLS = 9;

    /**
     * Number of rows in temporary table.
     */
    private static final int ROWS = 20;

    /**
     * How many tries long to prime the jit.
     */
    private static final int PRIME_LOOP = 100;

    /**
     * How many times to run the timing and print the times.
     */
    private static final int TIMING_LOOP = 200;
    
    private Connection _conn;

    private PreparedStatement insert;

    private String queryString;

    private PreparedStatement query;
    
    public BenchTest(String name) {
        super(name);
    }

    protected void setUp() throws Exception {
        _conn = TestUtil.openDB();
        createTable();

        _conn.setAutoCommit(false);

        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO hold (key");
        for (int i=0; i<COLS; ++i) {
            sb.append(", val").append(i);
        }
        sb.append(") VALUES (?");
        for (int i=0; i<COLS; ++i) {
            sb.append(",?");
        }
        sb.append(")");
        //System.out.println(sb);

        insert = _conn.prepareStatement(sb.toString());

        sb.setLength(0);
        sb.append("SELECT ");
        for (int i=0; i<COLS; ++i) {
            sb.append("val").append(i).append(',');
        }
        sb.setLength(sb.length()-1);
        sb.append(" FROM hold");

        query = _conn.prepareStatement(sb.toString());
        query.setFetchSize(ROWS);
    }

    private void createTable() throws SQLException {
	StringBuilder sb = new StringBuilder();
        sb.append("CREATE TEMP TABLE hold(key int");
        for (int i=0; i<COLS; ++i) {
            sb.append(", val").append(i);
            switch (i % 3) {
            case 0:
        	sb.append(" varchar(16)");
        	break;
            case 1:
        	sb.append(" int4");
        	break;
            case 2:
        	sb.append(" int8");
        	break;
            }
        }
        sb.append(") ON COMMIT PRESERVE ROWS");
	Statement stmt = _conn.createStatement();
        stmt.execute(sb.toString());
        stmt.close();
        _conn.commit();
    }

    protected void tearDown() throws SQLException {
	insert.close();
	query.close();
        dropTable();
        TestUtil.closeDB(_conn);
    }

    private void dropTable() {
	Statement stmt;
	try {
	    stmt = _conn.createStatement();
	    stmt.execute("DROP TABLE hold");
	    stmt.close();
           _conn.commit();
	} catch (SQLException e) {
	    // ignored
	}
    }

    public void testBenchmark() throws Exception {
        insert();
        
        for (int i=0; i<PRIME_LOOP; ++i) {
            query();
        }
        
        // let jit do it's magic and flush all initial garbage
        Thread.sleep(50);
        System.runFinalization();
        System.gc();
        Thread.sleep(500);

        
        double best = 0;
        long maxMemUse = 0;
        long start, stop;
        for (int i=0; i<TIMING_LOOP; ++i) {
            start = System.currentTimeMillis();
            for (int j=0; j<QUERY_LOOP; ++j) {
        	query();
            }
            stop = System.currentTimeMillis();
            Runtime rt = Runtime.getRuntime();
            long memUse = rt.totalMemory() - rt.freeMemory();
            if (memUse > maxMemUse) {
                maxMemUse = memUse;
            }
            
            double speed = QUERY_LOOP * 1000.0 / (stop-start);
            if (speed > best) {
        	best = speed;
            }
            System.out.printf("speed: %7.2f %7.2f (%3dms) memory: %5.1fMB %5.1fMB\n",
                    speed, best, (stop-start), memUse/1024.0/1024.0, maxMemUse/1024.0/1024.0);
        }
    }

    private void query() throws SQLException {
	for (int i=0; i<20; ++i) {
	    ResultSet rs = query.executeQuery();
	    for (int r=0; r<ROWS; ++r) {
		assertTrue(rs.next());
		for (int c=0; c<COLS; ++c) {
		    switch (c % 3) {
		    case 0:
			assertEquals("value", rs.getString(c+1));
			break;
		    case 1:
			assertTrue(1000 == rs.getInt(c+1));
			break;
		    case 2:
			assertTrue(1000*1000 == rs.getLong(c+1));
			break;
		    }
		}
	    }
	    rs.close();
	}
    }

    private void insert() throws SQLException {
	for (int r=0; r<ROWS; ++r) {
	    insert.setInt(1, r);
	    for (int c=0; c<COLS; ++c) {
		switch (c % 3) {
		case 0:
		    insert.setString(c+2, "value");
		    break;
		case 1:
		    insert.setInt(c+2, 1000);
		    break;
		case 2:
		    insert.setLong(c+2, 1000*1000);
		    break;
		}
	    }
	    assertFalse(insert.execute());
	    assertTrue(1 == insert.getUpdateCount());
	}
	_conn.commit();
    }
}
