<NME> JDBCContentBLOB.java
<BEF> /*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2022 DBeaver Corp and others
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jkiss.dbeaver.model.impl.jdbc.data;

import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.ModelPreferences;
import org.jkiss.dbeaver.model.DBPDataKind;
import org.jkiss.dbeaver.model.DBValueFormatting;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.data.DBDContentCached;
import org.jkiss.dbeaver.model.data.DBDContentStorage;
import org.jkiss.dbeaver.model.data.DBDDisplayFormat;
import org.jkiss.dbeaver.model.data.storage.BytesContentStorage;
import org.jkiss.dbeaver.model.data.storage.TemporaryContentStorage;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
import org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement;
import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.MimeTypes;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Blob;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;

/**
 * JDBCContentBLOB
 *
 * @author Serge Rider
 */
public class JDBCContentBLOB extends JDBCContentLOB {

    private static final Log log = Log.getLog(JDBCContentBLOB.class);

    private Blob blob;
    private InputStream tmpStream;

    public JDBCContentBLOB(DBCExecutionContext dataSource, Blob blob) {
        super(dataSource);
        this.blob = blob;
    }

    @Override
    public long getLOBLength() throws DBCException {
        if (blob != null) {
            try {
                return blob.length();
            } catch (Throwable e) {
                throw new DBCException(e, executionContext);
            }
        }
        return 0;
    }

    @NotNull
    @Override
    public String getContentType()
    {
        return MimeTypes.OCTET_STREAM;
    }

    @Override
    public DBDContentStorage getContents(DBRProgressMonitor monitor)
        throws DBCException
    {
        if (storage == null && blob != null) {
            try {
                long contentLength = getContentLength();
                DBPPlatform platform = DBWorkbench.getPlatform();
                if (contentLength < platform.getPreferenceStore().getInt(ModelPreferences.MEMORY_CONTENT_MAX_SIZE)) {
                    try {
                        try (InputStream bs = blob.getBinaryStream()) {
                            storage = BytesContentStorage.createFromStream(
                                bs,
                                contentLength,
                                getDefaultEncoding());
                        }
                    } catch (IOException e) {
                        throw new DBCException("IO error while reading content", e);
                    } catch (Throwable e) {
                        throw new DBCException(e, executionContext);
                    }
                } else {
                    // Create new local storage
                    Path tempFile;
                    try {
                        tempFile = ContentUtils.createTempContentFile(monitor, platform, "blob" + blob.hashCode());
                    } catch (IOException e) {
                        throw new DBCException("Can't create temporary file", e);
                    }
                    try (OutputStream os = Files.newOutputStream(tempFile)) {
                        try (InputStream bs = blob.getBinaryStream()) {
                            ContentUtils.copyStreams(bs, contentLength, os, monitor);
                        }
                    } catch (IOException e) {
                        ContentUtils.deleteTempFile(tempFile);
                        throw new DBCException("IO error while copying stream", e);
                    } catch (Throwable e) {
                        ContentUtils.deleteTempFile(tempFile);
                        throw new DBCException(e, executionContext);
                    }
                    this.storage = new TemporaryContentStorage(platform, tempFile, getDefaultEncoding(), true);
                }
            } catch (DBCException e) {
                handleContentReadingException(e);
            }
            // Free blob - we don't need it anymore
            releaseBlob();
        }
        return storage;
    }

    @Override
    public void release()
    {
        releaseTempStream();
        releaseBlob();
        super.release();
    }

    private void releaseBlob() {
        if (blob != null) {
            try {
                blob.free();
            } catch (Throwable e) {
                // Log as warning only if it is an exception.
                // Errors just spam log
                log.debug("Error freeing BLOB: " + e.getClass().getName() + ": " + e.getMessage());
            }
            blob = null;
        }
    }

    private void releaseTempStream() {
        if (tmpStream != null) {
            ContentUtils.close(tmpStream);
            tmpStream = null;
        }
    }

