Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decompression of blobs #4979

Open
KOLANICH opened this issue Jan 11, 2019 · 8 comments
Open

Decompression of blobs #4979

KOLANICH opened this issue Jan 11, 2019 · 8 comments

Comments

@KOLANICH
Copy link

@KOLANICH KOLANICH commented Jan 11, 2019

Sometimes it is needed to store compressed data in the DB. Unfortunately not all the DBs have built-in compression and FUSE compressed FSes are not available for every OS. So it may make sense to store compressed binary blobs in the DB.

Unfortunately when one sees them in DBeaver he sees them compressed, but often they are needed uncompressed. So it'd be nice to have a feature to decompress the contents of certain columns.

This may be done by injecting an external function into SQL doing decompression.

@serge-rider
Copy link
Member

@serge-rider serge-rider commented Jan 11, 2019

Sound too specific. There are dozens of different compression algorithms moreover it is not clear how particular data types should be compressed (e.g. numbers, timestamps) - in some binary format, plain text or somehow else.

@KOLANICH
Copy link
Author

@KOLANICH KOLANICH commented Jan 11, 2019

There are dozens of different compression algorithms moreover

The most popular are: deflate, lzma, lzma2, brotli, zstd. Data stream type can be either determined by header, or can be provided (some compression libs allow to put raw data without any container headers).

moreover it is not clear how particular data types should be compressed

Compression algorythms usually work on binary data. So the transformation proposed is blob (compressed) <-> blob (decompressed). For other types they should be (de)serialized from/to a binary form first, this is out of scope of this feature IMHO.

@serge-rider
Copy link
Member

@serge-rider serge-rider commented Jan 11, 2019

I think this can be easily (relatively) implemented as a custom value transformer (View As context menu).

@flostadler
Copy link

@flostadler flostadler commented Jul 7, 2019

I'd like to tackle this issue if it is still relevant.

@sachinmukherjee
Copy link

@sachinmukherjee sachinmukherjee commented Sep 12, 2020

Is this issue still open?

@alex-chee
Copy link

@alex-chee alex-chee commented Mar 15, 2021

I cannot decompressing navision 2009R2, somebody can help?

BLOB fields starts with magic number 0x01447C5A
if throw a way 0x01447C5A results still not decompressed or error because unsupported compression method

in nav2009R2 my fields in HEX:
01447C5A000A386D40C0B870B879A973C406F7B061B0

it is simple txt file - with text:
testtest20212021

compressed string look like:
�D|Z
8m@A?p?y©sA�?°a°

i try to decompress string with code (can see below) but unsuccefuly because error... (System.IO.InvalidDataException: "The archive entry was compressed using an unsupported compression method.")

can somebody help to decompress NAV2009R2 blob?

mycode:
Private Sub Decompress(ByVal fi As FileInfo)
Using inFile As FileStream = fi.OpenRead()
Dim curFile As String = fi.FullName
Dim origName = curFile.Remove(curFile.Length - fi.Extension.Length)
Using outFile As FileStream = File.Create(origName)
Using Decompress As DeflateStream = New DeflateStream(inFile, CompressionMode.Decompress)
Decompress.CopyTo(outFile)
Console.WriteLine("Decompressed: {0}", fi.Name)
End Using
End Using
End Using
End Sub

@lambdaupb
Copy link

@lambdaupb lambdaupb commented Jun 3, 2021

I hacked a working AttributeTransformer, patch attached.

But looking at both the AttributeTransformer as well as as the BinaryFormatters, both are a little cumbersome for this task.

What would help is de-coupling the decompression from the resulting formatting of the contained data, like a transparent layer (wireshark has this for buffers).

A good alternative would be to provide decompression functions inside the experssion language and then the user can define a virtual column with nested decoding. like charset_decode(decompress(value, 'bzip2'), 'latin1').

I wonder how the image decoding is done and how that would integrate.

Index: plugins/org.jkiss.dbeaver.model/plugin.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/plugins/org.jkiss.dbeaver.model/plugin.xml b/plugins/org.jkiss.dbeaver.model/plugin.xml
--- a/plugins/org.jkiss.dbeaver.model/plugin.xml	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/plugins/org.jkiss.dbeaver.model/plugin.xml	(date 1622723453702)
@@ -229,6 +229,19 @@
             </propertyGroup>
         </transformer>
 
