/*
 * $Source: /usr/local/cvsroot/erserver/erserver/java/src/com/postgres/replic/util/TableColumnCache.java,v $
 * $Author: asullivan $ $Revision: 1.1.1.1 $ $Date: 2003/08/26 19:02:31 $
 *
 */

package com.postgres.replic.util;

import com.postgres.replic.server.props.*;
import com.postgres.replic.util.struct.*;
import java.sql.*;
import java.util.*;

public class TableColumnCache extends DBProcessor {
    // Hash of table columns: key is table name, value - array of column names
    private HashMap tableColumnsCache = new HashMap();
    private HashMap sqlUpdateCache = new HashMap();
    private HashMap sqlInsertCache = new HashMap();
    private HashMap sqlDeleteCache = new HashMap();

    // HAshes of PreparedStatement objects:
    private HashMap pstmtInsertCache = new HashMap();
    private HashMap pstmtUpdateCache = new HashMap();
    private HashMap pstmtDeleteCache = new HashMap();

    private TableMap tableMap;

    public TableColumnCache(Connection conn, TableMap tableMap, ServerProps serverProps)
    throws RservException, Exception {
        super.init(conn, serverProps);
        this.tableMap = tableMap;
        if (tableMap == null) {
             throw new RservException("TableColumnCache:: tableMap == null");
        }
        setTables();
        resetPstmtCache();
    }

    public int size(String tabname) {
        return getColumns(tabname).length;
    }

    /*
      @return table columns if table exists or null if it does not
    */
    public String [] getColumns(String tabname) {
         return (String []) tableColumnsCache.get(tabname);
    }

    /*
     * Populate tableColumnsCache
    */
    private void setTables() throws RservException, Exception {
        tableMap.setIterator();
        while (tableMap.hasNext()) {
            String tabname = tableMap.nextRowKey();
            tableMap.setRow(tabname);
            setTableColumns(tabname);
        }
    }

    /*
    */
    public String getSqlUpdate(String tabname) {
        return (String) sqlUpdateCache.get(tabname);
    }

    /*
    */
    private String getSqlUpdate(String tabname, String [] columns)
    throws RservException, Exception {
         //String sql = "insert into " + tabname + " values (";
         String sql = "update " + tabname + " set ";
         int i=0;
         for (; i<columns.length; i++) {
             String comma = i == 0 ? "" : ", ";
         	 sql += comma + columns[i] +  "=? ";
         }

         sql += " where " + tableMap.setRow(tabname).getAttName() + "=? ";

         return i == 0 ? null : sql;
    }

    /*
    */
    public String getSqlInsert(String tabname) {
        return (String) sqlInsertCache.get(tabname);
    }

    /*
    */
    private String getSqlInsert(String tabname, String [] columns)
    throws RservException {
         String sql = "insert into " + tabname + " values(";

         int i=0;
         for (; i<columns.length; i++) {
             String comma = i == 0 ? "" : ", ";
              sql += comma + "? ";
         }
         sql += ")";

         return i == 0 ? null : sql;
    }

    /*
    */
    public String getSqlDelete(String tabname) {
        return (String) sqlDeleteCache.get(tabname);
    }

    /*
    */
    private String getSqlDelete(String tabname, String [] columns)
    throws RservException, Exception {
         String sql = "delete from " + tabname + " where " +
         tableMap.setRow(tabname).getAttName() + "=? " ;

         return sql;
    }