    @Override
    public void bindParameter(JDBCSession session, JDBCPreparedStatement preparedStatement, DBSTypedObject columnType, int paramIndex)
        throws DBCException
    {
        try {
            if (storage != null) {
                // Write new blob value
                releaseTempStream();
                tmpStream = storage.getContentStream();
                try {
                    preparedStatement.setBinaryStream(paramIndex, tmpStream);
                }
                catch (Throwable e) {
                    try {
                        if (e instanceof SQLException) {
                            throw (SQLException)e;
                        } else {
                            try {
                                preparedStatement.setBinaryStream(paramIndex, tmpStream, storage.getContentLength());
                            }
                            catch (Throwable e1) {
                                if (e1 instanceof SQLException) {
                                    throw (SQLException)e1;
                                } else {
                        // Let's try bytes
                        int contentLength = (int) storage.getContentLength();
                        ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);
                        IOUtils.copyStream(tmpStream, buffer);
                        preparedStatement.setBytes(paramIndex, buffer.toByteArray());
                    }
                }
                        int contentLength = (int) storage.getContentLength();
                        ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);
                        ContentUtils.copyStreams(tmpStream, contentLength, buffer, session.getProgressMonitor());
                        preparedStatement.setBytes(paramIndex, buffer.toByteArray());
                    }
                }
            } else if (blob != null) {
                try {
                    if (columnType.getDataKind() == DBPDataKind.BINARY) {
                        preparedStatement.setBinaryStream(paramIndex, blob.getBinaryStream());
                    } else {
                        preparedStatement.setBlob(paramIndex, blob);
                    }
                }
                catch (Throwable e0) {
                    // Write new blob value
                    releaseTempStream();
                    tmpStream = blob.getBinaryStream();
                    try {
                        preparedStatement.setBinaryStream(paramIndex, tmpStream);
                    }
                    catch (Throwable e) {
                        if (e instanceof SQLException) {
                            throw (SQLException)e;
                        } else {
                            try {
                                preparedStatement.setBinaryStream(paramIndex, tmpStream, blob.length());
                            }
                            catch (Throwable e1) {
                                if (e1 instanceof SQLException) {
                                    throw (SQLException)e1;
                                } else {
                                    preparedStatement.setBinaryStream(paramIndex, tmpStream, (int)blob.length());
                                }
                            }
                        }
                    }
                }
            } else {
                preparedStatement.setNull(paramIndex, java.sql.Types.BLOB);
            }
        }
        catch (SQLException e) {
            throw new DBCException(e, session.getExecutionContext());
        }
        catch (Throwable e) {
            throw new DBCException("Error while reading content", e);
        }
    }

    @Override
    public Object getRawValue() {
        return blob;
    }

    @Override
    public boolean isNull()
    {
        return blob == null && storage == null;
    }

    @Override
    protected JDBCContentLOB createNewContent()
    {
        return new JDBCContentBLOB(executionContext, null);
    }

    @Override
    public String getDisplayString(DBDDisplayFormat format)
    {
        if (blob == null && storage == null) {
            return null;
        }
        if (storage != null && storage instanceof DBDContentCached) {
            final Object cachedValue = ((DBDContentCached) storage).getCachedValue();
            if (cachedValue instanceof byte[]) {
                return DBValueFormatting.formatBinaryString(executionContext.getDataSource(), (byte[]) cachedValue, format);
            }
        }
        return "[BLOB]";
    }

}

<MSG> #99 Binary value binding fix


Former-commit-id: ac57ab4a1af9bb9f10b718b39dfd0a2cdf192484
<DFF> @@ -189,7 +189,7 @@ public class JDBCContentBLOB extends JDBCContentLOB {
                         // Let's try bytes
                         int contentLength = (int) storage.getContentLength();
                         ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);
-                        IOUtils.copyStream(tmpStream, buffer);
+                        ContentUtils.copyStreams(tmpStream, contentLength, buffer, session.getProgressMonitor());
                         preparedStatement.setBytes(paramIndex, buffer.toByteArray());
                     }
                 }