+        <transformer
+                class="org.jkiss.dbeaver.model.impl.data.transformers.AutoDecompressAttributeTransformer"
+                id="org.jkiss.dbeaver.core.AutoDecompressAttributeTransformer"
+                name="AUTO_DECOMPRESS"
+                description="%AutoDecompressTransformer.general.description"
+                applyByDefault="false"
+                custom="true">
+            <type kind="BINARY"/>
+            <type kind="BINARY"/>
+            <propertyGroup label="Properties">
+                <property id="algorithm" label="%AutoDecompressTransformer.property.algorithm.label" type="string" description="%AutoDecompressTransformer.property.algorithm.description" defaultValue="auto" required="true" validValues="auto,snappy-raw,lz4-block,deflate64"/>
+                <property id="max_size" label="%AutoDecompressTransformer.property.max_size.label" type="string" description="%AutoDecompressTransformer.property.max_size.description" defaultValue="10485760" required="true" validValues="1024,102400,1048576,10485760,104857600"/>
+            </propertyGroup>
+        </transformer>
     </extension>
-
 </plugin>
Index: features/org.jkiss.dbeaver.runtime.feature/feature.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/features/org.jkiss.dbeaver.runtime.feature/feature.xml b/features/org.jkiss.dbeaver.runtime.feature/feature.xml
--- a/features/org.jkiss.dbeaver.runtime.feature/feature.xml	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/features/org.jkiss.dbeaver.runtime.feature/feature.xml	(date 1622660780512)
@@ -45,5 +45,8 @@
     <plugin id="org.apache.commons.jexl" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
     <plugin id="net.sf.opencsv" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
     <plugin id="com.google.gson" download-size="0" install-size="0" version="0.0.0"/>
-
+    <plugin id="org.apache.commons.compress" download-size="0" install-size="0" version="0.0.0"/>
+    <plugin id="org.tukaani.xz" download-size="0" install-size="0" version="0.0.0"/>
+    <!--<plugin id="org.brotli.dec" download-size="0" install-size="0" version="0.0.0"/>
+    <plugin id="com.github.luben.zstd" download-size="0" install-size="0" version="0.0.0"/>-->
 </feature>
Index: features/org.jkiss.dbeaver.rcp.feature/feature.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/features/org.jkiss.dbeaver.rcp.feature/feature.xml b/features/org.jkiss.dbeaver.rcp.feature/feature.xml
--- a/features/org.jkiss.dbeaver.rcp.feature/feature.xml	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/features/org.jkiss.dbeaver.rcp.feature/feature.xml	(date 1622660784204)
@@ -135,7 +135,10 @@
     <plugin id="com.jcraft.jsch" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
     <plugin id="org.apache.commons.logging" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
     <plugin id="org.objectweb.asm" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
-
+    <plugin id="org.apache.commons.compress" download-size="0" install-size="0" version="0.0.0"/>
+    <plugin id="org.tukaani.xz" download-size="0" install-size="0" version="0.0.0"/>
+    <!--<plugin id="org.brotli.dec" download-size="0" install-size="0" version="0.0.0"/>
+    <plugin id="com.github.luben.zstd" download-size="0" install-size="0" version="0.0.0"/>-->
     <!-- OS dependent -->
 
 <!--
Index: features/org.jkiss.dbeaver.standalone.feature/feature.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/features/org.jkiss.dbeaver.standalone.feature/feature.xml b/features/org.jkiss.dbeaver.standalone.feature/feature.xml
--- a/features/org.jkiss.dbeaver.standalone.feature/feature.xml	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/features/org.jkiss.dbeaver.standalone.feature/feature.xml	(date 1622660780508)
@@ -26,6 +26,10 @@
         <import plugin="org.apache.commons.logging" match="greaterOrEqual"/>
         <import plugin="com.jcraft.jsch" match="greaterOrEqual"/>
         <import plugin="org.objectweb.asm" match="greaterOrEqual"/>
