package org.mariadb.jdbc;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Test;
import org.junit.Before;


public class GiganticLoadDataInfileTest {

    private Connection connection;

    @Before
    public void setUp() throws Exception {
        connection = DriverManager.getConnection("jdbc:mariadb://localhost:3306/test?user=root");
        Statement statement = connection.createStatement();
        statement.execute("drop table if exists gigantic_load_data_infile");
        statement.execute("create table gigantic_load_data_infile (id int not null primary key auto_increment, name char(20)) ENGINE=myisam");
    }
    
    @Test
    public void giganticLoadDataInfileTest() throws SQLException {
        Statement statement = connection.createStatement();
        
        VeryLongAutoGeneratedInputStream in = new VeryLongAutoGeneratedInputStream(30000000);
        
        if (statement.isWrapperFor(org.mariadb.jdbc.MySQLStatement.class)) {
            MySQLStatement mariaDbStatement = statement.unwrap(org.mariadb.jdbc.MySQLStatement.class);
            mariaDbStatement.setLocalInfileInputStream(in);
        } else if (statement.isWrapperFor(com.mysql.jdbc.StatementImpl.class)) {
            com.mysql.jdbc.StatementImpl mysqlStatement = statement.unwrap(com.mysql.jdbc.StatementImpl.class);
            mysqlStatement.setLocalInfileInputStream(in);
        } else {
            throw new RuntimeException("Mariadb or Mysql JDBC adaptor must be used");
        }
            
        String sql =
                        "LOAD DATA LOCAL INFILE 'dummyFileName' " +
                        "INTO TABLE gigantic_load_data_infile " + 
                        " FIELDS TERMINATED BY '\\t' ENCLOSED BY ''" +
                        " ESCAPED BY '\\\\' LINES TERMINATED BY '\\n'"
                        ;
            
        statement.execute(sql);

        ResultSet resultSet = statement.executeQuery("select count(*) from gigantic_load_data_infile");
        assertTrue(resultSet.next());
        int numberOfRowsInTable = resultSet.getInt(1);
        assertEquals(in.numberOfRows, numberOfRowsInTable);
        System.out.println("Total number of bytes in the LOAD DATA INFILE: " + in.getNumberOfBytesRead());
    }
    
    /**
     * Custom memory conserving generator of a LOAD DATA INFILE that generates a stream like this:
     * 
     * 1\tname1
     * 2\tname2
     * 3\tname3
     */
    private static class VeryLongAutoGeneratedInputStream extends InputStream {
        
        private final int numberOfRows;
        
        private int currentPosInBuffer;
        private byte[] buffer;
        
        private int currentRow;

        private int totalNumberOfBytesRead = 0;

        private VeryLongAutoGeneratedInputStream(int numberOfRows) {
            this.numberOfRows = numberOfRows;
            currentRow = 0;
        }
        
        @Override
        public int read() throws IOException {
            
            if (currentRow > numberOfRows) {
                return -1;
            }

            if (buffer != null && currentPosInBuffer >= buffer.length) {
                buffer = null;
            }
            
            if (buffer == null) {
                currentRow++;
                currentPosInBuffer = 0;
                buffer = new String(currentRow + "\tname" + currentRow + "\n").getBytes();
            }
            
            totalNumberOfBytesRead++;
            
            return buffer[currentPosInBuffer++];
        }
        
        private int getNumberOfBytesRead() {
            return totalNumberOfBytesRead;
        }
        
    }
    
}