{'commit': "diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/jdbc/data/JDBCContentBLOB.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/jdbc/data/JDBCContentBLOB.java\nindex ac96c17c09..76a717e41b 100644\n--- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/jdbc/data/JDBCContentBLOB.java\n+++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/jdbc/data/JDBCContentBLOB.java\n@@ -189,7 +189,7 @@ public class JDBCContentBLOB extends JDBCContentLOB {\n                         // Let's try bytes\r\n                         int contentLength = (int) storage.getContentLength();\r\n                         ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);\r\n-                        IOUtils.copyStream(tmpStream, buffer);\r\n+                        ContentUtils.copyStreams(tmpStream, contentLength, buffer, session.getProgressMonitor());\r\n                         preparedStatement.setBytes(paramIndex, buffer.toByteArray());\r\n                     }\r\n                 }\r\n", 'commit_subject': '#99 Binary value binding fix', 'repo_name': 'dbeaver/dbeaver', 'committer': {'name': 'serge-rider', 'email': '94ca489880cc8afd6eab24060045e55aa178d19d@gmail.com', 'time_sec': '1449744588', 'tz_offset': '180', 'date': {'seconds': '1449744588'}}, 'commit_tree': '555689aa68164740675882a7c0d8018afe7957d9', 'commit_message': '#99 Binary value binding fix\n\n\nFormer-commit-id: ac57ab4a1af9bb9f10b718b39dfd0a2cdf192484', 'language_name': 'Java', 'license': 'apache-2.0', 'hunks': "@@ -189,7 +189,7 @@ public class JDBCContentBLOB extends JDBCContentLOB {\n                         // Let's try bytes\r\n                         int contentLength = (int) storage.getContentLength();\r\n                         ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);\r\n-                        IOUtils.copyStream(tmpStream, buffer);\r\n+                        ContentUtils.copyStreams(tmpStream, contentLength, buffer, session.getProgressMonitor());\r\n                         preparedStatement.setBytes(paramIndex, buffer.toByteArray());\r\n                     }\r\n                 }\r\n", 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/jdbc/data/JDBCContentBLOB.java', 'tgt_file': 'b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/jdbc/data/JDBCContentBLOB.java', 'file_extension': '.java', 'before_file': '/*\n * DBeaver - Universal Database Manager\n * Copyright (C) 2010-2022 DBeaver Corp and others\n *\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jkiss.dbeaver.model.impl.jdbc.data;\n\nimport org.jkiss.code.NotNull;\nimport org.jkiss.dbeaver.Log;\nimport org.jkiss.dbeaver.ModelPreferences;\nimport org.jkiss.dbeaver.model.DBPDataKind;\nimport org.jkiss.dbeaver.model.DBValueFormatting;\nimport org.jkiss.dbeaver.model.app.DBPPlatform;\nimport org.jkiss.dbeaver.model.data.DBDContentCached;\nimport org.jkiss.dbeaver.model.data.DBDContentStorage;\nimport org.jkiss.dbeaver.model.data.DBDDisplayFormat;\nimport org.jkiss.dbeaver.model.data.storage.BytesContentStorage;\nimport org.jkiss.dbeaver.model.data.storage.TemporaryContentStorage;\nimport org.jkiss.dbeaver.model.exec.DBCException;\nimport org.jkiss.dbeaver.model.exec.DBCExecutionContext;\nimport org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement;\nimport org.jkiss.dbeaver.model.exec.jdbc.JDBCSession;\nimport org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;\nimport org.jkiss.dbeaver.model.struct.DBSTypedObject;\nimport org.jkiss.dbeaver.runtime.DBWorkbench;\nimport org.jkiss.dbeaver.utils.ContentUtils;\nimport org.jkiss.dbeaver.utils.MimeTypes;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.sql.Blob;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\n\n/**\n * JDBCContentBLOB\n *\n * @author Serge Rider\n */\npublic class JDBCContentBLOB extends JDBCContentLOB {\n\n    private static final Log log = Log.getLog(JDBCContentBLOB.class);\n\n    private Blob blob;\n    private InputStream tmpStream;\n\n    public JDBCContentBLOB(DBCExecutionContext dataSource, Blob blob) {\n        super(dataSource);\n        this.blob = blob;\n    }\n\n    @Override\n    public long getLOBLength() throws DBCException {\n        if (blob != null) {\n            try {\n                return blob.length();\n            } catch (Throwable e) {\n                throw new DBCException(e, executionContext);\n            }\n        }\n        return 0;\n    }\n\n    @NotNull\n    @Override\n    public String getContentType()\n    {\n        return MimeTypes.OCTET_STREAM;\n    }\n\n    @Override\n    public DBDContentStorage getContents(DBRProgressMonitor monitor)\n        throws DBCException\n    {\n        if (storage == null && blob != null) {\n            try {\n                long contentLength = getContentLength();\n                DBPPlatform platform = DBWorkbench.getPlatform();\n                if (contentLength < platform.getPreferenceStore().getInt(ModelPreferences.MEMORY_CONTENT_MAX_SIZE)) {\n                    try {\n                        try (InputStream bs = blob.getBinaryStream()) {\n                            storage = BytesContentStorage.createFromStream(\n                                bs,\n                                contentLength,\n                                getDefaultEncoding());\n                        }\n                    } catch (IOException e) {\n                        throw new DBCException("IO error while reading content", e);\n                    } catch (Throwable e) {\n                        throw new DBCException(e, executionContext);\n                    }\n                } else {\n                    // Create new local storage\n                    Path tempFile;\n                    try {\n                        tempFile = ContentUtils.createTempContentFile(monitor, platform, "blob" + blob.hashCode());\n                    } catch (IOException e) {\n                        throw new DBCException("Can\'t create temporary file", e);\n                    }\n                    try (OutputStream os = Files.newOutputStream(tempFile)) {\n                        try (InputStream bs = blob.getBinaryStream()) {\n                            ContentUtils.copyStreams(bs, contentLength, os, monitor);\n                        }\n                    } catch (IOException e) {\n                        ContentUtils.deleteTempFile(tempFile);\n                        throw new DBCException("IO error while copying stream", e);\n                    } catch (Throwable e) {\n                        ContentUtils.deleteTempFile(tempFile);\n                        throw new DBCException(e, executionContext);\n                    }\n                    this.storage = new TemporaryContentStorage(platform, tempFile, getDefaultEncoding(), true);\n                }\n            } catch (DBCException e) {\n                handleContentReadingException(e);\n            }\n            // Free blob - we don\'t need it anymore\n            releaseBlob();\n        }\n        return storage;\n    }\n\n    @Override\n    public void release()\n    {\n        releaseTempStream();\n        releaseBlob();\n        super.release();\n    }\n\n    private void releaseBlob() {\n        if (blob != null) {\n            try {\n                blob.free();\n            } catch (Throwable e) {\n                // Log as warning only if it is an exception.\n                // Errors just spam log\n                log.debug("Error freeing BLOB: " + e.getClass().getName() + ": " + e.getMessage());\n            }\n            blob = null;\n        }\n    }\n\n    private void releaseTempStream() {\n        if (tmpStream != null) {\n            ContentUtils.close(tmpStream);\n            tmpStream = null;\n        }\n    }\n\n    @Override\n    public void bindParameter(JDBCSession session, JDBCPreparedStatement preparedStatement, DBSTypedObject columnType, int paramIndex)\n        throws DBCException\n    {\n        try {\n            if (storage != null) {\n                // Write new blob value\n                releaseTempStream();\n                tmpStream = storage.getContentStream();\n                try {\n                    preparedStatement.setBinaryStream(paramIndex, tmpStream);\n                }\n                catch (Throwable e) {\n                    try {\n                        if (e instanceof SQLException) {\n                            throw (SQLException)e;\n                        } else {\n                            try {\n                                preparedStatement.setBinaryStream(paramIndex, tmpStream, storage.getContentLength());\n                            }\n                            catch (Throwable e1) {\n                                if (e1 instanceof SQLException) {\n                                    throw (SQLException)e1;\n                                } else {\n                        // Let\'s try bytes\r\n                        int contentLength = (int) storage.getContentLength();\r\n                        ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);\r\n                        IOUtils.copyStream(tmpStream, buffer);\r\n                        preparedStatement.setBytes(paramIndex, buffer.toByteArray());\r\n                    }\r\n                }\r\n                        int contentLength = (int) storage.getContentLength();\n                        ByteArrayOutputStream buffer = new ByteArrayOutputStream(contentLength);\n                        ContentUtils.copyStreams(tmpStream, contentLength, buffer, session.getProgressMonitor());\n                        preparedStatement.setBytes(paramIndex, buffer.toByteArray());\n                    }\n                }\n            } else if (blob != null) {\n                try {\n                    if (columnType.getDataKind() == DBPDataKind.BINARY) {\n                        preparedStatement.setBinaryStream(paramIndex, blob.getBinaryStream());\n                    } else {\n                        preparedStatement.setBlob(paramIndex, blob);\n                    }\n                }\n                catch (Throwable e0) {\n                    // Write new blob value\n                    releaseTempStream();\n                    tmpStream = blob.getBinaryStream();\n                    try {\n                        preparedStatement.setBinaryStream(paramIndex, tmpStream);\n                    }\n                    catch (Throwable e) {\n                        if (e instanceof SQLException) {\n                            throw (SQLException)e;\n                        } else {\n                            try {\n                                preparedStatement.setBinaryStream(paramIndex, tmpStream, blob.length());\n                            }\n                            catch (Throwable e1) {\n                                if (e1 instanceof SQLException) {\n                                    throw (SQLException)e1;\n                                } else {\n                                    preparedStatement.setBinaryStream(paramIndex, tmpStream, (int)blob.length());\n                                }\n                            }\n                        }\n                    }\n                }\n            } else {\n                preparedStatement.setNull(paramIndex, java.sql.Types.BLOB);\n            }\n        }\n        catch (SQLException e) {\n            throw new DBCException(e, session.getExecutionContext());\n        }\n        catch (Throwable e) {\n            throw new DBCException("Error while reading content", e);\n        }\n    }\n\n    @Override\n    public Object getRawValue() {\n        return blob;\n    }\n\n    @Override\n    public boolean isNull()\n    {\n        return blob == null && storage == null;\n    }\n\n    @Override\n    protected JDBCContentLOB createNewContent()\n    {\n        return new JDBCContentBLOB(executionContext, null);\n    }\n\n    @Override\n    public String getDisplayString(DBDDisplayFormat format)\n    {\n        if (blob == null && storage == null) {\n            return null;\n        }\n        if (storage != null && storage instanceof DBDContentCached) {\n            final Object cachedValue = ((DBDContentCached) storage).getCachedValue();\n            if (cachedValue instanceof byte[]) {\n                return DBValueFormatting.formatBinaryString(executionContext.getDataSource(), (byte[]) cachedValue, format);\n            }\n        }\n        return "[BLOB]";\n    }\n\n}\n', 'message': '#99 Binary value binding fix\n\n\nFormer-commit-id: ac57ab4a1af9bb9f10b718b39dfd0a2cdf192484', 'author_name': 'serge-rider', 'lang': 'jkiss'}