+        <import plugin="org.apache.commons.compress" match="greaterOrEqual"/>
+        <!--<import plugin="org.brotli.dec" match="greaterOrEqual"/>
+        <import plugin="com.github.luben.zstd" match="greaterOrEqual"/>-->
+        <import plugin="org.tukaani.xz" match="greaterOrEqual"/>
     </requires>
 
     <includes id="org.jkiss.dbeaver.rcp.feature" version="0.0.0"/>
Index: features/org.jkiss.dbeaver.appstore.feature/feature.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/features/org.jkiss.dbeaver.appstore.feature/feature.xml b/features/org.jkiss.dbeaver.appstore.feature/feature.xml
--- a/features/org.jkiss.dbeaver.appstore.feature/feature.xml	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/features/org.jkiss.dbeaver.appstore.feature/feature.xml	(date 1622660788988)
@@ -26,6 +26,10 @@
         <import plugin="org.apache.commons.logging" match="greaterOrEqual"/>
         <import plugin="com.jcraft.jsch" match="greaterOrEqual"/>
         <import plugin="org.objectweb.asm" match="greaterOrEqual"/>
+        <import plugin="org.apache.commons.compress" match="greaterOrEqual"/>
+        <!--<import plugin="org.brotli.dec" match="greaterOrEqual"/>
+        <import plugin="com.github.luben.zstd" match="greaterOrEqual"/> -->
+        <import plugin="org.tukaani.xz" match="greaterOrEqual"/>
     </requires>
 
     <includes id="org.jkiss.dbeaver.rcp.feature" version="0.0.0"/>
Index: plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/data/transformers/AutoDecompressAttributeTransformer.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/data/transformers/AutoDecompressAttributeTransformer.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/data/transformers/AutoDecompressAttributeTransformer.java
new file mode 100644
--- /dev/null	(date 1622723520926)
+++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/data/transformers/AutoDecompressAttributeTransformer.java	(date 1622723520926)
@@ -0,0 +1,92 @@
+package org.jkiss.dbeaver.model.impl.data.transformers;
+
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.CompressorInputStream;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+import org.jkiss.dbeaver.DBException;
+import org.jkiss.dbeaver.model.DBPDataKind;
+import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
+import org.jkiss.dbeaver.model.data.DBDAttributeTransformer;
+import org.jkiss.dbeaver.model.data.DBDDisplayFormat;
+import org.jkiss.dbeaver.model.data.DBDValueHandler;
+import org.jkiss.dbeaver.model.exec.DBCSession;
+import org.jkiss.dbeaver.model.impl.data.ProxyValueHandler;
+import org.jkiss.dbeaver.model.impl.jdbc.data.JDBCContentBytes;
+import org.jkiss.dbeaver.model.struct.DBSTypedObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+public class AutoDecompressAttributeTransformer implements DBDAttributeTransformer {
+    public void transformAttribute(
+            DBCSession session,
+            DBDAttributeBinding attribute,
+            List<Object[]> rows,
+            Map<String, Object> options
+    ) throws DBException {
+
+        attribute.setPresentationAttribute(
+                new TransformerPresentationAttribute(attribute, "COMPRESSED", -1, DBPDataKind.BINARY));
+
+        String algorithm = options.getOrDefault("algorithm", "auto").toString().trim().toLowerCase();
+        int maxSize = Integer.parseInt(options.getOrDefault("max_size", "10485760").toString().trim());
+        attribute.setTransformHandler(new AutoDecompressAttributeTransformer.AutoDecompressHandler(
+                attribute.getValueHandler(),
+                algorithm,
+                maxSize
+        ));
+
+
+    }
+
+    private static class AutoDecompressHandler extends ProxyValueHandler {
+        private static final int MAX_SIZE = 10*1024*1024;
+        private final String algorithm;
+        private final int maxSize;
+
+        public AutoDecompressHandler(DBDValueHandler target, String algorithm, int maxSize) {
+            super(target);
+            this.algorithm = algorithm;
+            this.maxSize = maxSize;
+        }
+
+        @Override
+        public String getValueDisplayString(DBSTypedObject column, Object value, DBDDisplayFormat format) {
+            byte[] bytes = null;
+            if (value instanceof byte[]) {
+                bytes = (byte[]) value;
+            } else if (value instanceof JDBCContentBytes) {
+                bytes = ((JDBCContentBytes) value).getRawValue();
+            }
+            try(InputStream rawStream = new ByteArrayInputStream(bytes)) {
+                try (CompressorInputStream cin = makeStream(rawStream)) {
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    byte[] buf = new byte[32];
+                    int read = 0;
+                    do {
+                        out.write(buf, 0, read);
+                        read = cin.read(buf);
+                    } while(read > -1 && out.size() < this.maxSize);
+                    return new String(out.toByteArray(), StandardCharsets.UTF_8);
+                }
+            } catch (Exception e) {
+                return "UNABLE TO DECOMPRESS: "+e.getMessage()+":::"+new String(bytes);
+            }
+        }
+
+        private CompressorInputStream makeStream(InputStream plain) throws CompressorException {
+            CompressorStreamFactory fac = new CompressorStreamFactory();
+
+            if(fac.getCompressorInputStreamProviders().containsKey(this.algorithm)) {
+                return fac.createCompressorInputStream(this.algorithm, plain, true);
+            } else {
+                return fac.createCompressorInputStream(CompressorStreamFactory.detect(plain), plain, true);
+            }
+        }
+    }
+}
Index: plugins/org.jkiss.dbeaver.model/META-INF/MANIFEST.MF
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/plugins/org.jkiss.dbeaver.model/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.model/META-INF/MANIFEST.MF
--- a/plugins/org.jkiss.dbeaver.model/META-INF/MANIFEST.MF	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/plugins/org.jkiss.dbeaver.model/META-INF/MANIFEST.MF	(date 1622660800127)
@@ -94,6 +94,8 @@
  org.jkiss.utils;visibility:=reexport,
  com.google.gson,
  org.bouncycastle.bcpkix;resolution:=optional,