    private void setTableColumns(String tabname) throws RservException {
        getLogger().debug("TableColumnsCache::setTableColumns: BEGIN tabname=" + tabname);

        ResultSet rs = null;
        Statement stmt = null;
        SnapshotDataBlockOld snapDataBlock = null;
        ArrayList tColumns = null;

        try {
            String sql =  "select attnum, attname from pg_attribute"
            + " where attrelid = " + tableMap.setRow(tabname).getOid()
            + " and attnum > 0 order by attnum";
            getLogger().debug("TableColumnsCache::setTableColumns: sql=" + sql);

            stmt = getConnection().createStatement();
            int row = 0;
            rs = stmt.executeQuery(sql);
            if (getDebug()) getLogger().debug("TableColumnsCache::setTableColumns: executed Query=" + sql);
            while (rs.next()) {
                row++;
                if (tColumns == null) {
                    tColumns = new  ArrayList();
                }

                String attName = rs.getString(2);
                getLogger().debug("TableColumnsCache::setTableColumns: trying to add column " + attName);

                tColumns.add(attName);
            }

            if (row == 0) {
                throw new Exception ( "TableColumnsCache::setTableColumns: No table columns retrieved");
            }

            // transform ArrayList into String array for better performance:
            String [] columns = new String[tColumns.size()];
            for (int i=0; i<tColumns.size(); i++) {
            	columns[i] = (String) tColumns.get(i);
            }

            getLogger().debug("TableColumnsCache::setTableColumns: trying to put in cache tabname="+tabname+"; tColumns=" + tColumns);
            tableColumnsCache.put(tabname, columns);

            // set SqlUpdate:
            sqlUpdateCache.put(tabname, getSqlUpdate(tabname, columns));

            // set SqlInsert:
            sqlInsertCache.put(tabname, getSqlInsert(tabname, columns));

            // set sqlDelete:
            sqlDeleteCache.put(tabname, getSqlDelete(tabname, columns));


            getLogger().debug("TableColumnsCache::setTableColumns: END" );
        } catch (Exception e) {
            throw new RservException("TableColumnsCache::setTableColumns: " +
                e.toString());
        } finally {
            generalClose(stmt, rs);
        }
    }

    protected PreparedStatement getPstmtInsert(String tabname) {
        return (PreparedStatement) pstmtInsertCache.get(tabname);
    }

    protected PreparedStatement getPstmtUpdate(String tabname) {
        return (PreparedStatement) pstmtUpdateCache.get(tabname);
    }

    protected PreparedStatement getPstmtDelete(String tabname) {
        return (PreparedStatement) pstmtDeleteCache.get(tabname);
    }

    /*
     * Reset pstmt caches:
    */
    protected void resetPstmtCache()
    throws SQLException, Exception {
        getLogger().debug("TableColumnsCache::resetPstmtCache: BEGIN " );

        destroyPsmt();

        pstmtInsertCache.clear();
        pstmtUpdateCache.clear();
        pstmtDeleteCache.clear();
        PreparedStatement pstmt = null;

        tableMap.setIterator();
        while (tableMap.hasNext()) {
            String tabname = tableMap.nextRowKey();
            tableMap.setRow(tabname);

            // Update:
            pstmt = getConnection().prepareStatement(getSqlUpdate(tabname));
            pstmtUpdateCache.put(tabname, pstmt);

            // Insert:
            pstmt = getConnection().prepareStatement(getSqlInsert(tabname));
            pstmtInsertCache.put(tabname, pstmt);

            // Delete:
            pstmt = getConnection().prepareStatement(getSqlDelete(tabname));
            pstmtDeleteCache.put(tabname, pstmt);
        }
        getLogger().debug("TableColumnsCache::resetPstmtCache: END " );
    }

    public void destroyPsmt() throws Exception {
        getLogger().debug("TableColumnsCache::destroyPsmt: BEGIN " );
        PreparedStatement pstmt = null;
        tableMap.setIterator();
        while (tableMap.hasNext()) {
            String tabname = tableMap.nextRowKey();
            tableMap.setRow(tabname);

            // Update:
            pstmt = (PreparedStatement) pstmtUpdateCache.get(tabname);
            try { if (pstmt != null) pstmt.close(); } catch (Exception e) {}

            // Insert:
            pstmt = (PreparedStatement) pstmtInsertCache.get(tabname);
            try { if (pstmt != null) pstmt.close(); } catch (Exception e) {}

            // Delete:
            pstmt = (PreparedStatement) pstmtDeleteCache.get(tabname);
            try { if (pstmt != null) pstmt.close(); } catch (Exception e) {}
        }
        getLogger().debug("TableColumnsCache::destroyPsmt: END " );
    }

    protected void finalize() {
        try {
            destroyPsmt();
        } catch (Exception e) {}
    }

}
