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

builtin: add {de,}compress builtins #67426

Merged
merged 1 commit into from Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/generated/sql/functions.md
Expand Up @@ -2407,6 +2407,8 @@ The swap_ordinate_string parameter is a 2-character string naming the ordinates
</span></td></tr>
<tr><td><a name="chr"></a><code>chr(val: <a href="int.html">int</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Returns the character with the code given in <code>val</code>. Inverse function of <code>ascii()</code>.</p>
</span></td></tr>
<tr><td><a name="compress"></a><code>compress(data: <a href="bytes.html">bytes</a>, codec: <a href="string.html">string</a>) &rarr; <a href="bytes.html">bytes</a></code></td><td><span class="funcdesc"><p>Compress <code>data</code> with the specified <code>codec</code> (<code>gzip</code>).</p>
</span></td></tr>
<tr><td><a name="concat"></a><code>concat(<a href="string.html">string</a>...) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Concatenates a comma-separated list of strings.</p>
</span></td></tr>
<tr><td><a name="concat_ws"></a><code>concat_ws(<a href="string.html">string</a>...) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Uses the first argument as a separator between the concatenation of the subsequent arguments.</p>
Expand All @@ -2426,6 +2428,8 @@ The output can be used to recreate a database.’</p>
</span></td></tr>
<tr><td><a name="decode"></a><code>decode(text: <a href="string.html">string</a>, format: <a href="string.html">string</a>) &rarr; <a href="bytes.html">bytes</a></code></td><td><span class="funcdesc"><p>Decodes <code>data</code> using <code>format</code> (<code>hex</code> / <code>escape</code> / <code>base64</code>).</p>
</span></td></tr>
<tr><td><a name="decompress"></a><code>decompress(data: <a href="bytes.html">bytes</a>, codec: <a href="string.html">string</a>) &rarr; <a href="bytes.html">bytes</a></code></td><td><span class="funcdesc"><p>Decompress <code>data</code> with the specified <code>codec</code> (<code>gzip</code>).</p>
</span></td></tr>
<tr><td><a name="difference"></a><code>difference(source: <a href="string.html">string</a>, target: <a href="string.html">string</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Convert two strings to their Soundex codes and then reports the number of matching code positions.</p>
</span></td></tr>
<tr><td><a name="encode"></a><code>encode(data: <a href="bytes.html">bytes</a>, format: <a href="string.html">string</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Encodes <code>data</code> using <code>format</code> (<code>hex</code> / <code>escape</code> / <code>base64</code>).</p>
Expand Down
59 changes: 59 additions & 0 deletions pkg/sql/sem/builtins/builtins.go
Expand Up @@ -12,6 +12,7 @@ package builtins

import (
"bytes"
"compress/gzip"
"crypto/md5"
cryptorand "crypto/rand"
"crypto/sha1"
Expand All @@ -22,6 +23,7 @@ import (
"hash"
"hash/crc32"
"hash/fnv"
"io/ioutil"
"math"
"math/rand"
"net"
Expand Down Expand Up @@ -1073,6 +1075,63 @@ var builtins = map[string]builtinDefinition{
},
),

"compress": makeBuiltin(defProps(),
tree.Overload{
Types: tree.ArgTypes{{"data", types.Bytes}, {"codec", types.String}},
ReturnType: tree.FixedReturnType(types.Bytes),
Fn: func(evalCtx *tree.EvalContext, args tree.Datums) (_ tree.Datum, err error) {
uncompressedData := []byte(tree.MustBeDBytes(args[0]))
codec := string(tree.MustBeDString(args[1]))
switch strings.ToUpper(codec) {
case "GZIP":
gzipBuf := bytes.NewBuffer([]byte{})
gz := gzip.NewWriter(gzipBuf)
if _, err := gz.Write(uncompressedData); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return tree.NewDBytes(tree.DBytes(gzipBuf.Bytes())), nil
default:
return nil, pgerror.New(pgcode.InvalidParameterValue,
"only 'gzip' codec is supported for compress()")
}
},
Info: "Compress `data` with the specified `codec` (`gzip`).",
Volatility: tree.VolatilityImmutable,
},
),

"decompress": makeBuiltin(defProps(),
tree.Overload{
Types: tree.ArgTypes{{"data", types.Bytes}, {"codec", types.String}},
ReturnType: tree.FixedReturnType(types.Bytes),
Fn: func(evalCtx *tree.EvalContext, args tree.Datums) (_ tree.Datum, err error) {
compressedData := []byte(tree.MustBeDBytes(args[0]))
codec := string(tree.MustBeDString(args[1]))
switch strings.ToUpper(codec) {
case "GZIP":
r, err := gzip.NewReader(bytes.NewBuffer(compressedData))
if err != nil {
return nil, errors.Wrap(err, "failed to decompress")
}
defer r.Close()
decompressedBytes, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return tree.NewDBytes(tree.DBytes(decompressedBytes)), nil
default:
return nil, pgerror.New(pgcode.InvalidParameterValue,
"only 'gzip' codec is supported for decompress()")
}
},
Info: "Decompress `data` with the specified `codec` (`gzip`).",
Volatility: tree.VolatilityImmutable,
},
),

"ascii": makeBuiltin(tree.FunctionProperties{Category: categoryString},
stringOverload1(
func(_ *tree.EvalContext, s string) (tree.Datum, error) {
Expand Down