multi_compress 0.3.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -0
- data/GET_STARTED.md +207 -0
- data/README.md +69 -0
- data/db_core/include/mcdb_format.h +113 -0
- data/db_core/src/mcdb_format.c +594 -0
- data/db_deployment/mysql/Makefile +48 -0
- data/db_deployment/mysql/README.md +61 -0
- data/db_deployment/mysql/bin/common +212 -0
- data/db_deployment/mysql/bin/disable +40 -0
- data/db_deployment/mysql/bin/doctor +64 -0
- data/db_deployment/mysql/bin/enable +61 -0
- data/db_deployment/mysql/bin/install +64 -0
- data/db_deployment/mysql/bin/status +67 -0
- data/db_deployment/mysql/bin/uninstall +47 -0
- data/db_deployment/mysql/bin/upgrade +127 -0
- data/db_deployment/mysql/bin/verify +14 -0
- data/db_deployment/postgres/Makefile +45 -0
- data/db_deployment/postgres/README.md +84 -0
- data/db_deployment/postgres/bin/doctor +61 -0
- data/db_deployment/postgres/bin/enable +138 -0
- data/db_deployment/postgres/bin/install +106 -0
- data/db_deployment/postgres/bin/uninstall +54 -0
- data/db_deployment/postgres/bin/verify +14 -0
- data/docs/database-envelope-v1.md +159 -0
- data/docs/database-envelope-v2.md +147 -0
- data/docs/rfcs/0002-mcdb2-dictionaries.md +75 -0
- data/exe/multi_compress +228 -0
- data/ext/multi_compress/extconf.rb +4 -0
- data/ext/multi_compress/multi_compress.c +62 -0
- data/lib/multi_compress/active_record.rb +61 -0
- data/lib/multi_compress/codec.rb +164 -0
- data/lib/multi_compress/database.rb +312 -0
- data/lib/multi_compress/db_deployment.rb +868 -0
- data/lib/multi_compress/version.rb +1 -1
- data/multi_compress.gemspec +55 -0
- data/mysql_udf/Makefile +96 -0
- data/mysql_udf/README.md +140 -0
- data/mysql_udf/sql/examples.sql +16 -0
- data/mysql_udf/sql/install.sql +39 -0
- data/mysql_udf/sql/uninstall.sql +10 -0
- data/mysql_udf/sql/views.sql +20 -0
- data/mysql_udf/src/multi_compress_mysql.c +472 -0
- data/mysql_udf/src/mysql_udf_abi_57.h +34 -0
- data/mysql_udf/test/mcdb_cli.c +60 -0
- data/mysql_udf/test/run_e2e.sh +414 -0
- data/postgres_extension/Makefile +74 -0
- data/postgres_extension/README.md +160 -0
- data/postgres_extension/multi_compress.control +4 -0
- data/postgres_extension/multi_compress_pg.exports +24 -0
- data/postgres_extension/sql/examples.sql +23 -0
- data/postgres_extension/sql/multi_compress--0.5.0--0.6.0.sql +59 -0
- data/postgres_extension/sql/multi_compress--0.5.0.sql +24 -0
- data/postgres_extension/sql/multi_compress--0.6.0.sql +85 -0
- data/postgres_extension/sql/uninstall.sql +2 -0
- data/postgres_extension/sql/views.sql +7 -0
- data/postgres_extension/src/mcdb_format.c +2 -0
- data/postgres_extension/src/multi_compress_pg.c +381 -0
- data/postgres_extension/test/run_e2e.sh +411 -0
- metadata +56 -3
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- Install into a dedicated schema.
|
|
2
|
+
CREATE SCHEMA IF NOT EXISTS multi_compress;
|
|
3
|
+
CREATE EXTENSION multi_compress WITH SCHEMA multi_compress;
|
|
4
|
+
|
|
5
|
+
SELECT multi_compress.multi_compress_db_version();
|
|
6
|
+
SELECT multi_compress.multi_compress_db_is_valid(payload_compressed)
|
|
7
|
+
FROM events
|
|
8
|
+
WHERE id = 123;
|
|
9
|
+
|
|
10
|
+
SELECT multi_compress.multi_compress_db_decompress(payload_compressed)
|
|
11
|
+
FROM events
|
|
12
|
+
WHERE id = 123;
|
|
13
|
+
|
|
14
|
+
-- MCDB2: dictionary-backed payloads. The generated view should be preferred:
|
|
15
|
+
-- SELECT id, payload FROM admin.events_readable WHERE id >= 100 ORDER BY id LIMIT 100;
|
|
16
|
+
--
|
|
17
|
+
-- Direct diagnostic shape:
|
|
18
|
+
-- SELECT multi_compress_db_decompress_dict(
|
|
19
|
+
-- e.payload_compressed, e.payload_dictionary_id, d.sha256, d.bytes
|
|
20
|
+
-- )
|
|
21
|
+
-- FROM app.events e
|
|
22
|
+
-- JOIN app.mcdb_dictionary_versions d ON d.id = e.payload_dictionary_id
|
|
23
|
+
-- WHERE e.id = 123;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
\echo Use "ALTER EXTENSION multi_compress UPDATE" to load this file. \quit
|
|
2
|
+
|
|
3
|
+
CREATE FUNCTION multi_compress_db_original_size(blob bytea)
|
|
4
|
+
RETURNS bigint
|
|
5
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_original_size'
|
|
6
|
+
LANGUAGE C
|
|
7
|
+
IMMUTABLE
|
|
8
|
+
STRICT
|
|
9
|
+
PARALLEL SAFE;
|
|
10
|
+
|
|
11
|
+
CREATE FUNCTION multi_compress_db_dictionary_ref(blob bytea)
|
|
12
|
+
RETURNS bigint
|
|
13
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_dictionary_ref'
|
|
14
|
+
LANGUAGE C
|
|
15
|
+
IMMUTABLE
|
|
16
|
+
STRICT
|
|
17
|
+
PARALLEL SAFE;
|
|
18
|
+
|
|
19
|
+
CREATE FUNCTION multi_compress_db_dictionary_zstd_id(dictionary bytea)
|
|
20
|
+
RETURNS bigint
|
|
21
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_dictionary_zstd_id'
|
|
22
|
+
LANGUAGE C
|
|
23
|
+
IMMUTABLE
|
|
24
|
+
STRICT
|
|
25
|
+
PARALLEL SAFE;
|
|
26
|
+
|
|
27
|
+
CREATE FUNCTION multi_compress_db_dictionary_sha256(dictionary bytea)
|
|
28
|
+
RETURNS bytea
|
|
29
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_dictionary_sha256'
|
|
30
|
+
LANGUAGE C
|
|
31
|
+
IMMUTABLE
|
|
32
|
+
STRICT
|
|
33
|
+
PARALLEL SAFE;
|
|
34
|
+
|
|
35
|
+
CREATE FUNCTION multi_compress_db_is_valid_dict(
|
|
36
|
+
blob bytea,
|
|
37
|
+
dictionary_ref bigint,
|
|
38
|
+
dictionary_sha256 bytea,
|
|
39
|
+
dictionary bytea
|
|
40
|
+
)
|
|
41
|
+
RETURNS boolean
|
|
42
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_is_valid_dict'
|
|
43
|
+
LANGUAGE C
|
|
44
|
+
IMMUTABLE
|
|
45
|
+
STRICT
|
|
46
|
+
PARALLEL SAFE;
|
|
47
|
+
|
|
48
|
+
CREATE FUNCTION multi_compress_db_decompress_dict(
|
|
49
|
+
blob bytea,
|
|
50
|
+
dictionary_ref bigint,
|
|
51
|
+
dictionary_sha256 bytea,
|
|
52
|
+
dictionary bytea
|
|
53
|
+
)
|
|
54
|
+
RETURNS text
|
|
55
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_decompress_dict'
|
|
56
|
+
LANGUAGE C
|
|
57
|
+
IMMUTABLE
|
|
58
|
+
STRICT
|
|
59
|
+
PARALLEL SAFE;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
\echo Use "CREATE EXTENSION multi_compress" to load this file. \quit
|
|
2
|
+
|
|
3
|
+
CREATE FUNCTION multi_compress_db_version()
|
|
4
|
+
RETURNS text
|
|
5
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_version'
|
|
6
|
+
LANGUAGE C
|
|
7
|
+
IMMUTABLE
|
|
8
|
+
PARALLEL SAFE;
|
|
9
|
+
|
|
10
|
+
CREATE FUNCTION multi_compress_db_is_valid(blob bytea)
|
|
11
|
+
RETURNS boolean
|
|
12
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_is_valid'
|
|
13
|
+
LANGUAGE C
|
|
14
|
+
IMMUTABLE
|
|
15
|
+
STRICT
|
|
16
|
+
PARALLEL SAFE;
|
|
17
|
+
|
|
18
|
+
CREATE FUNCTION multi_compress_db_decompress(blob bytea)
|
|
19
|
+
RETURNS text
|
|
20
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_decompress'
|
|
21
|
+
LANGUAGE C
|
|
22
|
+
IMMUTABLE
|
|
23
|
+
STRICT
|
|
24
|
+
PARALLEL SAFE;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
\echo Use "CREATE EXTENSION multi_compress" to load this file. \quit
|
|
2
|
+
|
|
3
|
+
CREATE FUNCTION multi_compress_db_version()
|
|
4
|
+
RETURNS text
|
|
5
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_version'
|
|
6
|
+
LANGUAGE C
|
|
7
|
+
IMMUTABLE
|
|
8
|
+
PARALLEL SAFE;
|
|
9
|
+
|
|
10
|
+
-- MCDB1: dictionary-free, one blob argument.
|
|
11
|
+
CREATE FUNCTION multi_compress_db_is_valid(blob bytea)
|
|
12
|
+
RETURNS boolean
|
|
13
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_is_valid'
|
|
14
|
+
LANGUAGE C
|
|
15
|
+
IMMUTABLE
|
|
16
|
+
STRICT
|
|
17
|
+
PARALLEL SAFE;
|
|
18
|
+
|
|
19
|
+
CREATE FUNCTION multi_compress_db_decompress(blob bytea)
|
|
20
|
+
RETURNS text
|
|
21
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_decompress'
|
|
22
|
+
LANGUAGE C
|
|
23
|
+
IMMUTABLE
|
|
24
|
+
STRICT
|
|
25
|
+
PARALLEL SAFE;
|
|
26
|
+
|
|
27
|
+
-- MCDB2 metadata and dictionary registry validation helpers.
|
|
28
|
+
CREATE FUNCTION multi_compress_db_original_size(blob bytea)
|
|
29
|
+
RETURNS bigint
|
|
30
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_original_size'
|
|
31
|
+
LANGUAGE C
|
|
32
|
+
IMMUTABLE
|
|
33
|
+
STRICT
|
|
34
|
+
PARALLEL SAFE;
|
|
35
|
+
|
|
36
|
+
CREATE FUNCTION multi_compress_db_dictionary_ref(blob bytea)
|
|
37
|
+
RETURNS bigint
|
|
38
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_dictionary_ref'
|
|
39
|
+
LANGUAGE C
|
|
40
|
+
IMMUTABLE
|
|
41
|
+
STRICT
|
|
42
|
+
PARALLEL SAFE;
|
|
43
|
+
|
|
44
|
+
CREATE FUNCTION multi_compress_db_dictionary_zstd_id(dictionary bytea)
|
|
45
|
+
RETURNS bigint
|
|
46
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_dictionary_zstd_id'
|
|
47
|
+
LANGUAGE C
|
|
48
|
+
IMMUTABLE
|
|
49
|
+
STRICT
|
|
50
|
+
PARALLEL SAFE;
|
|
51
|
+
|
|
52
|
+
CREATE FUNCTION multi_compress_db_dictionary_sha256(dictionary bytea)
|
|
53
|
+
RETURNS bytea
|
|
54
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_dictionary_sha256'
|
|
55
|
+
LANGUAGE C
|
|
56
|
+
IMMUTABLE
|
|
57
|
+
STRICT
|
|
58
|
+
PARALLEL SAFE;
|
|
59
|
+
|
|
60
|
+
-- MCDB2: blob, immutable application registry id, SHA-256 bytes, dictionary bytes.
|
|
61
|
+
CREATE FUNCTION multi_compress_db_is_valid_dict(
|
|
62
|
+
blob bytea,
|
|
63
|
+
dictionary_ref bigint,
|
|
64
|
+
dictionary_sha256 bytea,
|
|
65
|
+
dictionary bytea
|
|
66
|
+
)
|
|
67
|
+
RETURNS boolean
|
|
68
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_is_valid_dict'
|
|
69
|
+
LANGUAGE C
|
|
70
|
+
IMMUTABLE
|
|
71
|
+
STRICT
|
|
72
|
+
PARALLEL SAFE;
|
|
73
|
+
|
|
74
|
+
CREATE FUNCTION multi_compress_db_decompress_dict(
|
|
75
|
+
blob bytea,
|
|
76
|
+
dictionary_ref bigint,
|
|
77
|
+
dictionary_sha256 bytea,
|
|
78
|
+
dictionary bytea
|
|
79
|
+
)
|
|
80
|
+
RETURNS text
|
|
81
|
+
AS 'MODULE_PATHNAME', 'multi_compress_db_decompress_dict'
|
|
82
|
+
LANGUAGE C
|
|
83
|
+
IMMUTABLE
|
|
84
|
+
STRICT
|
|
85
|
+
PARALLEL SAFE;
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
#include "postgres.h"
|
|
2
|
+
|
|
3
|
+
#include "fmgr.h"
|
|
4
|
+
#include "mb/pg_wchar.h"
|
|
5
|
+
#include "utils/builtins.h"
|
|
6
|
+
#include "utils/memutils.h"
|
|
7
|
+
#include "varatt.h"
|
|
8
|
+
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
|
|
13
|
+
#include <zstd.h>
|
|
14
|
+
|
|
15
|
+
#include "mcdb_format.h"
|
|
16
|
+
|
|
17
|
+
PG_MODULE_MAGIC;
|
|
18
|
+
|
|
19
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_version);
|
|
20
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_is_valid);
|
|
21
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_decompress);
|
|
22
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_original_size);
|
|
23
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_dictionary_ref);
|
|
24
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_dictionary_zstd_id);
|
|
25
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_dictionary_sha256);
|
|
26
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_is_valid_dict);
|
|
27
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_decompress_dict);
|
|
28
|
+
|
|
29
|
+
#define MCDB_PG_DDICT_CACHE_ENTRIES 4
|
|
30
|
+
|
|
31
|
+
typedef struct {
|
|
32
|
+
uint64_t dictionary_ref;
|
|
33
|
+
unsigned char sha256[MCDB_SHA256_BYTES];
|
|
34
|
+
unsigned char *bytes;
|
|
35
|
+
size_t bytes_len;
|
|
36
|
+
ZSTD_DDict *ddict;
|
|
37
|
+
uint64_t used_at;
|
|
38
|
+
} mcdb_pg_cache_entry;
|
|
39
|
+
|
|
40
|
+
typedef struct {
|
|
41
|
+
ZSTD_DCtx *dctx;
|
|
42
|
+
uint64_t clock;
|
|
43
|
+
mcdb_pg_cache_entry entries[MCDB_PG_DDICT_CACHE_ENTRIES];
|
|
44
|
+
} mcdb_pg_dictionary_cache;
|
|
45
|
+
|
|
46
|
+
static void mcdb_require_utf8_database(void) {
|
|
47
|
+
if (GetDatabaseEncoding() != PG_UTF8)
|
|
48
|
+
ereport(
|
|
49
|
+
ERROR,
|
|
50
|
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
51
|
+
errmsg("multi_compress requires a UTF-8 PostgreSQL database"),
|
|
52
|
+
errdetail("MCDB stores UTF-8 text and multi_compress_db_decompress returns text.")));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static void mcdb_raise(mcdb_status status, const char *detail) {
|
|
56
|
+
if (status == MCDB_ERR_ALLOC)
|
|
57
|
+
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY),
|
|
58
|
+
errmsg("multi_compress: unable to allocate MCDB output")));
|
|
59
|
+
|
|
60
|
+
ereport(ERROR,
|
|
61
|
+
(errcode(ERRCODE_DATA_CORRUPTED), errmsg("multi_compress: invalid MCDB envelope"),
|
|
62
|
+
errdetail("%s", detail && detail[0] ? detail : mcdb_status_str(status))));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static void mcdb_raise_dictionary(const char *detail) {
|
|
66
|
+
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
|
|
67
|
+
errmsg("multi_compress: invalid MCDB2 dictionary"), errdetail("%s", detail)));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static void mcdb_pg_cache_release(void *arg) {
|
|
71
|
+
mcdb_pg_dictionary_cache *cache = (mcdb_pg_dictionary_cache *)arg;
|
|
72
|
+
int i;
|
|
73
|
+
|
|
74
|
+
if (cache == NULL)
|
|
75
|
+
return;
|
|
76
|
+
for (i = 0; i < MCDB_PG_DDICT_CACHE_ENTRIES; i++) {
|
|
77
|
+
if (cache->entries[i].ddict)
|
|
78
|
+
ZSTD_freeDDict(cache->entries[i].ddict);
|
|
79
|
+
}
|
|
80
|
+
if (cache->dctx)
|
|
81
|
+
ZSTD_freeDCtx(cache->dctx);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static mcdb_pg_dictionary_cache *mcdb_pg_cache(FunctionCallInfo fcinfo) {
|
|
85
|
+
FmgrInfo *flinfo = fcinfo->flinfo;
|
|
86
|
+
mcdb_pg_dictionary_cache *cache;
|
|
87
|
+
MemoryContext oldcontext;
|
|
88
|
+
|
|
89
|
+
if (flinfo->fn_extra)
|
|
90
|
+
return (mcdb_pg_dictionary_cache *)flinfo->fn_extra;
|
|
91
|
+
|
|
92
|
+
oldcontext = MemoryContextSwitchTo(flinfo->fn_mcxt);
|
|
93
|
+
cache = (mcdb_pg_dictionary_cache *)palloc0(sizeof(*cache));
|
|
94
|
+
cache->dctx = ZSTD_createDCtx();
|
|
95
|
+
if (!cache->dctx) {
|
|
96
|
+
MemoryContextSwitchTo(oldcontext);
|
|
97
|
+
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY),
|
|
98
|
+
errmsg("multi_compress: unable to allocate zstd context")));
|
|
99
|
+
}
|
|
100
|
+
{
|
|
101
|
+
MemoryContextCallback *callback = (MemoryContextCallback *)palloc0(sizeof(*callback));
|
|
102
|
+
callback->func = mcdb_pg_cache_release;
|
|
103
|
+
callback->arg = cache;
|
|
104
|
+
MemoryContextRegisterResetCallback(flinfo->fn_mcxt, callback);
|
|
105
|
+
}
|
|
106
|
+
flinfo->fn_extra = cache;
|
|
107
|
+
MemoryContextSwitchTo(oldcontext);
|
|
108
|
+
return cache;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static int mcdb_pg_cache_victim(mcdb_pg_dictionary_cache *cache, int same_key) {
|
|
112
|
+
int i;
|
|
113
|
+
int victim = 0;
|
|
114
|
+
uint64_t oldest = UINT64_MAX;
|
|
115
|
+
|
|
116
|
+
if (same_key >= 0)
|
|
117
|
+
return same_key;
|
|
118
|
+
for (i = 0; i < MCDB_PG_DDICT_CACHE_ENTRIES; i++) {
|
|
119
|
+
if (!cache->entries[i].ddict)
|
|
120
|
+
return i;
|
|
121
|
+
if (cache->entries[i].used_at < oldest) {
|
|
122
|
+
oldest = cache->entries[i].used_at;
|
|
123
|
+
victim = i;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return victim;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static ZSTD_DDict *mcdb_pg_cached_ddict(FunctionCallInfo fcinfo, int64 dictionary_ref,
|
|
130
|
+
const unsigned char *dictionary_sha256,
|
|
131
|
+
const unsigned char *dictionary, size_t dictionary_len,
|
|
132
|
+
char *errbuf, mcdb_status *out_status) {
|
|
133
|
+
mcdb_pg_dictionary_cache *cache = mcdb_pg_cache(fcinfo);
|
|
134
|
+
unsigned char actual_sha256[MCDB_SHA256_BYTES];
|
|
135
|
+
int i;
|
|
136
|
+
int same_key = -1;
|
|
137
|
+
int victim;
|
|
138
|
+
ZSTD_DDict *ddict;
|
|
139
|
+
MemoryContext oldcontext;
|
|
140
|
+
|
|
141
|
+
*out_status = MCDB_OK;
|
|
142
|
+
for (i = 0; i < MCDB_PG_DDICT_CACHE_ENTRIES; i++) {
|
|
143
|
+
mcdb_pg_cache_entry *entry = &cache->entries[i];
|
|
144
|
+
if (!entry->ddict || entry->dictionary_ref != (uint64_t)dictionary_ref ||
|
|
145
|
+
memcmp(entry->sha256, dictionary_sha256, MCDB_SHA256_BYTES) != 0)
|
|
146
|
+
continue;
|
|
147
|
+
same_key = i;
|
|
148
|
+
if (entry->bytes_len == dictionary_len &&
|
|
149
|
+
memcmp(entry->bytes, dictionary, dictionary_len) == 0) {
|
|
150
|
+
entry->used_at = ++cache->clock;
|
|
151
|
+
return entry->ddict;
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
mcdb_sha256(dictionary, dictionary_len, actual_sha256);
|
|
157
|
+
if (memcmp(actual_sha256, dictionary_sha256, MCDB_SHA256_BYTES) != 0) {
|
|
158
|
+
snprintf(errbuf, MCDB_ERRLEN, "dictionary sha256 does not match dictionary bytes");
|
|
159
|
+
*out_status = MCDB_ERR_DICTIONARY_ID;
|
|
160
|
+
return NULL;
|
|
161
|
+
}
|
|
162
|
+
*out_status = mcdb_validate_dictionary(dictionary, dictionary_len, NULL);
|
|
163
|
+
if (*out_status != MCDB_OK) {
|
|
164
|
+
snprintf(errbuf, MCDB_ERRLEN,
|
|
165
|
+
"dictionary is not a conformant zstd dictionary with a non-zero DictID");
|
|
166
|
+
return NULL;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ddict = ZSTD_createDDict(dictionary, dictionary_len);
|
|
170
|
+
if (!ddict) {
|
|
171
|
+
*out_status = MCDB_ERR_ALLOC;
|
|
172
|
+
return NULL;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
victim = mcdb_pg_cache_victim(cache, same_key);
|
|
176
|
+
if (cache->entries[victim].ddict)
|
|
177
|
+
ZSTD_freeDDict(cache->entries[victim].ddict);
|
|
178
|
+
if (cache->entries[victim].bytes)
|
|
179
|
+
pfree(cache->entries[victim].bytes);
|
|
180
|
+
|
|
181
|
+
oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
|
|
182
|
+
cache->entries[victim].bytes = (unsigned char *)palloc(dictionary_len);
|
|
183
|
+
memcpy(cache->entries[victim].bytes, dictionary, dictionary_len);
|
|
184
|
+
MemoryContextSwitchTo(oldcontext);
|
|
185
|
+
cache->entries[victim].dictionary_ref = (uint64_t)dictionary_ref;
|
|
186
|
+
memcpy(cache->entries[victim].sha256, dictionary_sha256, MCDB_SHA256_BYTES);
|
|
187
|
+
cache->entries[victim].bytes_len = dictionary_len;
|
|
188
|
+
cache->entries[victim].ddict = ddict;
|
|
189
|
+
cache->entries[victim].used_at = ++cache->clock;
|
|
190
|
+
return ddict;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static bytea *mcdb_bytea_sha256(const unsigned char *bytes, size_t bytes_len) {
|
|
194
|
+
bytea *result = (bytea *)palloc(VARHDRSZ + MCDB_SHA256_BYTES);
|
|
195
|
+
mcdb_sha256(bytes, bytes_len, (unsigned char *)VARDATA(result));
|
|
196
|
+
SET_VARSIZE(result, VARHDRSZ + MCDB_SHA256_BYTES);
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
Datum multi_compress_db_version(PG_FUNCTION_ARGS) {
|
|
201
|
+
char version[96];
|
|
202
|
+
(void)fcinfo;
|
|
203
|
+
snprintf(version, sizeof(version), "multi_compress reader %s; MCDB1 + MCDB2; zstd %s",
|
|
204
|
+
MCDB_READER_VERSION, ZSTD_versionString());
|
|
205
|
+
PG_RETURN_TEXT_P(cstring_to_text(version));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Datum multi_compress_db_is_valid(PG_FUNCTION_ARGS) {
|
|
209
|
+
bytea *input;
|
|
210
|
+
char err[MCDB_ERRLEN];
|
|
211
|
+
mcdb_status status;
|
|
212
|
+
if (PG_ARGISNULL(0))
|
|
213
|
+
PG_RETURN_NULL();
|
|
214
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
215
|
+
status = mcdb_decode((const unsigned char *)VARDATA_ANY(input),
|
|
216
|
+
(size_t)VARSIZE_ANY_EXHDR(input), NULL, NULL, err);
|
|
217
|
+
PG_RETURN_BOOL(status == MCDB_OK);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
Datum multi_compress_db_decompress(PG_FUNCTION_ARGS) {
|
|
221
|
+
bytea *input;
|
|
222
|
+
size_t output_len = 0;
|
|
223
|
+
char err[MCDB_ERRLEN];
|
|
224
|
+
mcdb_status status;
|
|
225
|
+
uint64_t original_size = 0;
|
|
226
|
+
text *result;
|
|
227
|
+
if (PG_ARGISNULL(0))
|
|
228
|
+
PG_RETURN_NULL();
|
|
229
|
+
mcdb_require_utf8_database();
|
|
230
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
231
|
+
status = mcdb_validate_header((const unsigned char *)VARDATA_ANY(input),
|
|
232
|
+
(size_t)VARSIZE_ANY_EXHDR(input), &original_size);
|
|
233
|
+
if (status != MCDB_OK)
|
|
234
|
+
mcdb_raise(status, mcdb_status_str(status));
|
|
235
|
+
result = (text *)palloc(VARHDRSZ + (size_t)original_size);
|
|
236
|
+
status = mcdb_decode_into((const unsigned char *)VARDATA_ANY(input),
|
|
237
|
+
(size_t)VARSIZE_ANY_EXHDR(input), (unsigned char *)VARDATA(result),
|
|
238
|
+
(size_t)original_size, &output_len, err);
|
|
239
|
+
if (status != MCDB_OK)
|
|
240
|
+
mcdb_raise(status, err);
|
|
241
|
+
SET_VARSIZE(result, VARHDRSZ + output_len);
|
|
242
|
+
PG_RETURN_TEXT_P(result);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Datum multi_compress_db_original_size(PG_FUNCTION_ARGS) {
|
|
246
|
+
bytea *input;
|
|
247
|
+
mcdb_header header;
|
|
248
|
+
mcdb_status status;
|
|
249
|
+
if (PG_ARGISNULL(0))
|
|
250
|
+
PG_RETURN_NULL();
|
|
251
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
252
|
+
status = mcdb_parse_header((const unsigned char *)VARDATA_ANY(input),
|
|
253
|
+
(size_t)VARSIZE_ANY_EXHDR(input), &header);
|
|
254
|
+
if (status != MCDB_OK)
|
|
255
|
+
mcdb_raise(status, mcdb_status_str(status));
|
|
256
|
+
PG_RETURN_INT64((int64)header.original_size);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
Datum multi_compress_db_dictionary_ref(PG_FUNCTION_ARGS) {
|
|
260
|
+
bytea *input;
|
|
261
|
+
uint64_t ref;
|
|
262
|
+
mcdb_status status;
|
|
263
|
+
if (PG_ARGISNULL(0))
|
|
264
|
+
PG_RETURN_NULL();
|
|
265
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
266
|
+
ref = mcdb_dictionary_ref((const unsigned char *)VARDATA_ANY(input),
|
|
267
|
+
(size_t)VARSIZE_ANY_EXHDR(input), &status);
|
|
268
|
+
if (status != MCDB_OK)
|
|
269
|
+
mcdb_raise(status, mcdb_status_str(status));
|
|
270
|
+
PG_RETURN_INT64((int64)ref);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
Datum multi_compress_db_dictionary_zstd_id(PG_FUNCTION_ARGS) {
|
|
274
|
+
bytea *dictionary;
|
|
275
|
+
uint32_t dict_id;
|
|
276
|
+
mcdb_status status;
|
|
277
|
+
if (PG_ARGISNULL(0))
|
|
278
|
+
PG_RETURN_NULL();
|
|
279
|
+
dictionary = PG_GETARG_BYTEA_PP(0);
|
|
280
|
+
status = mcdb_validate_dictionary((const unsigned char *)VARDATA_ANY(dictionary),
|
|
281
|
+
(size_t)VARSIZE_ANY_EXHDR(dictionary), &dict_id);
|
|
282
|
+
if (status != MCDB_OK)
|
|
283
|
+
mcdb_raise_dictionary(mcdb_status_str(status));
|
|
284
|
+
PG_RETURN_INT64((int64)dict_id);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
Datum multi_compress_db_dictionary_sha256(PG_FUNCTION_ARGS) {
|
|
288
|
+
bytea *dictionary;
|
|
289
|
+
if (PG_ARGISNULL(0))
|
|
290
|
+
PG_RETURN_NULL();
|
|
291
|
+
dictionary = PG_GETARG_BYTEA_PP(0);
|
|
292
|
+
PG_RETURN_BYTEA_P(mcdb_bytea_sha256((const unsigned char *)VARDATA_ANY(dictionary),
|
|
293
|
+
(size_t)VARSIZE_ANY_EXHDR(dictionary)));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
Datum multi_compress_db_is_valid_dict(PG_FUNCTION_ARGS) {
|
|
297
|
+
bytea *input;
|
|
298
|
+
int64 dictionary_ref;
|
|
299
|
+
bytea *dictionary_sha256;
|
|
300
|
+
bytea *dictionary;
|
|
301
|
+
ZSTD_DDict *ddict;
|
|
302
|
+
char err[MCDB_ERRLEN];
|
|
303
|
+
mcdb_status status;
|
|
304
|
+
uint64_t original_size;
|
|
305
|
+
unsigned char *output;
|
|
306
|
+
size_t output_len;
|
|
307
|
+
|
|
308
|
+
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3))
|
|
309
|
+
PG_RETURN_NULL();
|
|
310
|
+
dictionary_ref = PG_GETARG_INT64(1);
|
|
311
|
+
if (dictionary_ref <= 0)
|
|
312
|
+
PG_RETURN_BOOL(false);
|
|
313
|
+
dictionary_sha256 = PG_GETARG_BYTEA_PP(2);
|
|
314
|
+
if (VARSIZE_ANY_EXHDR(dictionary_sha256) != MCDB_SHA256_BYTES)
|
|
315
|
+
PG_RETURN_BOOL(false);
|
|
316
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
317
|
+
dictionary = PG_GETARG_BYTEA_PP(3);
|
|
318
|
+
ddict = mcdb_pg_cached_ddict(fcinfo, dictionary_ref,
|
|
319
|
+
(const unsigned char *)VARDATA_ANY(dictionary_sha256),
|
|
320
|
+
(const unsigned char *)VARDATA_ANY(dictionary),
|
|
321
|
+
(size_t)VARSIZE_ANY_EXHDR(dictionary), err, &status);
|
|
322
|
+
if (!ddict || status != MCDB_OK)
|
|
323
|
+
PG_RETURN_BOOL(false);
|
|
324
|
+
status = mcdb_validate_v2_header((const unsigned char *)VARDATA_ANY(input),
|
|
325
|
+
(size_t)VARSIZE_ANY_EXHDR(input), &original_size, NULL);
|
|
326
|
+
if (status != MCDB_OK)
|
|
327
|
+
PG_RETURN_BOOL(false);
|
|
328
|
+
output = (unsigned char *)palloc((size_t)original_size ? (size_t)original_size : 1);
|
|
329
|
+
status = mcdb_decode_v2_into((const unsigned char *)VARDATA_ANY(input),
|
|
330
|
+
(size_t)VARSIZE_ANY_EXHDR(input), (uint64_t)dictionary_ref,
|
|
331
|
+
(const unsigned char *)VARDATA_ANY(dictionary),
|
|
332
|
+
(size_t)VARSIZE_ANY_EXHDR(dictionary), mcdb_pg_cache(fcinfo)->dctx,
|
|
333
|
+
ddict, output, (size_t)original_size, &output_len, err);
|
|
334
|
+
pfree(output);
|
|
335
|
+
PG_RETURN_BOOL(status == MCDB_OK);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
Datum multi_compress_db_decompress_dict(PG_FUNCTION_ARGS) {
|
|
339
|
+
bytea *input;
|
|
340
|
+
int64 dictionary_ref;
|
|
341
|
+
bytea *dictionary_sha256;
|
|
342
|
+
bytea *dictionary;
|
|
343
|
+
ZSTD_DDict *ddict;
|
|
344
|
+
char err[MCDB_ERRLEN];
|
|
345
|
+
mcdb_status status;
|
|
346
|
+
uint64_t original_size;
|
|
347
|
+
size_t output_len = 0;
|
|
348
|
+
text *result;
|
|
349
|
+
|
|
350
|
+
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3))
|
|
351
|
+
PG_RETURN_NULL();
|
|
352
|
+
mcdb_require_utf8_database();
|
|
353
|
+
dictionary_ref = PG_GETARG_INT64(1);
|
|
354
|
+
if (dictionary_ref <= 0)
|
|
355
|
+
mcdb_raise_dictionary("dictionary reference must be a positive signed bigint");
|
|
356
|
+
dictionary_sha256 = PG_GETARG_BYTEA_PP(2);
|
|
357
|
+
if (VARSIZE_ANY_EXHDR(dictionary_sha256) != MCDB_SHA256_BYTES)
|
|
358
|
+
mcdb_raise_dictionary("dictionary sha256 must be exactly 32 bytes");
|
|
359
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
360
|
+
dictionary = PG_GETARG_BYTEA_PP(3);
|
|
361
|
+
ddict = mcdb_pg_cached_ddict(fcinfo, dictionary_ref,
|
|
362
|
+
(const unsigned char *)VARDATA_ANY(dictionary_sha256),
|
|
363
|
+
(const unsigned char *)VARDATA_ANY(dictionary),
|
|
364
|
+
(size_t)VARSIZE_ANY_EXHDR(dictionary), err, &status);
|
|
365
|
+
if (!ddict || status != MCDB_OK)
|
|
366
|
+
mcdb_raise(status, err);
|
|
367
|
+
status = mcdb_validate_v2_header((const unsigned char *)VARDATA_ANY(input),
|
|
368
|
+
(size_t)VARSIZE_ANY_EXHDR(input), &original_size, NULL);
|
|
369
|
+
if (status != MCDB_OK)
|
|
370
|
+
mcdb_raise(status, mcdb_status_str(status));
|
|
371
|
+
result = (text *)palloc(VARHDRSZ + (size_t)original_size);
|
|
372
|
+
status = mcdb_decode_v2_into(
|
|
373
|
+
(const unsigned char *)VARDATA_ANY(input), (size_t)VARSIZE_ANY_EXHDR(input),
|
|
374
|
+
(uint64_t)dictionary_ref, (const unsigned char *)VARDATA_ANY(dictionary),
|
|
375
|
+
(size_t)VARSIZE_ANY_EXHDR(dictionary), mcdb_pg_cache(fcinfo)->dctx, ddict,
|
|
376
|
+
(unsigned char *)VARDATA(result), (size_t)original_size, &output_len, err);
|
|
377
|
+
if (status != MCDB_OK)
|
|
378
|
+
mcdb_raise(status, err);
|
|
379
|
+
SET_VARSIZE(result, VARHDRSZ + output_len);
|
|
380
|
+
PG_RETURN_TEXT_P(result);
|
|
381
|
+
}
|