multi_compress 0.3.5 → 0.5.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 +76 -0
- data/GET_STARTED.md +188 -0
- data/README.md +56 -0
- data/db_core/include/mcdb_format.h +55 -0
- data/db_core/src/mcdb_format.c +341 -0
- data/db_deployment/mysql/Makefile +48 -0
- data/db_deployment/mysql/README.md +42 -0
- data/db_deployment/mysql/bin/common +160 -0
- data/db_deployment/mysql/bin/disable +40 -0
- data/db_deployment/mysql/bin/doctor +64 -0
- data/db_deployment/mysql/bin/enable +57 -0
- data/db_deployment/mysql/bin/install +64 -0
- data/db_deployment/mysql/bin/status +65 -0
- data/db_deployment/mysql/bin/uninstall +47 -0
- data/db_deployment/mysql/bin/upgrade +115 -0
- data/db_deployment/mysql/bin/verify +14 -0
- data/db_deployment/postgres/Makefile +45 -0
- data/db_deployment/postgres/README.md +60 -0
- data/db_deployment/postgres/bin/doctor +61 -0
- data/db_deployment/postgres/bin/enable +136 -0
- data/db_deployment/postgres/bin/install +104 -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/exe/multi_compress +228 -0
- data/ext/multi_compress/extconf.rb +4 -0
- data/ext/multi_compress/multi_compress.c +30 -0
- data/lib/multi_compress/active_record.rb +61 -0
- data/lib/multi_compress/codec.rb +164 -0
- data/lib/multi_compress/database.rb +106 -0
- data/lib/multi_compress/db_deployment.rb +456 -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 +95 -0
- data/mysql_udf/sql/examples.sql +11 -0
- data/mysql_udf/sql/install.sql +15 -0
- data/mysql_udf/sql/uninstall.sql +4 -0
- data/mysql_udf/sql/views.sql +8 -0
- data/mysql_udf/src/multi_compress_mysql.c +133 -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 +296 -0
- data/postgres_extension/Makefile +74 -0
- data/postgres_extension/README.md +112 -0
- data/postgres_extension/multi_compress.control +4 -0
- data/postgres_extension/multi_compress_pg.exports +12 -0
- data/postgres_extension/sql/examples.sql +12 -0
- data/postgres_extension/sql/multi_compress--0.5.0.sql +24 -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 +96 -0
- data/postgres_extension/test/run_e2e.sh +323 -0
- metadata +52 -3
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
EXTENSION = multi_compress
|
|
2
|
+
MODULE_big = multi_compress_pg
|
|
3
|
+
OBJS = src/multi_compress_pg.o src/mcdb_format.o
|
|
4
|
+
DATA = sql/multi_compress--0.5.0.sql
|
|
5
|
+
|
|
6
|
+
PG_CONFIG ?= pg_config
|
|
7
|
+
ZSTD_DIR ?= ../ext/multi_compress/vendor/zstd/lib
|
|
8
|
+
CORE_DIR ?= ../db_core
|
|
9
|
+
BUILD ?= build
|
|
10
|
+
CC ?= cc
|
|
11
|
+
AR ?= ar
|
|
12
|
+
|
|
13
|
+
WARNFLAGS ?= -Wall -Wextra -Werror
|
|
14
|
+
PG_CPPFLAGS += -I$(CORE_DIR)/include -I$(ZSTD_DIR)
|
|
15
|
+
PG_CFLAGS += -O2 -fPIC -std=c99 $(WARNFLAGS)
|
|
16
|
+
EXPORT_MAP := $(CURDIR)/multi_compress_pg.exports
|
|
17
|
+
SHLIB_LINK += -Wl,--version-script=$(EXPORT_MAP) $(ZSTD_LIB)
|
|
18
|
+
EXTRA_CLEAN += $(BUILD)
|
|
19
|
+
|
|
20
|
+
ZSTD_CFLAGS ?= -O2 -fPIC -std=c99 -I$(ZSTD_DIR) -I$(ZSTD_DIR)/common
|
|
21
|
+
ZSTD_OBJ_DIR := $(BUILD)/zstd
|
|
22
|
+
ZSTD_C_SRCS := $(wildcard $(ZSTD_DIR)/common/*.c) $(wildcard $(ZSTD_DIR)/decompress/*.c)
|
|
23
|
+
ZSTD_C_OBJS := $(patsubst $(ZSTD_DIR)/%.c,$(ZSTD_OBJ_DIR)/%.o,$(ZSTD_C_SRCS))
|
|
24
|
+
|
|
25
|
+
ARCH := $(shell uname -m)
|
|
26
|
+
ifneq (,$(filter x86_64 amd64,$(ARCH)))
|
|
27
|
+
ZSTD_ASM_SRC := $(ZSTD_DIR)/decompress/huf_decompress_amd64.S
|
|
28
|
+
ifneq (,$(wildcard $(ZSTD_ASM_SRC)))
|
|
29
|
+
ZSTD_ASM_OBJ := $(ZSTD_OBJ_DIR)/decompress/huf_decompress_amd64.o
|
|
30
|
+
endif
|
|
31
|
+
endif
|
|
32
|
+
|
|
33
|
+
ZSTD_OBJS := $(ZSTD_C_OBJS) $(ZSTD_ASM_OBJ)
|
|
34
|
+
ZSTD_LIB := $(BUILD)/libzstd_dec.a
|
|
35
|
+
|
|
36
|
+
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
|
37
|
+
include $(PGXS)
|
|
38
|
+
|
|
39
|
+
.PHONY: all test parity e2e fixtures
|
|
40
|
+
|
|
41
|
+
all: $(ZSTD_LIB)
|
|
42
|
+
|
|
43
|
+
$(MODULE_big)$(DLSUFFIX): $(ZSTD_LIB)
|
|
44
|
+
|
|
45
|
+
$(BUILD):
|
|
46
|
+
mkdir -p $@
|
|
47
|
+
|
|
48
|
+
$(ZSTD_OBJ_DIR)/%.o: $(ZSTD_DIR)/%.c
|
|
49
|
+
@mkdir -p $(dir $@)
|
|
50
|
+
$(CC) $(ZSTD_CFLAGS) -MMD -MP -c $< -o $@
|
|
51
|
+
|
|
52
|
+
$(ZSTD_OBJ_DIR)/%.o: $(ZSTD_DIR)/%.S
|
|
53
|
+
@mkdir -p $(dir $@)
|
|
54
|
+
$(CC) -fPIC -I$(ZSTD_DIR) -c $< -o $@
|
|
55
|
+
|
|
56
|
+
$(ZSTD_LIB): $(ZSTD_OBJS) | $(BUILD)
|
|
57
|
+
$(AR) rcs $@ $^
|
|
58
|
+
|
|
59
|
+
# The source wrapper includes the common MCDB1 decoder; it is not a fork.
|
|
60
|
+
src/mcdb_format.o: src/mcdb_format.c $(CORE_DIR)/src/mcdb_format.c $(CORE_DIR)/include/mcdb_format.h
|
|
61
|
+
|
|
62
|
+
# Golden fixtures are committed. Regeneration is a development action only.
|
|
63
|
+
fixtures:
|
|
64
|
+
RUBYLIB=../lib ruby ../test/fixtures/database_v1/generate.rb
|
|
65
|
+
|
|
66
|
+
parity:
|
|
67
|
+
$(MAKE) -C ../mysql_udf test
|
|
68
|
+
|
|
69
|
+
test: all parity
|
|
70
|
+
|
|
71
|
+
e2e:
|
|
72
|
+
./test/run_e2e.sh
|
|
73
|
+
|
|
74
|
+
-include $(ZSTD_C_OBJS:.o=.d)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# MultiCompress PostgreSQL extension (`MCDB1` reader)
|
|
2
|
+
|
|
3
|
+
This PostgreSQL extension reads the same frozen `MCDB1` envelopes as the Ruby
|
|
4
|
+
API and the MySQL UDF. Rails stores `MultiCompress::Database.compress(text)` in
|
|
5
|
+
`bytea`; PostgreSQL exposes decoded UTF-8 through SQL and a view for DBeaver.
|
|
6
|
+
|
|
7
|
+
It is a separate server-native artifact, not part of `gem install`. The shared
|
|
8
|
+
format contract is [`docs/database-envelope-v1.md`](../docs/database-envelope-v1.md).
|
|
9
|
+
|
|
10
|
+
## SQL API
|
|
11
|
+
|
|
12
|
+
| Function | Returns | Purpose |
|
|
13
|
+
|---|---:|---|
|
|
14
|
+
| `multi_compress_db_version()` | `text` | reader build, MCDB1 and zstd version smoke test |
|
|
15
|
+
| `multi_compress_db_is_valid(bytea)` | `boolean` | validates an MCDB1 blob without raising |
|
|
16
|
+
| `multi_compress_db_decompress(bytea)` | `text` | returns UTF-8 text or raises on corruption |
|
|
17
|
+
|
|
18
|
+
`multi_compress_db_decompress` requires a UTF-8 PostgreSQL database. MCDB1 does
|
|
19
|
+
not permit NUL bytes, so decoded values are valid PostgreSQL `text` values.
|
|
20
|
+
|
|
21
|
+
## Production deployment: generated DBA bundle
|
|
22
|
+
|
|
23
|
+
Do **not** ask a DBA to clone this repository, find a release tag, manually copy
|
|
24
|
+
`multi_compress_pg.so`, or guess PostgreSQL install paths. The application
|
|
25
|
+
developer generates a source bundle from the exact installed gem version:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bundle exec multi_compress db package postgres --output tmp/multi-compress-postgres.tar.gz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The DBA receives that one archive and runs this on the PostgreSQL host:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
tar -xzf multi-compress-postgres.tar.gz
|
|
35
|
+
cd multi_compress-postgres-0.5.0
|
|
36
|
+
make verify
|
|
37
|
+
make doctor
|
|
38
|
+
sudo make install
|
|
39
|
+
sudo -u postgres make enable DB=app_production \
|
|
40
|
+
MIGRATION_ROLE=app_migrations READ_ROLE=dbeaver_readonly
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`install` compiles the extension against the selected host `pg_config` and puts
|
|
44
|
+
its library/control/SQL files in the directories reported by that exact server.
|
|
45
|
+
Install the matching `postgresql-server-dev-<major>` package and a C toolchain
|
|
46
|
+
beforehand; `make doctor` verifies both.
|
|
47
|
+
For a non-default PostgreSQL install use:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
sudo make install PG_CONFIG=/usr/lib/postgresql/17/bin/pg_config
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`enable` rejects non-UTF8 databases and an extension already installed in a
|
|
54
|
+
different schema. It runs `CREATE EXTENSION` once per application database,
|
|
55
|
+
revokes public access, grants the migration role `USAGE` on the extension schema,
|
|
56
|
+
and grants both named roles `EXECUTE` on the decoder functions. The application
|
|
57
|
+
migration must grant the read role `SELECT` on each generated view. Repeat the
|
|
58
|
+
native install on every reporting, failover and restore target before enabling
|
|
59
|
+
or querying MCDB1 there.
|
|
60
|
+
|
|
61
|
+
## DBeaver: generated readable view
|
|
62
|
+
|
|
63
|
+
Generate the view in the application repository and put the output in a Rails
|
|
64
|
+
migration or versioned SQL. It is application schema, not a hand-typed DBA task:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
bundle exec multi_compress db view postgres \
|
|
68
|
+
--table app.events \
|
|
69
|
+
--column payload_compressed \
|
|
70
|
+
--view admin.events_readable \
|
|
71
|
+
--columns id,created_at,status \
|
|
72
|
+
--as payload \
|
|
73
|
+
--output db/views/events_readable.sql
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
DBeaver users only query `admin.events_readable`. Their role needs `USAGE` on
|
|
77
|
+
`admin`, `SELECT` on this view, and `EXECUTE` on the decoder functions granted by
|
|
78
|
+
`make enable`; it does not need `USAGE` on `multi_compress`. If `make enable`
|
|
79
|
+
used a different extension schema, pass it to the generator with
|
|
80
|
+
`--extension-schema that_schema`:
|
|
81
|
+
|
|
82
|
+
```sql
|
|
83
|
+
SELECT id, created_at, status, payload
|
|
84
|
+
FROM admin.events_readable
|
|
85
|
+
WHERE id = 123;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The view decompresses selected rows. Filter with indexed ordinary columns before
|
|
89
|
+
reading it; do not scan decoded payloads with an unbounded `LIKE '%text%'`.
|
|
90
|
+
|
|
91
|
+
## Migration and constraints
|
|
92
|
+
|
|
93
|
+
Do not reinterpret an existing plaintext column as MCDB1. Add a new nullable
|
|
94
|
+
`bytea` column, dual-write it from the application, backfill in batches, verify
|
|
95
|
+
`multi_compress.multi_compress_db_is_valid(blob)`, switch reads, then keep the
|
|
96
|
+
old column for a rollback window. MCDB1 v1 is zstd-only, accepts valid UTF-8
|
|
97
|
+
text without NUL bytes, and has a 16 MiB decompressed limit. The target database
|
|
98
|
+
must use UTF-8 encoding for the text-returning function.
|
|
99
|
+
|
|
100
|
+
## Development source build
|
|
101
|
+
|
|
102
|
+
The source target remains useful for contributors and CI:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
cd postgres_extension
|
|
106
|
+
make PG_CONFIG=/usr/lib/postgresql/17/bin/pg_config
|
|
107
|
+
make test PG_CONFIG=/usr/lib/postgresql/17/bin/pg_config
|
|
108
|
+
make e2e
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`test/run_e2e.sh` builds against a pinned PostgreSQL Docker target and is the
|
|
112
|
+
server-side acceptance gate. It is not part of the end-user installation flow.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
global:
|
|
3
|
+
Pg_magic_func;
|
|
4
|
+
pg_finfo_multi_compress_db_version;
|
|
5
|
+
pg_finfo_multi_compress_db_is_valid;
|
|
6
|
+
pg_finfo_multi_compress_db_decompress;
|
|
7
|
+
multi_compress_db_version;
|
|
8
|
+
multi_compress_db_is_valid;
|
|
9
|
+
multi_compress_db_decompress;
|
|
10
|
+
local:
|
|
11
|
+
*;
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
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;
|
|
@@ -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,96 @@
|
|
|
1
|
+
#include "postgres.h"
|
|
2
|
+
|
|
3
|
+
#include "fmgr.h"
|
|
4
|
+
#include "mb/pg_wchar.h"
|
|
5
|
+
#include "utils/builtins.h"
|
|
6
|
+
#include "varatt.h"
|
|
7
|
+
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
#include <zstd.h>
|
|
13
|
+
|
|
14
|
+
#include "mcdb_format.h"
|
|
15
|
+
|
|
16
|
+
PG_MODULE_MAGIC;
|
|
17
|
+
|
|
18
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_version);
|
|
19
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_is_valid);
|
|
20
|
+
PG_FUNCTION_INFO_V1(multi_compress_db_decompress);
|
|
21
|
+
|
|
22
|
+
static void mcdb_require_utf8_database(void) {
|
|
23
|
+
if (GetDatabaseEncoding() != PG_UTF8)
|
|
24
|
+
ereport(
|
|
25
|
+
ERROR,
|
|
26
|
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
27
|
+
errmsg("multi_compress requires a UTF-8 PostgreSQL database"),
|
|
28
|
+
errdetail("MCDB1 stores UTF-8 text and multi_compress_db_decompress returns text.")));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static void mcdb_raise(mcdb_status status, const char *detail) {
|
|
32
|
+
if (status == MCDB_ERR_ALLOC)
|
|
33
|
+
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY),
|
|
34
|
+
errmsg("multi_compress: unable to allocate MCDB1 output")));
|
|
35
|
+
|
|
36
|
+
ereport(ERROR,
|
|
37
|
+
(errcode(ERRCODE_DATA_CORRUPTED), errmsg("multi_compress: invalid MCDB1 envelope"),
|
|
38
|
+
errdetail("%s", detail && detail[0] ? detail : mcdb_status_str(status))));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Datum multi_compress_db_version(PG_FUNCTION_ARGS) {
|
|
42
|
+
char version[64];
|
|
43
|
+
|
|
44
|
+
(void)fcinfo;
|
|
45
|
+
|
|
46
|
+
snprintf(version, sizeof(version), "multi_compress reader %s; MCDB1; zstd %s",
|
|
47
|
+
MCDB_READER_VERSION, ZSTD_versionString());
|
|
48
|
+
PG_RETURN_TEXT_P(cstring_to_text(version));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Datum multi_compress_db_is_valid(PG_FUNCTION_ARGS) {
|
|
52
|
+
bytea *input;
|
|
53
|
+
char err[MCDB_ERRLEN];
|
|
54
|
+
mcdb_status status;
|
|
55
|
+
|
|
56
|
+
if (PG_ARGISNULL(0))
|
|
57
|
+
PG_RETURN_NULL();
|
|
58
|
+
|
|
59
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
60
|
+
status = mcdb_decode((const unsigned char *)VARDATA_ANY(input),
|
|
61
|
+
(size_t)VARSIZE_ANY_EXHDR(input), NULL, NULL, err);
|
|
62
|
+
|
|
63
|
+
PG_RETURN_BOOL(status == MCDB_OK);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Datum multi_compress_db_decompress(PG_FUNCTION_ARGS) {
|
|
67
|
+
bytea *input;
|
|
68
|
+
size_t output_len = 0;
|
|
69
|
+
char err[MCDB_ERRLEN];
|
|
70
|
+
mcdb_status status;
|
|
71
|
+
uint64_t original_size = 0;
|
|
72
|
+
text *result;
|
|
73
|
+
|
|
74
|
+
if (PG_ARGISNULL(0))
|
|
75
|
+
PG_RETURN_NULL();
|
|
76
|
+
|
|
77
|
+
mcdb_require_utf8_database();
|
|
78
|
+
|
|
79
|
+
input = PG_GETARG_BYTEA_PP(0);
|
|
80
|
+
status = mcdb_validate_header((const unsigned char *)VARDATA_ANY(input),
|
|
81
|
+
(size_t)VARSIZE_ANY_EXHDR(input), &original_size);
|
|
82
|
+
if (status != MCDB_OK)
|
|
83
|
+
mcdb_raise(status, mcdb_status_str(status));
|
|
84
|
+
|
|
85
|
+
result = (text *)palloc(VARHDRSZ + (size_t)original_size);
|
|
86
|
+
|
|
87
|
+
status = mcdb_decode_into((const unsigned char *)VARDATA_ANY(input),
|
|
88
|
+
(size_t)VARSIZE_ANY_EXHDR(input), (unsigned char *)VARDATA(result),
|
|
89
|
+
(size_t)original_size, &output_len, err);
|
|
90
|
+
if (status != MCDB_OK)
|
|
91
|
+
mcdb_raise(status, err);
|
|
92
|
+
|
|
93
|
+
SET_VARSIZE(result, VARHDRSZ + output_len);
|
|
94
|
+
|
|
95
|
+
PG_RETURN_TEXT_P(result);
|
|
96
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
cd "$(dirname "$0")/../.."
|
|
4
|
+
|
|
5
|
+
# Platform-specific manifest for postgres:17.9-bookworm on linux/amd64.
|
|
6
|
+
DEFAULT_POSTGRES_IMAGE="postgres:17.9-bookworm@sha256:3a69b9de644363f30110fbaa78b9e3298ce71bb1aa9124c0b0ceb0a2602c3283"
|
|
7
|
+
IMAGE="${MCDB_POSTGRES_IMAGE:-$DEFAULT_POSTGRES_IMAGE}"
|
|
8
|
+
PLATFORM="${MCDB_POSTGRES_PLATFORM:-linux/amd64}"
|
|
9
|
+
CONTAINER="${MCDB_POSTGRES_CONTAINER:-mcdb-postgres-e2e}"
|
|
10
|
+
WAIT_SECONDS="${MCDB_POSTGRES_WAIT_SECONDS:-90}"
|
|
11
|
+
WORK_ROOT="${MCDB_POSTGRES_WORK_ROOT:-$(mktemp -d -t multi-compress-postgres.XXXXXX)}"
|
|
12
|
+
REMOVE_WORK_ROOT=0
|
|
13
|
+
ARCHIVE="$WORK_ROOT/multi_compress-postgres.tar.gz"
|
|
14
|
+
GEM_UNPACK_DIR="$WORK_ROOT/unpacked"
|
|
15
|
+
GEM_FILE="$WORK_ROOT/multi_compress.gem"
|
|
16
|
+
PACKAGED_GEM_ROOT=""
|
|
17
|
+
BUNDLE_PARENT="/opt"
|
|
18
|
+
|
|
19
|
+
command -v docker >/dev/null 2>&1 || { echo "docker is required" >&2; exit 2; }
|
|
20
|
+
|
|
21
|
+
cleanup() {
|
|
22
|
+
docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
|
|
23
|
+
[ "$REMOVE_WORK_ROOT" -eq 1 ] && rm -rf "$WORK_ROOT"
|
|
24
|
+
}
|
|
25
|
+
trap cleanup EXIT
|
|
26
|
+
|
|
27
|
+
if [ -z "${MCDB_POSTGRES_WORK_ROOT:-}" ]; then
|
|
28
|
+
REMOVE_WORK_ROOT=1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
ensure_ruby_extension() {
|
|
32
|
+
if ruby -Ilib -e 'require "multi_compress"; require "multi_compress/database"' >/dev/null 2>&1; then
|
|
33
|
+
return
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
echo "building Ruby extension..."
|
|
37
|
+
if command -v bundle >/dev/null 2>&1; then
|
|
38
|
+
bundle exec rake compile
|
|
39
|
+
else
|
|
40
|
+
rake compile
|
|
41
|
+
fi
|
|
42
|
+
ruby -Ilib -e 'require "multi_compress"; require "multi_compress/database"' >/dev/null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
require_fixture() {
|
|
46
|
+
local path="$1"
|
|
47
|
+
[ -f "$path" ] || { echo "missing committed fixture: $path" >&2; exit 2; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fixture_hex() {
|
|
51
|
+
ruby -e 'print File.binread(ARGV.fetch(0)).unpack1("H*")' "$1"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
prepare_packaged_gem() {
|
|
55
|
+
gem build multi_compress.gemspec --output "$GEM_FILE" >/dev/null
|
|
56
|
+
mkdir -p "$GEM_UNPACK_DIR"
|
|
57
|
+
gem unpack "$GEM_FILE" --target "$GEM_UNPACK_DIR" >/dev/null
|
|
58
|
+
shopt -s nullglob
|
|
59
|
+
roots=("$GEM_UNPACK_DIR"/*)
|
|
60
|
+
shopt -u nullglob
|
|
61
|
+
[ "${#roots[@]}" -eq 1 ] && [ -d "${roots[0]}" ] || { echo "could not unpack exactly one multi_compress gem root" >&2; exit 2; }
|
|
62
|
+
PACKAGED_GEM_ROOT="${roots[0]}"
|
|
63
|
+
[ -f "$PACKAGED_GEM_ROOT/multi_compress.gemspec" ] || { echo "packaged gem lacks gemspec" >&2; exit 2; }
|
|
64
|
+
[ -d "$PACKAGED_GEM_ROOT/lib" ] || { echo "packaged gem lacks lib" >&2; exit 2; }
|
|
65
|
+
[ -d "$PACKAGED_GEM_ROOT/exe" ] || { echo "packaged gem lacks exe" >&2; exit 2; }
|
|
66
|
+
[ -f "$PACKAGED_GEM_ROOT/lib/multi_compress/db_deployment.rb" ] || { echo "packaged gem lacks db deployment CLI" >&2; exit 2; }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
db_cli() {
|
|
70
|
+
ruby -I "$PACKAGED_GEM_ROOT/lib" "$PACKAGED_GEM_ROOT/exe/multi_compress" db "$@"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
compress_hex() {
|
|
74
|
+
MCDB_TEXT="$1" ruby -Ilib -r multi_compress -r multi_compress/database \
|
|
75
|
+
-e 'print MultiCompress::Database.compress(ENV.fetch("MCDB_TEXT")).unpack1("H*")'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
bundle_name() {
|
|
79
|
+
ruby -I "$PACKAGED_GEM_ROOT/lib" -r multi_compress/version \
|
|
80
|
+
-e 'print "multi_compress-postgres-#{MultiCompress::VERSION}"'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
build_deployment_bundle() {
|
|
84
|
+
db_cli package postgres --output "$ARCHIVE" --force
|
|
85
|
+
test -s "$ARCHIVE"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
install_postgres_build_tools() {
|
|
89
|
+
local runtime_pg_config="$1"
|
|
90
|
+
|
|
91
|
+
docker exec -u 0 \
|
|
92
|
+
-e "MCDB_RUNTIME_PG_CONFIG=$runtime_pg_config" \
|
|
93
|
+
"$CONTAINER" sh -ceu '
|
|
94
|
+
runtime_pg_config="${MCDB_RUNTIME_PG_CONFIG:?missing runtime pg_config}"
|
|
95
|
+
pg_major="$("$runtime_pg_config" --version | awk '\''{print $2}'\'' | cut -d. -f1)"
|
|
96
|
+
case "$pg_major" in
|
|
97
|
+
""|*[!0-9]*)
|
|
98
|
+
echo "could not determine PostgreSQL major version" >&2
|
|
99
|
+
exit 2
|
|
100
|
+
;;
|
|
101
|
+
esac
|
|
102
|
+
|
|
103
|
+
if ! command -v make >/dev/null 2>&1 || ! command -v cc >/dev/null 2>&1; then
|
|
104
|
+
if ! command -v apt-get >/dev/null 2>&1; then
|
|
105
|
+
echo "PostgreSQL container has no supported package manager" >&2
|
|
106
|
+
exit 2
|
|
107
|
+
fi
|
|
108
|
+
apt-get update >&2
|
|
109
|
+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
110
|
+
build-essential ca-certificates curl gnupg tar gzip >&2
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
runtime_pgxs="$("$runtime_pg_config" --pgxs 2>/dev/null || true)"
|
|
114
|
+
runtime_headers="$("$runtime_pg_config" --includedir-server 2>/dev/null || true)"
|
|
115
|
+
if [ ! -f "$runtime_pgxs" ] || [ ! -f "$runtime_headers/postgres.h" ]; then
|
|
116
|
+
if ! command -v apt-get >/dev/null 2>&1; then
|
|
117
|
+
echo "PostgreSQL development headers are unavailable" >&2
|
|
118
|
+
exit 2
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
install -d -m 0755 /etc/apt/keyrings
|
|
122
|
+
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
|
|
123
|
+
gpg --dearmor --yes -o /etc/apt/keyrings/postgresql.gpg
|
|
124
|
+
. /etc/os-release
|
|
125
|
+
echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] https://apt.postgresql.org/pub/repos/apt ${VERSION_CODENAME}-pgdg main" \
|
|
126
|
+
>/etc/apt/sources.list.d/pgdg.list
|
|
127
|
+
apt-get update >&2
|
|
128
|
+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
129
|
+
"postgresql-server-dev-${pg_major}" >&2
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
build_pg_config=""
|
|
133
|
+
for candidate in "/usr/lib/postgresql/${pg_major}/bin/pg_config" "$(command -v pg_config || true)"; do
|
|
134
|
+
[ -n "$candidate" ] && [ -x "$candidate" ] || continue
|
|
135
|
+
pgxs="$("$candidate" --pgxs 2>/dev/null || true)"
|
|
136
|
+
headers="$("$candidate" --includedir-server 2>/dev/null || true)"
|
|
137
|
+
if [ -f "$pgxs" ] && [ -f "$headers/postgres.h" ]; then
|
|
138
|
+
build_pg_config="$candidate"
|
|
139
|
+
break
|
|
140
|
+
fi
|
|
141
|
+
done
|
|
142
|
+
|
|
143
|
+
[ -n "$build_pg_config" ] || {
|
|
144
|
+
echo "PostgreSQL PGXS/server headers are unavailable" >&2
|
|
145
|
+
exit 2
|
|
146
|
+
}
|
|
147
|
+
printf "%s\n" "$build_pg_config"
|
|
148
|
+
'
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
install_bundle_on_db_host() {
|
|
152
|
+
local bundle_name="$1"
|
|
153
|
+
local build_pg_config="$2"
|
|
154
|
+
local runtime_pg_config="$3"
|
|
155
|
+
local bundle_root="${BUNDLE_PARENT}/${bundle_name}"
|
|
156
|
+
|
|
157
|
+
tar -tzf "$ARCHIVE" | grep -Fx "${bundle_name}/THIRD_PARTY_NOTICES.md" >/dev/null
|
|
158
|
+
if tar -tzf "$ARCHIVE" | grep -E '/\.DS_Store$' >/dev/null; then
|
|
159
|
+
echo 'deployment bundle must not contain .DS_Store' >&2
|
|
160
|
+
exit 2
|
|
161
|
+
fi
|
|
162
|
+
docker cp "$ARCHIVE" "$CONTAINER:/tmp/${bundle_name}.tar.gz"
|
|
163
|
+
docker exec -u 0 \
|
|
164
|
+
-e "MCDB_BUNDLE_ROOT=$bundle_root" \
|
|
165
|
+
-e "MCDB_BUILD_PG_CONFIG=$build_pg_config" \
|
|
166
|
+
-e "MCDB_RUNTIME_PG_CONFIG=$runtime_pg_config" \
|
|
167
|
+
"$CONTAINER" sh -ceu '
|
|
168
|
+
rm -rf "$MCDB_BUNDLE_ROOT"
|
|
169
|
+
mkdir -p "$(dirname "$MCDB_BUNDLE_ROOT")"
|
|
170
|
+
tar -xzf "/tmp/$(basename "$MCDB_BUNDLE_ROOT").tar.gz" -C "$(dirname "$MCDB_BUNDLE_ROOT")"
|
|
171
|
+
cd "$MCDB_BUNDLE_ROOT"
|
|
172
|
+
make verify
|
|
173
|
+
make doctor PG_CONFIG="$MCDB_BUILD_PG_CONFIG"
|
|
174
|
+
make install BUILD_PG_CONFIG="$MCDB_BUILD_PG_CONFIG" RUNTIME_PG_CONFIG="$MCDB_RUNTIME_PG_CONFIG"
|
|
175
|
+
'
|
|
176
|
+
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
apply_readable_view() {
|
|
180
|
+
local path="$1"
|
|
181
|
+
db_cli view postgres \
|
|
182
|
+
--table app.events \
|
|
183
|
+
--column payload_compressed \
|
|
184
|
+
--view admin.events_readable \
|
|
185
|
+
--columns id \
|
|
186
|
+
--output "$path"
|
|
187
|
+
docker exec -i "$CONTAINER" psql -X -U app_migrations -d postgres -v ON_ERROR_STOP=1 < "$path"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
ensure_ruby_extension
|
|
191
|
+
prepare_packaged_gem
|
|
192
|
+
build_deployment_bundle
|
|
193
|
+
|
|
194
|
+
for name in corrupt_magic corrupt_payload corrupt_crc invalid_utf8 nul_text trailing_skippable trailing_frame; do
|
|
195
|
+
require_fixture "test/fixtures/database_v1/${name}.mcdb"
|
|
196
|
+
done
|
|
197
|
+
|
|
198
|
+
TEXT='Привет, DBeaver! Сәлем! 🌍'
|
|
199
|
+
BIG=$(ruby -e 'print "a" * 300')
|
|
200
|
+
VALID_HEX=$(compress_hex "$TEXT")
|
|
201
|
+
BIG_HEX=$(compress_hex "$BIG")
|
|
202
|
+
BUNDLE_NAME="$(bundle_name)"
|
|
203
|
+
VIEW_SQL="$(mktemp -t multi-compress-postgres-view.XXXXXX.sql)"
|
|
204
|
+
trap 'rm -f "$VIEW_SQL"; cleanup' EXIT
|
|
205
|
+
|
|
206
|
+
docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
|
|
207
|
+
docker run --platform "$PLATFORM" -d --name "$CONTAINER" \
|
|
208
|
+
-e POSTGRES_HOST_AUTH_METHOD=trust "$IMAGE" >/dev/null
|
|
209
|
+
|
|
210
|
+
echo "waiting for PostgreSQL in $IMAGE..."
|
|
211
|
+
deadline=$(( $(date +%s) + WAIT_SECONDS ))
|
|
212
|
+
until docker exec "$CONTAINER" pg_isready -U postgres -d postgres >/dev/null 2>&1; do
|
|
213
|
+
if [ "$(date +%s)" -ge "$deadline" ]; then
|
|
214
|
+
echo "PostgreSQL did not become ready within ${WAIT_SECONDS}s" >&2
|
|
215
|
+
docker logs "$CONTAINER" >&2 || true
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
sleep 2
|
|
219
|
+
done
|
|
220
|
+
|
|
221
|
+
q() { docker exec "$CONTAINER" psql -X -U postgres -d postgres -Atq -v ON_ERROR_STOP=1 -c "$1"; }
|
|
222
|
+
qfail() {
|
|
223
|
+
if q "$1" >/dev/null 2>&1; then return 1; else return 0; fi
|
|
224
|
+
}
|
|
225
|
+
q_read() { docker exec "$CONTAINER" psql -X -U dbeaver_readonly -d postgres -Atq -v ON_ERROR_STOP=1 -c "$1"; }
|
|
226
|
+
q_read_fail() {
|
|
227
|
+
if q_read "$1" >/dev/null 2>&1; then return 1; else return 0; fi
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
RUNTIME_PG_CONFIG=$(docker exec "$CONTAINER" sh -ceu 'command -v pg_config' | tr -d '\r\n')
|
|
231
|
+
[ -n "$RUNTIME_PG_CONFIG" ] || { echo "PostgreSQL runtime does not provide pg_config" >&2; exit 2; }
|
|
232
|
+
BUILD_PG_CONFIG=$(install_postgres_build_tools "$RUNTIME_PG_CONFIG")
|
|
233
|
+
install_bundle_on_db_host "$BUNDLE_NAME" "$BUILD_PG_CONFIG" "$RUNTIME_PG_CONFIG"
|
|
234
|
+
|
|
235
|
+
q "CREATE ROLE app_migrations LOGIN;
|
|
236
|
+
CREATE ROLE dbeaver_readonly LOGIN;
|
|
237
|
+
CREATE SCHEMA app;
|
|
238
|
+
CREATE SCHEMA admin;
|
|
239
|
+
CREATE TABLE app.events (id integer PRIMARY KEY, payload_compressed bytea NULL);
|
|
240
|
+
INSERT INTO app.events VALUES
|
|
241
|
+
(1, decode('$VALID_HEX', 'hex')),
|
|
242
|
+
(2, NULL),
|
|
243
|
+
(3, decode('$BIG_HEX', 'hex')),
|
|
244
|
+
(4, decode('$VALID_HEX', 'hex'));
|
|
245
|
+
GRANT USAGE ON SCHEMA app, admin TO app_migrations, dbeaver_readonly;
|
|
246
|
+
GRANT CREATE ON SCHEMA admin TO app_migrations;
|
|
247
|
+
GRANT SELECT ON app.events TO app_migrations;"
|
|
248
|
+
docker exec -u postgres -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" "$CONTAINER" sh -ceu '
|
|
249
|
+
cd "$MCDB_BUNDLE_ROOT"
|
|
250
|
+
make enable DB=postgres MIGRATION_ROLE=app_migrations READ_ROLE=dbeaver_readonly
|
|
251
|
+
'
|
|
252
|
+
apply_readable_view "$VIEW_SQL"
|
|
253
|
+
q 'GRANT SELECT ON admin.events_readable TO dbeaver_readonly;'
|
|
254
|
+
|
|
255
|
+
q "CREATE DATABASE mcdb_latin1 WITH TEMPLATE template0 ENCODING 'LATIN1' LC_COLLATE 'C' LC_CTYPE 'C';"
|
|
256
|
+
docker exec -u postgres -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" "$CONTAINER" sh -ceu '
|
|
257
|
+
cd "$MCDB_BUNDLE_ROOT"
|
|
258
|
+
if make enable DB=mcdb_latin1 MIGRATION_ROLE=app_migrations READ_ROLE=dbeaver_readonly >/tmp/mcdb-latin1.out 2>&1; then
|
|
259
|
+
echo "LATIN1 enable unexpectedly succeeded" >&2
|
|
260
|
+
exit 1
|
|
261
|
+
fi
|
|
262
|
+
grep -F "requires UTF8" /tmp/mcdb-latin1.out
|
|
263
|
+
'
|
|
264
|
+
q "CREATE DATABASE mcdb_other_schema WITH TEMPLATE template0 ENCODING 'UTF8';"
|
|
265
|
+
docker exec "$CONTAINER" psql -X -U postgres -d mcdb_other_schema -v ON_ERROR_STOP=1 -c 'CREATE SCHEMA wrong_schema; CREATE EXTENSION multi_compress WITH SCHEMA wrong_schema;' >/dev/null
|
|
266
|
+
docker exec -u postgres -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" "$CONTAINER" sh -ceu '
|
|
267
|
+
cd "$MCDB_BUNDLE_ROOT"
|
|
268
|
+
if make enable DB=mcdb_other_schema SCHEMA=multi_compress MIGRATION_ROLE=app_migrations READ_ROLE=dbeaver_readonly >/tmp/mcdb-schema.out 2>&1; then
|
|
269
|
+
echo "mismatched extension schema unexpectedly succeeded" >&2
|
|
270
|
+
exit 1
|
|
271
|
+
fi
|
|
272
|
+
grep -F "already installed in schema wrong_schema" /tmp/mcdb-schema.out
|
|
273
|
+
'
|
|
274
|
+
|
|
275
|
+
fails=0
|
|
276
|
+
check() {
|
|
277
|
+
if [ "$2" = "$3" ]; then echo " PASS $1"; else echo " FAIL $1 (got [$2] want [$3])"; fails=$((fails + 1)); fi
|
|
278
|
+
}
|
|
279
|
+
check_err() {
|
|
280
|
+
if qfail "$2"; then echo " PASS $1 (rejected)"; else echo " FAIL $1 (should have errored)"; fails=$((fails + 1)); fi
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
check "UTF8 database" "$(q 'SHOW server_encoding;' | tr -d '\r\n')" "UTF8"
|
|
284
|
+
check "deployment bundle version() smoke" "$(q 'SELECT strpos(multi_compress.multi_compress_db_version(), chr(77) || chr(67) || chr(68) || chr(66) || chr(49)) > 0;' | tr -d '\r\n')" "t"
|
|
285
|
+
check "PostgreSQL reader uses vendored zstd" "$(q "SELECT strpos(multi_compress.multi_compress_db_version(), 'zstd 1.5.7') > 0;" | tr -d '\r\n')" "t"
|
|
286
|
+
check "decode roundtrip" "$(q 'SELECT multi_compress.multi_compress_db_decompress(payload_compressed) FROM app.events WHERE id=1;' | tr -d '\r')" "$TEXT"
|
|
287
|
+
check "NULL -> NULL" "$(q 'SELECT multi_compress.multi_compress_db_decompress(payload_compressed) IS NULL FROM app.events WHERE id=2;' | tr -d '\r\n')" "t"
|
|
288
|
+
check "big (>255, >input)" "$(q 'SELECT octet_length(multi_compress.multi_compress_db_decompress(payload_compressed)) FROM app.events WHERE id=3;' | tr -d '\r\n')" "300"
|
|
289
|
+
check "generated readable view" "$(q 'SELECT count(*) FROM admin.events_readable WHERE id IN (1, 4);' | tr -d '\r\n')" "2"
|
|
290
|
+
check "read role reads generated Unicode view" "$(q_read 'SELECT payload FROM admin.events_readable WHERE id=1;' | tr -d '\r')" "$TEXT"
|
|
291
|
+
if q_read_fail 'SELECT payload_compressed FROM app.events WHERE id=1;'; then
|
|
292
|
+
echo " PASS read role cannot read source table"
|
|
293
|
+
else
|
|
294
|
+
echo " FAIL read role unexpectedly read source table"
|
|
295
|
+
fails=$((fails + 1))
|
|
296
|
+
fi
|
|
297
|
+
RUNTIME_MAJOR="$(docker exec "$CONTAINER" "$RUNTIME_PG_CONFIG" --version | awk '{print $2}' | cut -d. -f1 | tr -d '\r\n')"
|
|
298
|
+
SERVER_MAJOR="$(q 'SHOW server_version;' | cut -d. -f1 | tr -d '\r\n')"
|
|
299
|
+
check "connected server major matches PG_CONFIG" "$SERVER_MAJOR" "$RUNTIME_MAJOR"
|
|
300
|
+
check "is_valid true" "$(q 'SELECT multi_compress.multi_compress_db_is_valid(payload_compressed) FROM app.events WHERE id=1;' | tr -d '\r\n')" "t"
|
|
301
|
+
check "is_valid on NULL" "$(q 'SELECT multi_compress.multi_compress_db_is_valid(payload_compressed) IS NULL FROM app.events WHERE id=2;' | tr -d '\r\n')" "t"
|
|
302
|
+
|
|
303
|
+
for name in corrupt_magic corrupt_payload corrupt_crc invalid_utf8 nul_text trailing_skippable trailing_frame; do
|
|
304
|
+
hex=$(fixture_hex "test/fixtures/database_v1/${name}.mcdb")
|
|
305
|
+
check_err "$name errors" "SELECT multi_compress.multi_compress_db_decompress(decode('$hex', 'hex'));"
|
|
306
|
+
done
|
|
307
|
+
check "is_valid false (crc)" "$(q "SELECT multi_compress.multi_compress_db_is_valid(decode('$(fixture_hex test/fixtures/database_v1/corrupt_crc.mcdb)', 'hex'));" | tr -d '\r\n')" "f"
|
|
308
|
+
|
|
309
|
+
q 'DROP VIEW admin.events_readable; DROP EXTENSION multi_compress;'
|
|
310
|
+
docker exec "$CONTAINER" psql -X -U postgres -d mcdb_other_schema -v ON_ERROR_STOP=1 -c 'DROP EXTENSION multi_compress;' >/dev/null
|
|
311
|
+
docker exec -u 0 -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" -e "MCDB_RUNTIME_PG_CONFIG=$RUNTIME_PG_CONFIG" "$CONTAINER" sh -ceu '
|
|
312
|
+
cd "$MCDB_BUNDLE_ROOT"
|
|
313
|
+
if make uninstall RUNTIME_PG_CONFIG="$MCDB_RUNTIME_PG_CONFIG" >/dev/null 2>&1; then
|
|
314
|
+
echo "uninstall succeeded without CONFIRM" >&2
|
|
315
|
+
exit 1
|
|
316
|
+
fi
|
|
317
|
+
make uninstall CONFIRM=REMOVE_MULTI_COMPRESS RUNTIME_PG_CONFIG="$MCDB_RUNTIME_PG_CONFIG"
|
|
318
|
+
'
|
|
319
|
+
check_err "uninstall removes functions" "SELECT multi_compress.multi_compress_db_version();"
|
|
320
|
+
check_err "uninstall removes extension files" "CREATE EXTENSION multi_compress;"
|
|
321
|
+
|
|
322
|
+
if [ "$fails" -ne 0 ]; then echo "E2E FAILED ($fails)"; exit 1; fi
|
|
323
|
+
echo "E2E OK"
|