- org.bouncycastle.bcprov;resolution:=optional
+ org.bouncycastle.bcprov;resolution:=optional,
+ org.apache.commons.compress,
+ org.tukaani.xz
 Bundle-Localization: OSGI-INF/l10n/bundle
 Automatic-Module-Name: org.jkiss.dbeaver.model
Index: plugins/org.jkiss.dbeaver.model/OSGI-INF/l10n/bundle.properties
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/plugins/org.jkiss.dbeaver.model/OSGI-INF/l10n/bundle.properties b/plugins/org.jkiss.dbeaver.model/OSGI-INF/l10n/bundle.properties
--- a/plugins/org.jkiss.dbeaver.model/OSGI-INF/l10n/bundle.properties	(revision 83031d74725599b49015b08877f76d10ab0e0625)
+++ b/plugins/org.jkiss.dbeaver.model/OSGI-INF/l10n/bundle.properties	(date 1622723403959)
@@ -172,4 +172,11 @@
 BinaryTransformer.property.format.label = Binary format
 BinaryTransformer.property.format.description = Binary value format.
 BinaryTransformer.property.encoding.label = Character encoding
-BinaryTransformer.property.encoding.description = Character encoding used to decode string value into bytes.
\ No newline at end of file
+BinaryTransformer.property.encoding.description = Character encoding used to decode string value into bytes.
+
+
+AutoDecompressTransformer.general.description = Decompress binary data by detecting header
+AutoDecompressTransformer.property.algorithm.label = Algorithm
+AutoDecompressTransformer.property.algorithm.description = Compression algorithm
+AutoDecompressTransformer.property.max_size.label = Max Size
+AutoDecompressTransformer.property.max_size.description = De-Compress up to N bytes before stopping
\ No newline at end of file

@KOLANICH
Copy link
Author

@KOLANICH KOLANICH commented Jun 3, 2021

Thank you, @lambdaupb

@dbeaver dbeaver deleted a comment Oct 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
7 participants
@KOLANICH @lambdaupb @flostadler @serge-rider @sachinmukherjee @alex-chee and others