PostgreSQLOidDataType.java

package org.dbunit.ext.postgresql;

import org.dbunit.dataset.datatype.BytesDataType;
import org.dbunit.dataset.datatype.TypeCastException;
import org.postgresql.PGConnection;
import org.postgresql.largeobject.LargeObject;
import org.postgresql.largeobject.LargeObjectManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.sql.*;

public class PostgreSQLOidDataType
        extends BytesDataType {

    /**
     * Logger for this class
     */
    private static final Logger logger = LoggerFactory.getLogger(PostgreSQLOidDataType.class);

    public PostgreSQLOidDataType() {
        super("OID", Types.BIGINT);
    }

    @Override
    public Object getSqlValue(final int column, final ResultSet resultSet)
            throws SQLException,
            TypeCastException
    {
        if (logger.isDebugEnabled()) {
            logger.debug("getSqlValue(column={}, resultSet={}) - start", new Integer(column), resultSet);
        }

        Statement statement = resultSet.getStatement();
        Connection connection = statement.getConnection();
        boolean autoCommit = connection.getAutoCommit();
        // kinda ugly
        connection.setAutoCommit(false);

        try {
            PGConnection pgConnection = connection.unwrap(PGConnection.class);
            LargeObjectManager lobj = pgConnection.getLargeObjectAPI();

            long oid = resultSet.getLong(column);
            if (oid == 0) {
                logger.debug("'oid' is zero, the data is NULL.");
                return null;
            }
            LargeObject obj = lobj.open(oid, LargeObjectManager.READ);
            // If lobj.open() throws an exception, it means something wrong with the OID / table.
            // So to be accurate, we don't catch this exception but let it propagate.
            // Swallowing the exception silently indeed hides the problem, which is the wrong behavior
//            try {
//                obj = lobj.open(oid, LargeObjectManager.READ);
//            } catch (SQLException ex) {
//                logger.error("Failed to open oid {} for Large Object reading.", oid);
//                logger.error("Exception: {}", ex.getMessage());
//                logger.error("Returning null instead of bailing out");
//                return null;
//            }

            // Read the data
            byte buf[] = new byte[obj.size()];
            obj.read(buf, 0, obj.size());
            // Close the object
            obj.close();

            return buf;
        } finally {
            connection.setAutoCommit(autoCommit);
        }
    }

    @Override
    public void setSqlValue(final Object value, final int column, final PreparedStatement statement)
            throws SQLException,
            TypeCastException
    {
        if (logger.isDebugEnabled())
        {
            logger.debug("setSqlValue(value={}, column={}, statement={}) - start",
                    new Object[] { value, new Integer(column), statement });
        }

        Connection connection = statement.getConnection();
        boolean autoCommit = connection.getAutoCommit();
        // kinda ugly
        connection.setAutoCommit(false);

        try {
            // Get the Large Object Manager to perform operations with
            LargeObjectManager lobj = ((org.postgresql.PGConnection) statement.getConnection()).getLargeObjectAPI();

            // Create a new large object
            long oid = lobj.createLO(LargeObjectManager.READ | LargeObjectManager.WRITE);

            // Open the large object for writing
            LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);

            // Now open the file
            ByteArrayInputStream bis = new ByteArrayInputStream((byte[]) super.typeCast(value));

            // Copy the data from the file to the large object
            byte buf[] = new byte[2048];
            int s = 0;
            while ((s = bis.read(buf, 0, 2048)) > 0) {
                obj.write(buf, 0, s);
            }

            // Close the large object
            obj.close();

            statement.setLong(column, oid);
        } finally {
            connection.setAutoCommit(autoCommit);
        }
    }
}