import java.sql.*;
import java.util.*;

public class TableDiff
{
	String url_src = null;
	String url_dst = null;
	String user = null;
	String pass = null;

	Connection con_src = null;
	Connection con_dst = null;

	int FETCH_SIZE = 4000;

	boolean printDebug = false;
//	boolean printDebug = true;

	int columnCount = 0;

	public TableDiff(String url_src, String url_dst, String user, String pass)
	{
		try {
			Class.forName("org.postgresql.Driver");
		}
		catch (Exception e)
		{
			error( e.getMessage() );
			System.exit(-1);
		}

		this.url_src = url_src;
		this.url_dst = url_dst;

		try {
			con_src = DriverManager.getConnection(this.url_src, user, pass);
			con_dst = DriverManager.getConnection(this.url_dst, user, pass);

			con_src.setAutoCommit(false);
			con_dst.setAutoCommit(false);
		}
		catch (Exception e)
		{
			error(e.getMessage());
			System.exit(-1);
		}
	}

	private String[] getColumnNames(String tableName)
	{
		Statement stmt = null;
		String[] columnNames = null;

		try {
			stmt = con_src.createStatement();

			String q = "SELECT attname FROM pg_attribute a, pg_class c " + 
				       " WHERE c.oid=a.attrelid " +
				       "   AND c.relname = '" +tableName+ "' AND a.attnum>0";

			ResultSet rs = stmt.executeQuery(q);

			if ( rs!=null )
			{
				ArrayList a = new ArrayList();

				while ( rs.next() )
				{
					a.add(rs.getString(1));
				}

				columnNames = new String[a.size()];

				for (int i=0 ; i<columnNames.length  ; i++)
				{
					columnNames[i] = (String)a.get(i);
				}

				rs.close();
			}

			stmt.close();
		}
		catch (Exception e)
		{
			error(e.getMessage());
			System.exit(-1);
		}

		return columnNames;
	}

	private String createQuery(String tableName)
	{
		String q = null;

		String[] columnNames = getColumnNames(tableName);
		String c = null;

		for (int i=0 ; i<columnNames.length ; i++)
		{
			if ( i>0 )
				c = c + "," + columnNames[i];
			else
				c = columnNames[i];
		}

		q = "SELECT " + c + " FROM " + tableName + " ORDER BY " + c;

		return q;
	}

	private boolean compareRow(String tableName, ResultSet rs1, ResultSet rs2,
							   int rows)
	{
		boolean rc = true;

		try {
			for (int i=1 ; i<=columnCount ; i++)
			{
				String c1 = rs1.getString(i);
				String c2 = rs2.getString(i);
				
				if ( c1==null && c2==null )
				{
					/* it's ok */
				}
				else if ( !c1.equals( c2 ) )
				{
					error("Table " + tableName + ", Row " + rows + ", Column " + i + " is not same.");
					rc = false;
					break;
				}
			}
		}
		catch (Exception e)
		{
			error(e.getMessage());
			System.exit(-1);
		}

		return rc;
	}

	public boolean compareTable(String tableName)
	{
		Statement stmt1 = null;
		Statement stmt2 = null;
		ResultSet rs1 = null;
		ResultSet rs2 = null;
		int rows = 0;
		boolean rc = true;

		String q = createQuery(tableName);

		try {

			debug("query = " + q);

			stmt1 = con_src.createStatement(ResultSet.TYPE_FORWARD_ONLY,
											ResultSet.CONCUR_READ_ONLY);
			stmt2 = con_dst.createStatement(ResultSet.TYPE_FORWARD_ONLY,
											ResultSet.CONCUR_READ_ONLY);

			stmt1.setFetchSize(FETCH_SIZE);
			stmt2.setFetchSize(FETCH_SIZE);

			rs1 = stmt1.executeQuery(q);
			rs2 = stmt2.executeQuery(q);

			if ( rs1!=null && rs2!=null )
			{
				columnCount = rs1.getMetaData().getColumnCount();

				debug("column count = " + columnCount);

				while ( rs1.next() && rs2.next() )
				{
					/*
					 * print different rows and return
					 */
					if ( !compareRow(tableName, rs1, rs2, rows) )
					{
						String r1 = null;
						String r2 = null;
						
						for (int i=1 ; i<=columnCount ; i++)
						{
							if ( i==1 )
							{
								r1 = rs1.getString(i);
								r2 = rs2.getString(i);
							}
							else
							{
								r1 = r1 + "," + rs1.getString(i);
								r2 = r2 + "," + rs2.getString(i);
							}
						}
						error("  [" + r1 + "]");
						error("  [" + r2 + "]");
						break;
					}

					rows++;
				}
			}
		}
		catch (Exception e)
		{
			error(e.getMessage());
			System.exit(-1);
		}

		try {
			rs1.close();
			rs2.close();

			stmt1.close();
			stmt2.close();
		}
		catch (Exception e)
		{
			error(e.getMessage());
			System.exit(-1);
		}

		debug("" + rows + " rows compared.");

		return rc;
	}

	public void close()
	{
		try {
			con_src.close();
			con_dst.close();
		}
		catch (Exception e)
		{
			error(e.getMessage());
			System.exit(-1);
		}
	}

	private void debug(String msg)
	{
		if ( printDebug )
			System.err.println("DEBUG: " + msg);
	}

	private void error(String msg)
	{
		System.err.println("ERROR: " + msg);
	}

	public static void usage()
	{
		System.out.println("Usage: TableDiff [URL1] [URL2] [USER NAME] [PASSSORD] [TABLE NAME]");
		System.out.println("");
	}

	public static void main(String[] args)
	{
		if ( args.length < 5 )
		{
			usage();
			System.exit(0);
		}

		String url1 = args[0];
		String url2 = args[1];
		String user = args[2];
		String pass = args[3];

		String table = args[4];

		TableDiff diff = new TableDiff(url1, url2, user, pass);

		if ( diff.compareTable(table) )
		{
			diff.close();
			System.exit(0);
		}
		else
		{
			diff.close();
			System.exit(1);
		}
	}
}
