multi_compress 0.5.0 → 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 +35 -0
- data/GET_STARTED.md +19 -0
- data/README.md +14 -1
- data/db_core/include/mcdb_format.h +67 -9
- data/db_core/src/mcdb_format.c +392 -139
- data/db_deployment/mysql/README.md +19 -0
- data/db_deployment/mysql/bin/common +70 -18
- data/db_deployment/mysql/bin/enable +5 -1
- data/db_deployment/mysql/bin/status +2 -0
- data/db_deployment/mysql/bin/upgrade +17 -5
- data/db_deployment/postgres/README.md +24 -0
- data/db_deployment/postgres/bin/enable +2 -0
- data/db_deployment/postgres/bin/install +4 -2
- data/docs/database-envelope-v2.md +147 -0
- data/docs/rfcs/0002-mcdb2-dictionaries.md +75 -0
- data/ext/multi_compress/multi_compress.c +32 -0
- data/lib/multi_compress/database.rb +255 -49
- data/lib/multi_compress/db_deployment.rb +423 -11
- data/lib/multi_compress/version.rb +1 -1
- data/mysql_udf/README.md +45 -0
- data/mysql_udf/sql/examples.sql +5 -0
- data/mysql_udf/sql/install.sql +25 -1
- data/mysql_udf/sql/uninstall.sql +7 -1
- data/mysql_udf/sql/views.sql +16 -4
- data/mysql_udf/src/multi_compress_mysql.c +383 -44
- data/mysql_udf/test/run_e2e.sh +119 -1
- data/postgres_extension/Makefile +2 -2
- data/postgres_extension/README.md +48 -0
- data/postgres_extension/multi_compress.control +2 -2
- data/postgres_extension/multi_compress_pg.exports +12 -0
- data/postgres_extension/sql/examples.sql +11 -0
- data/postgres_extension/sql/multi_compress--0.5.0--0.6.0.sql +59 -0
- data/postgres_extension/sql/multi_compress--0.6.0.sql +85 -0
- data/postgres_extension/src/multi_compress_pg.c +301 -16
- data/postgres_extension/test/run_e2e.sh +90 -2
- metadata +6 -2
|
@@ -75,6 +75,23 @@ compress_hex() {
|
|
|
75
75
|
-e 'print MultiCompress::Database.compress(ENV.fetch("MCDB_TEXT")).unpack1("H*")'
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
mcdb2_fixture() {
|
|
79
|
+
MCDB_TEXT="$1" ruby -Ilib -r multi_compress -r multi_compress/database <<'RUBY'
|
|
80
|
+
samples = 256.times.map do |i|
|
|
81
|
+
%({"kind":"event","tenant":#{i % 8},"metadata":{"source":"worker","version":1,"name":"same-shape-#{i % 16}"},"payload":"#{"x" * (48 + i % 64)}"})
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
dictionary = MultiCompress::Database::Dictionary.train(samples, id: 42, size: 4096)
|
|
85
|
+
blob = MultiCompress::Database.compress(ENV.fetch("MCDB_TEXT"), dictionary: dictionary)
|
|
86
|
+
|
|
87
|
+
puts dictionary.id
|
|
88
|
+
puts dictionary.zstd_id
|
|
89
|
+
puts dictionary.sha256.unpack1("H*")
|
|
90
|
+
puts dictionary.bytes.unpack1("H*")
|
|
91
|
+
puts blob.unpack1("H*")
|
|
92
|
+
RUBY
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
bundle_name() {
|
|
79
96
|
ruby -I "$PACKAGED_GEM_ROOT/lib" -r multi_compress/version \
|
|
80
97
|
-e 'print "multi_compress-postgres-#{MultiCompress::VERSION}"'
|
|
@@ -187,6 +204,19 @@ apply_readable_view() {
|
|
|
187
204
|
docker exec -i "$CONTAINER" psql -X -U app_migrations -d postgres -v ON_ERROR_STOP=1 < "$path"
|
|
188
205
|
}
|
|
189
206
|
|
|
207
|
+
apply_dictionary_readable_view() {
|
|
208
|
+
local path="$1"
|
|
209
|
+
db_cli view postgres \
|
|
210
|
+
--table app.dictionary_events \
|
|
211
|
+
--column payload_compressed \
|
|
212
|
+
--dictionary-table app.mcdb_dictionary_versions \
|
|
213
|
+
--dictionary-id-column payload_dictionary_id \
|
|
214
|
+
--view admin.dictionary_events_readable \
|
|
215
|
+
--columns id \
|
|
216
|
+
--output "$path"
|
|
217
|
+
docker exec -i "$CONTAINER" psql -X -U app_migrations -d postgres -v ON_ERROR_STOP=1 < "$path"
|
|
218
|
+
}
|
|
219
|
+
|
|
190
220
|
ensure_ruby_extension
|
|
191
221
|
prepare_packaged_gem
|
|
192
222
|
build_deployment_bundle
|
|
@@ -199,9 +229,23 @@ TEXT='Привет, DBeaver! Сәлем! 🌍'
|
|
|
199
229
|
BIG=$(ruby -e 'print "a" * 300')
|
|
200
230
|
VALID_HEX=$(compress_hex "$TEXT")
|
|
201
231
|
BIG_HEX=$(compress_hex "$BIG")
|
|
232
|
+
MCDB2_FIXTURE_OUTPUT="$(mcdb2_fixture "$TEXT")"
|
|
233
|
+
readarray -t MCDB2_FIXTURE <<< "$MCDB2_FIXTURE_OUTPUT"
|
|
234
|
+
if [ "${#MCDB2_FIXTURE[@]}" -ne 5 ] || [ -z "${MCDB2_FIXTURE[0]}" ] || [ -z "${MCDB2_FIXTURE[4]}" ]; then
|
|
235
|
+
echo "MCDB2 fixture generator returned an invalid payload" >&2
|
|
236
|
+
exit 2
|
|
237
|
+
fi
|
|
238
|
+
DICT_REF="${MCDB2_FIXTURE[0]}"
|
|
239
|
+
DICT_ZSTD_ID="${MCDB2_FIXTURE[1]}"
|
|
240
|
+
DICT_SHA_HEX="${MCDB2_FIXTURE[2]}"
|
|
241
|
+
DICT_HEX="${MCDB2_FIXTURE[3]}"
|
|
242
|
+
DICT_BLOB_HEX="${MCDB2_FIXTURE[4]}"
|
|
243
|
+
TEXT_BYTES=$(MCDB_TEXT="$TEXT" ruby -e 'print ENV.fetch("MCDB_TEXT").bytesize')
|
|
202
244
|
BUNDLE_NAME="$(bundle_name)"
|
|
203
245
|
VIEW_SQL="$(mktemp -t multi-compress-postgres-view.XXXXXX.sql)"
|
|
204
|
-
|
|
246
|
+
DICT_VIEW_SQL="$(mktemp -t multi-compress-postgres-dictionary-view.XXXXXX.sql)"
|
|
247
|
+
REGISTRY_SQL="$(mktemp -t multi-compress-postgres-registry.XXXXXX.sql)"
|
|
248
|
+
trap 'rm -f "$VIEW_SQL" "$DICT_VIEW_SQL" "$REGISTRY_SQL"; cleanup' EXIT
|
|
205
249
|
|
|
206
250
|
docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
|
|
207
251
|
docker run --platform "$PLATFORM" -d --name "$CONTAINER" \
|
|
@@ -234,6 +278,7 @@ install_bundle_on_db_host "$BUNDLE_NAME" "$BUILD_PG_CONFIG" "$RUNTIME_PG_CONFIG"
|
|
|
234
278
|
|
|
235
279
|
q "CREATE ROLE app_migrations LOGIN;
|
|
236
280
|
CREATE ROLE dbeaver_readonly LOGIN;
|
|
281
|
+
CREATE ROLE mcdb_dictionary_owner NOLOGIN;
|
|
237
282
|
CREATE SCHEMA app;
|
|
238
283
|
CREATE SCHEMA admin;
|
|
239
284
|
CREATE TABLE app.events (id integer PRIMARY KEY, payload_compressed bytea NULL);
|
|
@@ -252,6 +297,28 @@ docker exec -u postgres -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" "$
|
|
|
252
297
|
apply_readable_view "$VIEW_SQL"
|
|
253
298
|
q 'GRANT SELECT ON admin.events_readable TO dbeaver_readonly;'
|
|
254
299
|
|
|
300
|
+
# MCDB2: package-provided registry DDL, immutable dictionary row, generated INNER JOIN view.
|
|
301
|
+
q "CREATE TABLE app.dictionary_events (
|
|
302
|
+
id integer PRIMARY KEY,
|
|
303
|
+
payload_compressed bytea NOT NULL,
|
|
304
|
+
payload_dictionary_id bigint NOT NULL
|
|
305
|
+
);
|
|
306
|
+
GRANT SELECT ON app.dictionary_events TO app_migrations;"
|
|
307
|
+
db_cli registry postgres \
|
|
308
|
+
--schema app --owner mcdb_dictionary_owner --migration-role app_migrations \
|
|
309
|
+
--payload-table dictionary_events --payload-column payload_compressed \
|
|
310
|
+
--payload-dictionary-id-column payload_dictionary_id \
|
|
311
|
+
--output "$REGISTRY_SQL"
|
|
312
|
+
docker exec -i "$CONTAINER" psql -X -U postgres -d postgres -v ON_ERROR_STOP=1 < "$REGISTRY_SQL"
|
|
313
|
+
q "
|
|
314
|
+
INSERT INTO app.mcdb_dictionary_versions (id, family, zstd_dict_id, sha256, bytes)
|
|
315
|
+
VALUES ($DICT_REF, 'events_payload_v1', $DICT_ZSTD_ID, decode('$DICT_SHA_HEX', 'hex'), decode('$DICT_HEX', 'hex'));
|
|
316
|
+
INSERT INTO app.dictionary_events (id, payload_compressed, payload_dictionary_id)
|
|
317
|
+
SELECT g, decode('$DICT_BLOB_HEX', 'hex'), $DICT_REF FROM generate_series(1, 1000) AS g;
|
|
318
|
+
ANALYZE app.dictionary_events;"
|
|
319
|
+
apply_dictionary_readable_view "$DICT_VIEW_SQL"
|
|
320
|
+
q 'GRANT SELECT ON admin.dictionary_events_readable TO dbeaver_readonly;'
|
|
321
|
+
|
|
255
322
|
q "CREATE DATABASE mcdb_latin1 WITH TEMPLATE template0 ENCODING 'LATIN1' LC_COLLATE 'C' LC_CTYPE 'C';"
|
|
256
323
|
docker exec -u postgres -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" "$CONTAINER" sh -ceu '
|
|
257
324
|
cd "$MCDB_BUNDLE_ROOT"
|
|
@@ -305,8 +372,29 @@ for name in corrupt_magic corrupt_payload corrupt_crc invalid_utf8 nul_text trai
|
|
|
305
372
|
check_err "$name errors" "SELECT multi_compress.multi_compress_db_decompress(decode('$hex', 'hex'));"
|
|
306
373
|
done
|
|
307
374
|
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"
|
|
375
|
+
check "MCDB2 registry owner has schema usage" "$(q "SELECT has_schema_privilege('mcdb_dictionary_owner', 'app', 'USAGE');" | tr -d '\r\n')" "t"
|
|
376
|
+
check "MCDB2 dictionary reference" "$(q 'SELECT multi_compress.multi_compress_db_dictionary_ref(payload_compressed) FROM app.dictionary_events WHERE id=1;' | tr -d '\r\n')" "$DICT_REF"
|
|
377
|
+
check "MCDB2 original size" "$(q 'SELECT multi_compress.multi_compress_db_original_size(payload_compressed) FROM app.dictionary_events WHERE id=1;' | tr -d '\r\n')" "$TEXT_BYTES"
|
|
378
|
+
check "MCDB2 dictionary view roundtrip" "$(q_read 'SELECT payload FROM admin.dictionary_events_readable WHERE id=1;' | tr -d '\r')" "$TEXT"
|
|
379
|
+
check "MCDB2 dictionary is valid" "$(q "SELECT multi_compress.multi_compress_db_is_valid_dict(e.payload_compressed, e.payload_dictionary_id, d.sha256, d.bytes) FROM app.dictionary_events e JOIN app.mcdb_dictionary_versions d ON d.id = e.payload_dictionary_id WHERE e.id=1;" | tr -d '\r\n')" "t"
|
|
380
|
+
check_err "MCDB2 payload/FK mismatch rejected" "INSERT INTO app.dictionary_events VALUES (1001, decode('$DICT_BLOB_HEX', 'hex'), $((DICT_REF + 1)));"
|
|
381
|
+
check "MCDB2 rejects wrong dictionary digest" "$(q "SELECT multi_compress.multi_compress_db_is_valid_dict(e.payload_compressed, e.payload_dictionary_id, decode(repeat('00', 32), 'hex'), d.bytes) FROM app.dictionary_events e JOIN app.mcdb_dictionary_versions d ON d.id = e.payload_dictionary_id WHERE e.id=1;" | tr -d '\r\n')" "f"
|
|
382
|
+
if q_read_fail 'SELECT bytes FROM app.mcdb_dictionary_versions WHERE id=42;'; then
|
|
383
|
+
echo " PASS read role cannot read dictionary registry"
|
|
384
|
+
else
|
|
385
|
+
echo " FAIL read role unexpectedly read dictionary registry"
|
|
386
|
+
fails=$((fails + 1))
|
|
387
|
+
fi
|
|
388
|
+
PG_DICT_PLAN="$(q 'SET enable_seqscan = off; EXPLAIN (COSTS OFF) SELECT id, payload FROM admin.dictionary_events_readable WHERE id >= 3 ORDER BY id LIMIT 100;')"
|
|
389
|
+
if printf '%s\n' "$PG_DICT_PLAN" | grep -Eq 'Index Scan|Index Only Scan'; then
|
|
390
|
+
echo " PASS MCDB2 view preserves indexed LIMIT plan"
|
|
391
|
+
else
|
|
392
|
+
echo " FAIL MCDB2 view did not expose an indexed LIMIT plan"
|
|
393
|
+
printf '%s\n' "$PG_DICT_PLAN" >&2
|
|
394
|
+
fails=$((fails + 1))
|
|
395
|
+
fi
|
|
308
396
|
|
|
309
|
-
q 'DROP VIEW admin.events_readable; DROP EXTENSION multi_compress;'
|
|
397
|
+
q 'DROP VIEW admin.events_readable; DROP VIEW admin.dictionary_events_readable; DROP EXTENSION multi_compress;'
|
|
310
398
|
docker exec "$CONTAINER" psql -X -U postgres -d mcdb_other_schema -v ON_ERROR_STOP=1 -c 'DROP EXTENSION multi_compress;' >/dev/null
|
|
311
399
|
docker exec -u 0 -e "MCDB_BUNDLE_ROOT=${BUNDLE_PARENT}/${BUNDLE_NAME}" -e "MCDB_RUNTIME_PG_CONFIG=$RUNTIME_PG_CONFIG" "$CONTAINER" sh -ceu '
|
|
312
400
|
cd "$MCDB_BUNDLE_ROOT"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: multi_compress
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Haydarov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -104,6 +104,8 @@ files:
|
|
|
104
104
|
- db_deployment/postgres/bin/uninstall
|
|
105
105
|
- db_deployment/postgres/bin/verify
|
|
106
106
|
- docs/database-envelope-v1.md
|
|
107
|
+
- docs/database-envelope-v2.md
|
|
108
|
+
- docs/rfcs/0002-mcdb2-dictionaries.md
|
|
107
109
|
- exe/multi_compress
|
|
108
110
|
- ext/multi_compress/brotli_dec_static_init.c
|
|
109
111
|
- ext/multi_compress/brotli_enc_static_init.c
|
|
@@ -364,7 +366,9 @@ files:
|
|
|
364
366
|
- postgres_extension/multi_compress.control
|
|
365
367
|
- postgres_extension/multi_compress_pg.exports
|
|
366
368
|
- postgres_extension/sql/examples.sql
|
|
369
|
+
- postgres_extension/sql/multi_compress--0.5.0--0.6.0.sql
|
|
367
370
|
- postgres_extension/sql/multi_compress--0.5.0.sql
|
|
371
|
+
- postgres_extension/sql/multi_compress--0.6.0.sql
|
|
368
372
|
- postgres_extension/sql/uninstall.sql
|
|
369
373
|
- postgres_extension/sql/views.sql
|
|
370
374
|
- postgres_extension/src/mcdb_format.c
|