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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -0
  3. data/GET_STARTED.md +207 -0
  4. data/README.md +69 -0
  5. data/db_core/include/mcdb_format.h +113 -0
  6. data/db_core/src/mcdb_format.c +594 -0
  7. data/db_deployment/mysql/Makefile +48 -0
  8. data/db_deployment/mysql/README.md +61 -0
  9. data/db_deployment/mysql/bin/common +212 -0
  10. data/db_deployment/mysql/bin/disable +40 -0
  11. data/db_deployment/mysql/bin/doctor +64 -0
  12. data/db_deployment/mysql/bin/enable +61 -0
  13. data/db_deployment/mysql/bin/install +64 -0
  14. data/db_deployment/mysql/bin/status +67 -0
  15. data/db_deployment/mysql/bin/uninstall +47 -0
  16. data/db_deployment/mysql/bin/upgrade +127 -0
  17. data/db_deployment/mysql/bin/verify +14 -0
  18. data/db_deployment/postgres/Makefile +45 -0
  19. data/db_deployment/postgres/README.md +84 -0
  20. data/db_deployment/postgres/bin/doctor +61 -0
  21. data/db_deployment/postgres/bin/enable +138 -0
  22. data/db_deployment/postgres/bin/install +106 -0
  23. data/db_deployment/postgres/bin/uninstall +54 -0
  24. data/db_deployment/postgres/bin/verify +14 -0
  25. data/docs/database-envelope-v1.md +159 -0
  26. data/docs/database-envelope-v2.md +147 -0
  27. data/docs/rfcs/0002-mcdb2-dictionaries.md +75 -0
  28. data/exe/multi_compress +228 -0
  29. data/ext/multi_compress/extconf.rb +4 -0
  30. data/ext/multi_compress/multi_compress.c +62 -0
  31. data/lib/multi_compress/active_record.rb +61 -0
  32. data/lib/multi_compress/codec.rb +164 -0
  33. data/lib/multi_compress/database.rb +312 -0
  34. data/lib/multi_compress/db_deployment.rb +868 -0
  35. data/lib/multi_compress/version.rb +1 -1
  36. data/multi_compress.gemspec +55 -0
  37. data/mysql_udf/Makefile +96 -0
  38. data/mysql_udf/README.md +140 -0
  39. data/mysql_udf/sql/examples.sql +16 -0
  40. data/mysql_udf/sql/install.sql +39 -0
  41. data/mysql_udf/sql/uninstall.sql +10 -0
  42. data/mysql_udf/sql/views.sql +20 -0
  43. data/mysql_udf/src/multi_compress_mysql.c +472 -0
  44. data/mysql_udf/src/mysql_udf_abi_57.h +34 -0
  45. data/mysql_udf/test/mcdb_cli.c +60 -0
  46. data/mysql_udf/test/run_e2e.sh +414 -0
  47. data/postgres_extension/Makefile +74 -0
  48. data/postgres_extension/README.md +160 -0
  49. data/postgres_extension/multi_compress.control +4 -0
  50. data/postgres_extension/multi_compress_pg.exports +24 -0
  51. data/postgres_extension/sql/examples.sql +23 -0
  52. data/postgres_extension/sql/multi_compress--0.5.0--0.6.0.sql +59 -0
  53. data/postgres_extension/sql/multi_compress--0.5.0.sql +24 -0
  54. data/postgres_extension/sql/multi_compress--0.6.0.sql +85 -0
  55. data/postgres_extension/sql/uninstall.sql +2 -0
  56. data/postgres_extension/sql/views.sql +7 -0
  57. data/postgres_extension/src/mcdb_format.c +2 -0
  58. data/postgres_extension/src/multi_compress_pg.c +381 -0
  59. data/postgres_extension/test/run_e2e.sh +411 -0
  60. metadata +56 -3
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env bash
2
+
3
+ MCDB_UDF_LIBRARY='multi_compress_mysql.so'
4
+ MCDB_V1_UDF_NAMES=(
5
+ multi_compress_db_version
6
+ multi_compress_db_is_valid
7
+ multi_compress_db_decompress
8
+ )
9
+ MCDB_EMPTY_ENVELOPE_HEX='4d43444201010000000000000000000000000028b52ffd2000010000'
10
+ MCDB_UDF_NAMES=(
11
+ multi_compress_db_version
12
+ multi_compress_db_is_valid
13
+ multi_compress_db_decompress
14
+ multi_compress_db_original_size
15
+ multi_compress_db_dictionary_ref
16
+ multi_compress_db_dictionary_zstd_id
17
+ multi_compress_db_dictionary_sha256
18
+ multi_compress_db_is_valid_dict
19
+ multi_compress_db_decompress_dict
20
+ )
21
+
22
+ mcdb_die() {
23
+ echo "multi_compress mysql: $*" >&2
24
+ exit 2
25
+ }
26
+
27
+ mcdb_resolve_mysql() {
28
+ if [[ "$MYSQL" == */* ]]; then
29
+ [ -x "$MYSQL" ] || mcdb_die "mysql client is not executable: $MYSQL"
30
+ else
31
+ MYSQL="$(command -v "$MYSQL" || true)"
32
+ [ -n "$MYSQL" ] || mcdb_die "mysql client was not found"
33
+ fi
34
+ }
35
+
36
+ mcdb_mysql_args() {
37
+ MYSQL_ARGS=(--batch --skip-column-names --raw --max_allowed_packet=32M --protocol=SOCKET)
38
+ if [ -n "$DEFAULTS_FILE" ]; then
39
+ [ -f "$DEFAULTS_FILE" ] || mcdb_die "defaults file does not exist: $DEFAULTS_FILE"
40
+ MYSQL_ARGS=("--defaults-extra-file=$DEFAULTS_FILE" "${MYSQL_ARGS[@]}")
41
+ fi
42
+ if [ -n "${MYSQL_SOCKET:-}" ]; then
43
+ MYSQL_ARGS+=("--socket=$MYSQL_SOCKET")
44
+ fi
45
+ }
46
+
47
+ mcdb_query() {
48
+ "$MYSQL" "${MYSQL_ARGS[@]}" -e "$1"
49
+ }
50
+
51
+ mcdb_server_version() {
52
+ mcdb_query 'SELECT VERSION();' | tr -d '\r\n'
53
+ }
54
+
55
+ mcdb_require_mysql57() {
56
+ local version
57
+ version="$(mcdb_server_version)"
58
+ case "$version" in
59
+ 5.7.*) ;;
60
+ *) mcdb_die "this bundle targets MySQL 5.7; server reports $version" ;;
61
+ esac
62
+ }
63
+
64
+ mcdb_plugin_dir() {
65
+ mcdb_query 'SELECT @@plugin_dir;' | tr -d '\r\n'
66
+ }
67
+
68
+ mcdb_server_socket() {
69
+ mcdb_query 'SELECT @@socket;' | tr -d '\r\n'
70
+ }
71
+
72
+ mcdb_server_hostname() {
73
+ mcdb_query 'SELECT @@hostname;' | tr -d '\r\n'
74
+ }
75
+
76
+ mcdb_server_max_allowed_packet() {
77
+ mcdb_query 'SELECT @@GLOBAL.max_allowed_packet;' | tr -d '\r\n'
78
+ }
79
+
80
+ mcdb_require_expected_socket() {
81
+ local actual_socket
82
+ actual_socket="$(mcdb_server_socket)"
83
+ [ -n "$actual_socket" ] || mcdb_die 'MySQL returned an empty @@socket'
84
+ if [ -n "${MYSQL_SOCKET:-}" ] && [ "$MYSQL_SOCKET" != "$actual_socket" ]; then
85
+ mcdb_die "connected server socket $actual_socket does not match MYSQL_SOCKET $MYSQL_SOCKET"
86
+ fi
87
+ }
88
+
89
+ mcdb_read_udf_state() {
90
+ local rows name dl
91
+
92
+ MCDB_EXPECTED_OWNED=0
93
+ MCDB_FOREIGN_EXPECTED=0
94
+ MCDB_UNEXPECTED_LIBRARY=0
95
+ MCDB_REGISTERED_ROWS=()
96
+
97
+ local names_sql=""
98
+ local expected=()
99
+ for name in "${MCDB_UDF_NAMES[@]}"; do
100
+ expected+=("'${name}'")
101
+ done
102
+ names_sql="$(IFS=,; printf '%s' "${expected[*]}")"
103
+
104
+ rows="$(mcdb_query "SELECT name, dl FROM mysql.func WHERE dl = '${MCDB_UDF_LIBRARY}' OR name IN (${names_sql}) ORDER BY name;")"
105
+ while IFS=$'\t' read -r name dl; do
106
+ [ -n "$name" ] || continue
107
+ MCDB_REGISTERED_ROWS+=("$name"$'\t'"$dl")
108
+ local expected_name=0
109
+ local candidate
110
+ for candidate in "${MCDB_UDF_NAMES[@]}"; do
111
+ if [ "$candidate" = "$name" ]; then expected_name=1; break; fi
112
+ done
113
+ if [ "$expected_name" -eq 1 ]; then
114
+ if [ "$dl" = "$MCDB_UDF_LIBRARY" ]; then
115
+ MCDB_EXPECTED_OWNED=$((MCDB_EXPECTED_OWNED + 1))
116
+ else
117
+ MCDB_FOREIGN_EXPECTED=$((MCDB_FOREIGN_EXPECTED + 1))
118
+ fi
119
+ elif [ "$dl" = "$MCDB_UDF_LIBRARY" ]; then
120
+ MCDB_UNEXPECTED_LIBRARY=$((MCDB_UNEXPECTED_LIBRARY + 1))
121
+ fi
122
+ done <<< "$rows"
123
+ }
124
+
125
+ mcdb_state_is_clean_absent() {
126
+ [ "$MCDB_EXPECTED_OWNED" -eq 0 ] && [ "$MCDB_FOREIGN_EXPECTED" -eq 0 ] && [ "$MCDB_UNEXPECTED_LIBRARY" -eq 0 ]
127
+ }
128
+
129
+ mcdb_state_is_clean_enabled() {
130
+ [ "$MCDB_EXPECTED_OWNED" -eq "${#MCDB_UDF_NAMES[@]}" ] && [ "$MCDB_FOREIGN_EXPECTED" -eq 0 ] && [ "$MCDB_UNEXPECTED_LIBRARY" -eq 0 ]
131
+ }
132
+
133
+ mcdb_has_owned_registration() {
134
+ local target="$1" registration name dl
135
+ for registration in "${MCDB_REGISTERED_ROWS[@]}"; do
136
+ IFS=$'\t' read -r name dl <<< "$registration"
137
+ [ "$name" = "$target" ] && [ "$dl" = "$MCDB_UDF_LIBRARY" ] && return 0
138
+ done
139
+ return 1
140
+ }
141
+
142
+ # A 0.5.x host has only the three MCDB1 UDF registrations. It is a known,
143
+ # upgradeable state, not a partial installation. The 0.6 upgrade replaces the
144
+ # library using MySQL's required DROP -> replace -> CREATE order and registers
145
+ # the full MCDB1/MCDB2 surface.
146
+ mcdb_state_is_clean_v1_enabled() {
147
+ local name
148
+ [ "$MCDB_EXPECTED_OWNED" -eq "${#MCDB_V1_UDF_NAMES[@]}" ] || return 1
149
+ [ "$MCDB_FOREIGN_EXPECTED" -eq 0 ] || return 1
150
+ [ "$MCDB_UNEXPECTED_LIBRARY" -eq 0 ] || return 1
151
+ for name in "${MCDB_V1_UDF_NAMES[@]}"; do
152
+ mcdb_has_owned_registration "$name" || return 1
153
+ done
154
+ return 0
155
+ }
156
+
157
+ mcdb_require_clean_owned_state() {
158
+ if [ "$MCDB_FOREIGN_EXPECTED" -ne 0 ]; then
159
+ mcdb_die 'one or more expected UDF names are registered to a foreign SONAME; refusing to modify them'
160
+ fi
161
+ if [ "$MCDB_UNEXPECTED_LIBRARY" -ne 0 ]; then
162
+ mcdb_die "unexpected registrations reference ${MCDB_UDF_LIBRARY}; refusing to modify the shared library"
163
+ fi
164
+ }
165
+
166
+ mcdb_v1_smoke_check() {
167
+ local version decoded
168
+ version="$(mcdb_query 'SELECT multi_compress_db_version();' | tr -d '\r\n')"
169
+ case "$version" in
170
+ *'MCDB1'*) ;;
171
+ *) mcdb_die "MCDB1 version() smoke check failed: $version" ;;
172
+ esac
173
+ decoded="$(mcdb_query "SELECT multi_compress_db_decompress(UNHEX('${MCDB_EMPTY_ENVELOPE_HEX}')) = '';" | tr -d '\r\n')"
174
+ [ "$decoded" = '1' ] || mcdb_die 'decode smoke check failed'
175
+ }
176
+
177
+ mcdb_smoke_check() {
178
+ local version
179
+ mcdb_v1_smoke_check
180
+ version="$(mcdb_query 'SELECT multi_compress_db_version();' | tr -d '\r\n')"
181
+ case "$version" in
182
+ *'MCDB1'*'MCDB2'*) ;;
183
+ *) mcdb_die "MCDB1/MCDB2 version() smoke check failed: $version" ;;
184
+ esac
185
+ }
186
+
187
+ mcdb_drop_owned_udfs() {
188
+ mcdb_read_udf_state
189
+ mcdb_require_clean_owned_state
190
+ for name in "${MCDB_UDF_NAMES[@]}"; do
191
+ if mcdb_query "SELECT COUNT(*) FROM mysql.func WHERE name = '${name}' AND dl = '${MCDB_UDF_LIBRARY}';" | tr -d '\r\n' | grep -qx '1'; then
192
+ mcdb_query "DROP FUNCTION ${name};"
193
+ fi
194
+ done
195
+ }
196
+
197
+ mcdb_register_expected_udfs() {
198
+ "$MYSQL" "${MYSQL_ARGS[@]}" < "$ROOT/mysql_udf/sql/install.sql"
199
+ }
200
+
201
+ mcdb_library_checksum() {
202
+ local library="$1"
203
+ if [ ! -f "$library" ]; then
204
+ printf '%s\n' 'missing'
205
+ elif command -v sha256sum >/dev/null 2>&1; then
206
+ sha256sum "$library" | awk '{print $1}'
207
+ elif command -v shasum >/dev/null 2>&1; then
208
+ shasum -a 256 "$library" | awk '{print $1}'
209
+ else
210
+ printf '%s\n' 'unavailable (sha256sum or shasum is required)'
211
+ fi
212
+ }
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/disable [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Removes only MultiCompress-owned UDF registrations and leaves the native library in place.
14
+ TEXT
15
+ }
16
+
17
+ while [ "$#" -gt 0 ]; do
18
+ case "$1" in
19
+ --mysql)
20
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
21
+ MYSQL="$2"; shift 2 ;;
22
+ --defaults-extra-file)
23
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
24
+ DEFAULTS_FILE="$2"; shift 2 ;;
25
+ --help|-h)
26
+ usage; exit 0 ;;
27
+ *)
28
+ echo "multi_compress mysql disable: unknown option: $1" >&2; exit 2 ;;
29
+ esac
30
+ done
31
+
32
+ # shellcheck source=common
33
+ source "$ROOT/bin/common"
34
+ mcdb_resolve_mysql
35
+ mcdb_mysql_args
36
+ mcdb_require_mysql57
37
+ mcdb_require_expected_socket
38
+ mcdb_drop_owned_udfs
39
+
40
+ echo 'MultiCompress MySQL UDFs are disabled.'
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/doctor [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Run this on the MySQL 5.7 host. The bundle uses a local Unix socket so the UDF
14
+ library is installed into the plugin_dir of that same mysqld.
15
+ TEXT
16
+ }
17
+
18
+ while [ "$#" -gt 0 ]; do
19
+ case "$1" in
20
+ --mysql)
21
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
22
+ MYSQL="$2"; shift 2 ;;
23
+ --defaults-extra-file)
24
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
25
+ DEFAULTS_FILE="$2"; shift 2 ;;
26
+ --help|-h)
27
+ usage; exit 0 ;;
28
+ *)
29
+ echo "multi_compress mysql doctor: unknown option: $1" >&2; exit 2 ;;
30
+ esac
31
+ done
32
+
33
+ # shellcheck source=common
34
+ source "$ROOT/bin/common"
35
+ mcdb_resolve_mysql
36
+ mcdb_mysql_args
37
+ command -v make >/dev/null 2>&1 || mcdb_die 'make is required'
38
+ command -v cc >/dev/null 2>&1 || mcdb_die 'a C compiler is required (cc/gcc/clang)'
39
+ command -v ar >/dev/null 2>&1 || mcdb_die 'ar is required'
40
+ mcdb_require_mysql57
41
+ mcdb_require_expected_socket
42
+
43
+ PLUGIN_DIR="$(mcdb_plugin_dir)"
44
+ SERVER_SOCKET="$(mcdb_server_socket)"
45
+ SERVER_HOSTNAME="$(mcdb_server_hostname)"
46
+ MAX_ALLOWED_PACKET="$(mcdb_server_max_allowed_packet)"
47
+ [ -n "$PLUGIN_DIR" ] || mcdb_die 'MySQL returned an empty @@plugin_dir'
48
+ [ -d "$PLUGIN_DIR" ] || mcdb_die "@@plugin_dir is not a local directory: $PLUGIN_DIR"
49
+ case "$MAX_ALLOWED_PACKET" in
50
+ ''|*[!0-9]*) mcdb_die "MySQL returned an invalid @@GLOBAL.max_allowed_packet: $MAX_ALLOWED_PACKET" ;;
51
+ esac
52
+ if [ "$MAX_ALLOWED_PACKET" -lt 33554432 ]; then
53
+ echo "multi_compress mysql doctor: WARNING @@GLOBAL.max_allowed_packet is ${MAX_ALLOWED_PACKET}; configure at least 32M for the 16 MiB MCDB1 limit" >&2
54
+ fi
55
+
56
+ printf '%s\n' \
57
+ "MySQL: $(mcdb_server_version)" \
58
+ "mysql client: $MYSQL" \
59
+ "server hostname: $SERVER_HOSTNAME" \
60
+ "server socket: $SERVER_SOCKET" \
61
+ "plugin directory: $PLUGIN_DIR" \
62
+ "max_allowed_packet: $MAX_ALLOWED_PACKET" \
63
+ "bundle: $ROOT" \
64
+ 'doctor: OK'
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/enable [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Registers the MultiCompress MCDB1/MCDB2 UDFs once. Existing healthy registrations are
14
+ left untouched; this command never drops functions automatically.
15
+ TEXT
16
+ }
17
+
18
+ while [ "$#" -gt 0 ]; do
19
+ case "$1" in
20
+ --mysql)
21
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
22
+ MYSQL="$2"; shift 2 ;;
23
+ --defaults-extra-file)
24
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
25
+ DEFAULTS_FILE="$2"; shift 2 ;;
26
+ --help|-h)
27
+ usage; exit 0 ;;
28
+ *)
29
+ echo "multi_compress mysql enable: unknown option: $1" >&2; exit 2 ;;
30
+ esac
31
+ done
32
+
33
+ # shellcheck source=common
34
+ source "$ROOT/bin/common"
35
+ mcdb_resolve_mysql
36
+ mcdb_mysql_args
37
+ mcdb_require_mysql57
38
+ mcdb_require_expected_socket
39
+ mcdb_read_udf_state
40
+
41
+ if mcdb_state_is_clean_absent; then
42
+ mcdb_register_expected_udfs
43
+ mcdb_read_udf_state
44
+ mcdb_state_is_clean_enabled || mcdb_die 'registration did not create the expected UDF state'
45
+ mcdb_smoke_check
46
+ echo 'MultiCompress MySQL UDFs are enabled. Apply the generated readable-view SQL in the application migration.'
47
+ exit 0
48
+ fi
49
+
50
+ if mcdb_state_is_clean_enabled; then
51
+ mcdb_smoke_check
52
+ echo 'MultiCompress MySQL UDFs are already enabled; no changes made.'
53
+ exit 0
54
+ fi
55
+
56
+ if mcdb_state_is_clean_v1_enabled; then
57
+ mcdb_die 'MCDB1-only UDF registrations were found; run make upgrade to install the MCDB2 reader surface'
58
+ fi
59
+
60
+ mcdb_require_clean_owned_state
61
+ mcdb_die 'UDF registrations are partial; run make status and resolve the state before enabling'
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: sudo MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/install [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Builds and installs a fresh MySQL 5.7 UDF library. It refuses to replace a
14
+ library while any expected UDF is registered; use ./bin/upgrade for upgrades.
15
+ TEXT
16
+ }
17
+
18
+ while [ "$#" -gt 0 ]; do
19
+ case "$1" in
20
+ --mysql)
21
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
22
+ MYSQL="$2"; shift 2 ;;
23
+ --defaults-extra-file)
24
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
25
+ DEFAULTS_FILE="$2"; shift 2 ;;
26
+ --help|-h)
27
+ usage; exit 0 ;;
28
+ *)
29
+ echo "multi_compress mysql install: unknown option: $1" >&2; exit 2 ;;
30
+ esac
31
+ done
32
+
33
+ [ "${EUID:-$(id -u)}" -eq 0 ] || { echo 'multi_compress mysql install: run as root: sudo ./bin/install ...' >&2; exit 2; }
34
+ DOCTOR_ARGS=(--mysql "$MYSQL")
35
+ if [ -n "$DEFAULTS_FILE" ]; then
36
+ DOCTOR_ARGS+=(--defaults-extra-file "$DEFAULTS_FILE")
37
+ fi
38
+ MYSQL_SOCKET="$MYSQL_SOCKET" "$ROOT/bin/doctor" "${DOCTOR_ARGS[@]}"
39
+
40
+ # shellcheck source=common
41
+ source "$ROOT/bin/common"
42
+ mcdb_resolve_mysql
43
+ mcdb_mysql_args
44
+ mcdb_read_udf_state
45
+ if ! mcdb_state_is_clean_absent; then
46
+ mcdb_die 'fresh install refuses to replace a library while UDF registrations exist; run status or upgrade instead'
47
+ fi
48
+
49
+ PLUGIN_DIR="$(mcdb_plugin_dir)"
50
+ TEMP_LIBRARY="$PLUGIN_DIR/.${MCDB_UDF_LIBRARY}.$$.$RANDOM.tmp"
51
+ trap 'rm -f "$TEMP_LIBRARY"' EXIT
52
+
53
+ make -C "$ROOT/mysql_udf" clean udf MYSQL_UDF_ABI=mysql57
54
+ install -d -m 0755 "$PLUGIN_DIR"
55
+ install -m 0755 "$ROOT/mysql_udf/build/$MCDB_UDF_LIBRARY" "$TEMP_LIBRARY"
56
+ mv -f "$TEMP_LIBRARY" "$PLUGIN_DIR/$MCDB_UDF_LIBRARY"
57
+ trap - EXIT
58
+
59
+ printf '%s\n' \
60
+ 'MultiCompress MySQL UDF library installed.' \
61
+ " library: $PLUGIN_DIR/$MCDB_UDF_LIBRARY" \
62
+ '' \
63
+ 'Register the UDFs once on this MySQL server:' \
64
+ ' make enable'
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/status [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Reports MultiCompress UDF registrations. This command needs SELECT on mysql.func.
14
+ TEXT
15
+ }
16
+
17
+ while [ "$#" -gt 0 ]; do
18
+ case "$1" in
19
+ --mysql)
20
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
21
+ MYSQL="$2"; shift 2 ;;
22
+ --defaults-extra-file)
23
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
24
+ DEFAULTS_FILE="$2"; shift 2 ;;
25
+ --help|-h)
26
+ usage; exit 0 ;;
27
+ *)
28
+ echo "multi_compress mysql status: unknown option: $1" >&2; exit 2 ;;
29
+ esac
30
+ done
31
+
32
+ # shellcheck source=common
33
+ source "$ROOT/bin/common"
34
+ mcdb_resolve_mysql
35
+ mcdb_mysql_args
36
+ mcdb_require_mysql57
37
+ mcdb_require_expected_socket
38
+ PLUGIN_DIR="$(mcdb_plugin_dir)"
39
+ LIBRARY="$PLUGIN_DIR/$MCDB_UDF_LIBRARY"
40
+ mcdb_read_udf_state
41
+
42
+ printf '%s\n' \
43
+ "MySQL: $(mcdb_server_version)" \
44
+ "server hostname: $(mcdb_server_hostname)" \
45
+ "server socket: $(mcdb_server_socket)" \
46
+ "plugin directory: $PLUGIN_DIR" \
47
+ "library: $LIBRARY" \
48
+ "library sha256: $(mcdb_library_checksum "$LIBRARY")"
49
+
50
+ if [ "${#MCDB_REGISTERED_ROWS[@]}" -eq 0 ]; then
51
+ echo 'registrations: none'
52
+ else
53
+ echo 'registrations:'
54
+ printf ' %s\n' "${MCDB_REGISTERED_ROWS[@]}"
55
+ fi
56
+
57
+ if mcdb_state_is_clean_enabled; then
58
+ mcdb_smoke_check
59
+ echo 'state: enabled'
60
+ elif mcdb_state_is_clean_v1_enabled; then
61
+ echo 'state: MCDB1-only; run make upgrade before writing MCDB2 values'
62
+ elif mcdb_state_is_clean_absent; then
63
+ echo 'state: absent'
64
+ else
65
+ echo 'state: inconsistent or foreign registration detected'
66
+ exit 2
67
+ fi
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: sudo MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/uninstall [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Removes MultiCompress-owned UDF registrations and its native library. The Makefile
14
+ requires CONFIRM=REMOVE_MULTI_COMPRESS before this script can run.
15
+ TEXT
16
+ }
17
+
18
+ while [ "$#" -gt 0 ]; do
19
+ case "$1" in
20
+ --mysql)
21
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
22
+ MYSQL="$2"; shift 2 ;;
23
+ --defaults-extra-file)
24
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
25
+ DEFAULTS_FILE="$2"; shift 2 ;;
26
+ --help|-h)
27
+ usage; exit 0 ;;
28
+ *)
29
+ echo "multi_compress mysql uninstall: unknown option: $1" >&2; exit 2 ;;
30
+ esac
31
+ done
32
+
33
+ [ "${EUID:-$(id -u)}" -eq 0 ] || { echo 'multi_compress mysql uninstall: run as root' >&2; exit 2; }
34
+
35
+ # shellcheck source=common
36
+ source "$ROOT/bin/common"
37
+ mcdb_resolve_mysql
38
+ mcdb_mysql_args
39
+ mcdb_require_mysql57
40
+ mcdb_require_expected_socket
41
+ mcdb_read_udf_state
42
+ mcdb_require_clean_owned_state
43
+ PLUGIN_DIR="$(mcdb_plugin_dir)"
44
+ mcdb_drop_owned_udfs
45
+ rm -f "$PLUGIN_DIR/$MCDB_UDF_LIBRARY"
46
+
47
+ echo "MultiCompress MySQL UDFs disabled and library removed from $PLUGIN_DIR."
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ MYSQL="${MYSQL:-mysql}"
6
+ DEFAULTS_FILE=""
7
+ MYSQL_SOCKET="${MYSQL_SOCKET:-}"
8
+
9
+ usage() {
10
+ cat <<'TEXT'
11
+ Usage: sudo MYSQL_SOCKET=/run/mysqld/mysqld.sock ./bin/upgrade [--mysql PATH] [--defaults-extra-file PATH]
12
+
13
+ Performs the required MySQL UDF upgrade order: DROP FUNCTION, replace the
14
+ library, CREATE FUNCTION, then smoke-check. The Makefile requires
15
+ CONFIRM=UPGRADE_MULTI_COMPRESS before this script can run.
16
+ TEXT
17
+ }
18
+
19
+ while [ "$#" -gt 0 ]; do
20
+ case "$1" in
21
+ --mysql)
22
+ [ "$#" -ge 2 ] || { echo '--mysql requires a path' >&2; exit 2; }
23
+ MYSQL="$2"; shift 2 ;;
24
+ --defaults-extra-file)
25
+ [ "$#" -ge 2 ] || { echo '--defaults-extra-file requires a path' >&2; exit 2; }
26
+ DEFAULTS_FILE="$2"; shift 2 ;;
27
+ --help|-h)
28
+ usage; exit 0 ;;
29
+ *)
30
+ echo "multi_compress mysql upgrade: unknown option: $1" >&2; exit 2 ;;
31
+ esac
32
+ done
33
+
34
+ [ "${EUID:-$(id -u)}" -eq 0 ] || { echo 'multi_compress mysql upgrade: run as root' >&2; exit 2; }
35
+ DOCTOR_ARGS=(--mysql "$MYSQL")
36
+ if [ -n "$DEFAULTS_FILE" ]; then
37
+ DOCTOR_ARGS+=(--defaults-extra-file "$DEFAULTS_FILE")
38
+ fi
39
+ MYSQL_SOCKET="$MYSQL_SOCKET" "$ROOT/bin/doctor" "${DOCTOR_ARGS[@]}"
40
+
41
+ # shellcheck source=common
42
+ source "$ROOT/bin/common"
43
+ mcdb_resolve_mysql
44
+ mcdb_mysql_args
45
+ mcdb_read_udf_state
46
+ mcdb_require_clean_owned_state
47
+ LEGACY_MCDB1_ONLY=0
48
+ if mcdb_state_is_clean_v1_enabled; then
49
+ LEGACY_MCDB1_ONLY=1
50
+ elif ! mcdb_state_is_clean_enabled; then
51
+ mcdb_die 'upgrade requires either a complete MCDB1-only or MCDB1/MCDB2 owned UDF state'
52
+ fi
53
+ CAPTURED_REGISTRATIONS=("${MCDB_REGISTERED_ROWS[@]}")
54
+ if [ "$LEGACY_MCDB1_ONLY" -eq 1 ]; then
55
+ # A genuine 0.5.x library does not advertise MCDB2 yet. Verify only its
56
+ # MCDB1 surface before the required DROP -> replace -> CREATE transition.
57
+ mcdb_v1_smoke_check
58
+ else
59
+ mcdb_smoke_check
60
+ fi
61
+
62
+ restore_captured_registrations() {
63
+ local registration name dl returns
64
+
65
+ for registration in "${CAPTURED_REGISTRATIONS[@]}"; do
66
+ IFS=$'\t' read -r name dl <<< "$registration"
67
+ case "$name" in
68
+ multi_compress_db_version|multi_compress_db_decompress|multi_compress_db_dictionary_sha256|multi_compress_db_decompress_dict) returns='STRING' ;;
69
+ multi_compress_db_is_valid|multi_compress_db_original_size|multi_compress_db_dictionary_ref|multi_compress_db_dictionary_zstd_id|multi_compress_db_is_valid_dict) returns='INTEGER' ;;
70
+ *) mcdb_die "captured unexpected UDF registration: $name" ;;
71
+ esac
72
+ mcdb_query "CREATE FUNCTION ${name} RETURNS ${returns} SONAME '${dl}';"
73
+ done
74
+ }
75
+
76
+ PLUGIN_DIR="$(mcdb_plugin_dir)"
77
+ LIBRARY="$PLUGIN_DIR/$MCDB_UDF_LIBRARY"
78
+ [ -f "$LIBRARY" ] || mcdb_die "installed library is missing: $LIBRARY"
79
+ BACKUP="$PLUGIN_DIR/.${MCDB_UDF_LIBRARY}.backup.$$.$RANDOM"
80
+ TEMP_LIBRARY="$PLUGIN_DIR/.${MCDB_UDF_LIBRARY}.new.$$.$RANDOM.tmp"
81
+ RESTORED=0
82
+ cleanup() {
83
+ rm -f "$TEMP_LIBRARY"
84
+ if [ "$RESTORED" -eq 1 ]; then
85
+ rm -f "$BACKUP"
86
+ fi
87
+ }
88
+ trap cleanup EXIT
89
+
90
+ make -C "$ROOT/mysql_udf" clean udf MYSQL_UDF_ABI=mysql57
91
+ install -m 0755 "$ROOT/mysql_udf/build/$MCDB_UDF_LIBRARY" "$TEMP_LIBRARY"
92
+
93
+ if ! mcdb_drop_owned_udfs; then
94
+ mcdb_die 'could not drop existing MultiCompress UDFs; library was not replaced'
95
+ fi
96
+ if ! cp -p "$LIBRARY" "$BACKUP"; then
97
+ restore_captured_registrations || true
98
+ mcdb_die 'could not back up the installed library; original registrations were restored when possible'
99
+ fi
100
+
101
+ mv -f "$TEMP_LIBRARY" "$LIBRARY"
102
+ if mcdb_register_expected_udfs && mcdb_smoke_check; then
103
+ mcdb_read_udf_state
104
+ mcdb_state_is_clean_enabled || mcdb_die 'upgrade did not register the complete MCDB1/MCDB2 UDF surface'
105
+ rm -f "$BACKUP"
106
+ RESTORED=1
107
+ echo 'MultiCompress MySQL UDFs upgraded successfully.'
108
+ exit 0
109
+ fi
110
+
111
+ ROLLBACK_OK=1
112
+ mcdb_drop_owned_udfs || ROLLBACK_OK=0
113
+ if [ -f "$BACKUP" ]; then
114
+ mv -f "$BACKUP" "$LIBRARY" || ROLLBACK_OK=0
115
+ else
116
+ ROLLBACK_OK=0
117
+ fi
118
+ if ! restore_captured_registrations || ! mcdb_smoke_check; then
119
+ ROLLBACK_OK=0
120
+ fi
121
+ if [ "$ROLLBACK_OK" -eq 1 ]; then
122
+ RESTORED=1
123
+ echo 'multi_compress mysql upgrade: failed; previous library and registrations were restored' >&2
124
+ else
125
+ echo 'multi_compress mysql upgrade: failed and rollback did not complete; inspect mysql.func and plugin_dir immediately' >&2
126
+ fi
127
+ exit 1