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,472 @@
1
+ #include <stdint.h>
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+
6
+ #ifdef MCDB_MYSQL_UDF_ABI_57
7
+ #include "mysql_udf_abi_57.h"
8
+ #else
9
+ #include <stdbool.h>
10
+ #include <mysql.h>
11
+
12
+ #if defined(MARIADB_BASE_VERSION) || defined(MARIADB_VERSION_ID) || \
13
+ (defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID < 80000)
14
+ typedef my_bool mcdb_udf_init_result_t;
15
+ #else
16
+ typedef bool mcdb_udf_init_result_t;
17
+ #endif
18
+ #endif
19
+
20
+ #ifdef MCDB_MYSQL_UDF_ABI_57
21
+ typedef my_bool mcdb_udf_init_result_t;
22
+ #endif
23
+
24
+ #include <zstd.h>
25
+
26
+ #include "mcdb_format.h"
27
+
28
+ #define MCDB_MYSQL_DDICT_CACHE_ENTRIES 4
29
+
30
+ typedef struct {
31
+ uint64_t dictionary_ref;
32
+ unsigned char sha256[MCDB_SHA256_BYTES];
33
+ unsigned char *bytes;
34
+ size_t bytes_len;
35
+ ZSTD_DDict *ddict;
36
+ uint64_t used_at;
37
+ } mcdb_mysql_cache_entry;
38
+
39
+ typedef struct {
40
+ char *output;
41
+ ZSTD_DCtx *dctx;
42
+ uint64_t clock;
43
+ mcdb_mysql_cache_entry entries[MCDB_MYSQL_DDICT_CACHE_ENTRIES];
44
+ } mcdb_mysql_dictionary_state;
45
+
46
+ static void mcdb_mysql_state_free(mcdb_mysql_dictionary_state *state) {
47
+ int i;
48
+ if (!state)
49
+ return;
50
+ free(state->output);
51
+ if (state->dctx)
52
+ ZSTD_freeDCtx(state->dctx);
53
+ for (i = 0; i < MCDB_MYSQL_DDICT_CACHE_ENTRIES; i++) {
54
+ free(state->entries[i].bytes);
55
+ if (state->entries[i].ddict)
56
+ ZSTD_freeDDict(state->entries[i].ddict);
57
+ }
58
+ free(state);
59
+ }
60
+
61
+ static mcdb_mysql_dictionary_state *mcdb_mysql_state_new(void) {
62
+ mcdb_mysql_dictionary_state *state = (mcdb_mysql_dictionary_state *)calloc(1, sizeof(*state));
63
+ if (!state)
64
+ return NULL;
65
+ state->dctx = ZSTD_createDCtx();
66
+ if (!state->dctx) {
67
+ free(state);
68
+ return NULL;
69
+ }
70
+ return state;
71
+ }
72
+
73
+ static int mcdb_mysql_parse_ref(UDF_ARGS *args, uint64_t *out_ref) {
74
+ long long value;
75
+ if (!args->args[1])
76
+ return 0;
77
+ memcpy(&value, args->args[1], sizeof(value));
78
+ if (value <= 0)
79
+ return 0;
80
+ *out_ref = (uint64_t)value;
81
+ return 1;
82
+ }
83
+
84
+ static int mcdb_mysql_cache_victim(mcdb_mysql_dictionary_state *state, int same_key) {
85
+ int i;
86
+ int victim = 0;
87
+ uint64_t oldest = UINT64_MAX;
88
+ if (same_key >= 0)
89
+ return same_key;
90
+ for (i = 0; i < MCDB_MYSQL_DDICT_CACHE_ENTRIES; i++) {
91
+ if (!state->entries[i].ddict)
92
+ return i;
93
+ if (state->entries[i].used_at < oldest) {
94
+ oldest = state->entries[i].used_at;
95
+ victim = i;
96
+ }
97
+ }
98
+ return victim;
99
+ }
100
+
101
+ static ZSTD_DDict *mcdb_mysql_cached_ddict(mcdb_mysql_dictionary_state *state,
102
+ uint64_t dictionary_ref,
103
+ const unsigned char *dictionary_sha256,
104
+ const unsigned char *dictionary, size_t dictionary_len) {
105
+ unsigned char actual_sha256[MCDB_SHA256_BYTES];
106
+ int i;
107
+ int same_key = -1;
108
+ int victim;
109
+ ZSTD_DDict *ddict;
110
+
111
+ for (i = 0; i < MCDB_MYSQL_DDICT_CACHE_ENTRIES; i++) {
112
+ mcdb_mysql_cache_entry *entry = &state->entries[i];
113
+ if (!entry->ddict || entry->dictionary_ref != dictionary_ref ||
114
+ memcmp(entry->sha256, dictionary_sha256, MCDB_SHA256_BYTES) != 0)
115
+ continue;
116
+ same_key = i;
117
+ if (entry->bytes_len == dictionary_len &&
118
+ memcmp(entry->bytes, dictionary, dictionary_len) == 0) {
119
+ entry->used_at = ++state->clock;
120
+ return entry->ddict;
121
+ }
122
+ break;
123
+ }
124
+
125
+ mcdb_sha256(dictionary, dictionary_len, actual_sha256);
126
+ if (memcmp(actual_sha256, dictionary_sha256, MCDB_SHA256_BYTES) != 0)
127
+ return NULL;
128
+ if (mcdb_validate_dictionary(dictionary, dictionary_len, NULL) != MCDB_OK)
129
+ return NULL;
130
+ ddict = ZSTD_createDDict(dictionary, dictionary_len);
131
+ if (!ddict)
132
+ return NULL;
133
+
134
+ victim = mcdb_mysql_cache_victim(state, same_key);
135
+ free(state->entries[victim].bytes);
136
+ if (state->entries[victim].ddict)
137
+ ZSTD_freeDDict(state->entries[victim].ddict);
138
+ state->entries[victim].bytes = (unsigned char *)malloc(dictionary_len);
139
+ if (!state->entries[victim].bytes) {
140
+ ZSTD_freeDDict(ddict);
141
+ state->entries[victim].ddict = NULL;
142
+ state->entries[victim].bytes_len = 0;
143
+ return NULL;
144
+ }
145
+ memcpy(state->entries[victim].bytes, dictionary, dictionary_len);
146
+ state->entries[victim].dictionary_ref = dictionary_ref;
147
+ memcpy(state->entries[victim].sha256, dictionary_sha256, MCDB_SHA256_BYTES);
148
+ state->entries[victim].bytes_len = dictionary_len;
149
+ state->entries[victim].ddict = ddict;
150
+ state->entries[victim].used_at = ++state->clock;
151
+ return ddict;
152
+ }
153
+
154
+ static mcdb_udf_init_result_t mcdb_one_blob_init(UDF_INIT *initid, UDF_ARGS *args, char *message,
155
+ const char *signature, unsigned long max_length) {
156
+ if (args->arg_count != 1) {
157
+ snprintf(message, 255, "%s takes exactly one argument", signature);
158
+ return 1;
159
+ }
160
+ args->arg_type[0] = STRING_RESULT;
161
+ initid->maybe_null = 1;
162
+ initid->const_item = 0;
163
+ initid->max_length = max_length;
164
+ return 0;
165
+ }
166
+
167
+ mcdb_udf_init_result_t multi_compress_db_version_init(UDF_INIT *initid, UDF_ARGS *args,
168
+ char *message) {
169
+ (void)args;
170
+ if (args->arg_count != 0) {
171
+ strcpy(message, "multi_compress_db_version() takes no arguments");
172
+ return 1;
173
+ }
174
+ initid->maybe_null = 0;
175
+ initid->const_item = 1;
176
+ initid->max_length = 96;
177
+ return 0;
178
+ }
179
+
180
+ char *multi_compress_db_version(UDF_INIT *initid, UDF_ARGS *args, char *result,
181
+ unsigned long *length, char *is_null, char *error) {
182
+ int n;
183
+ (void)initid;
184
+ (void)args;
185
+ *is_null = 0;
186
+ *error = 0;
187
+ n = snprintf(result, 255, "multi_compress reader %s; MCDB1 + MCDB2; zstd %s",
188
+ MCDB_READER_VERSION, ZSTD_versionString());
189
+ *length = (unsigned long)(n > 0 ? n : 0);
190
+ return result;
191
+ }
192
+
193
+ mcdb_udf_init_result_t multi_compress_db_is_valid_init(UDF_INIT *initid, UDF_ARGS *args,
194
+ char *message) {
195
+ return mcdb_one_blob_init(initid, args, message, "multi_compress_db_is_valid(blob)", 1);
196
+ }
197
+
198
+ long long multi_compress_db_is_valid(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
199
+ unsigned char *out = NULL;
200
+ size_t out_len = 0;
201
+ char err[MCDB_ERRLEN];
202
+ mcdb_status st;
203
+ (void)initid;
204
+ *error = 0;
205
+ if (!args->args[0]) {
206
+ *is_null = 1;
207
+ return 0;
208
+ }
209
+ *is_null = 0;
210
+ st = mcdb_decode((const unsigned char *)args->args[0], (size_t)args->lengths[0], &out, &out_len,
211
+ err);
212
+ free(out);
213
+ return st == MCDB_OK ? 1 : 0;
214
+ }
215
+
216
+ mcdb_udf_init_result_t multi_compress_db_decompress_init(UDF_INIT *initid, UDF_ARGS *args,
217
+ char *message) {
218
+ mcdb_udf_init_result_t result = mcdb_one_blob_init(
219
+ initid, args, message, "multi_compress_db_decompress(blob)", MCDB_MAX_OUTPUT);
220
+ if (result)
221
+ return result;
222
+ initid->ptr = NULL;
223
+ return 0;
224
+ }
225
+
226
+ void multi_compress_db_decompress_deinit(UDF_INIT *initid) {
227
+ free(initid->ptr);
228
+ initid->ptr = NULL;
229
+ }
230
+
231
+ char *multi_compress_db_decompress(UDF_INIT *initid, UDF_ARGS *args, char *result,
232
+ unsigned long *length, char *is_null, char *error) {
233
+ unsigned char *out = NULL;
234
+ size_t out_len = 0;
235
+ char err[MCDB_ERRLEN];
236
+ mcdb_status st;
237
+ (void)result;
238
+ *error = 0;
239
+ if (!args->args[0]) {
240
+ *is_null = 1;
241
+ return NULL;
242
+ }
243
+ free(initid->ptr);
244
+ initid->ptr = NULL;
245
+ st = mcdb_decode((const unsigned char *)args->args[0], (size_t)args->lengths[0], &out, &out_len,
246
+ err);
247
+ if (st != MCDB_OK) {
248
+ *is_null = 1;
249
+ free(out);
250
+ return NULL;
251
+ }
252
+ initid->ptr = (char *)out;
253
+ *length = (unsigned long)out_len;
254
+ *is_null = 0;
255
+ return (char *)out;
256
+ }
257
+
258
+ mcdb_udf_init_result_t multi_compress_db_original_size_init(UDF_INIT *initid, UDF_ARGS *args,
259
+ char *message) {
260
+ return mcdb_one_blob_init(initid, args, message, "multi_compress_db_original_size(blob)", 20);
261
+ }
262
+
263
+ long long multi_compress_db_original_size(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
264
+ char *error) {
265
+ mcdb_header header;
266
+ mcdb_status st;
267
+ (void)initid;
268
+ *error = 0;
269
+ if (!args->args[0]) {
270
+ *is_null = 1;
271
+ return 0;
272
+ }
273
+ st = mcdb_parse_header((const unsigned char *)args->args[0], (size_t)args->lengths[0], &header);
274
+ if (st != MCDB_OK) {
275
+ *is_null = 1;
276
+ return 0;
277
+ }
278
+ *is_null = 0;
279
+ return (long long)header.original_size;
280
+ }
281
+
282
+ mcdb_udf_init_result_t multi_compress_db_dictionary_ref_init(UDF_INIT *initid, UDF_ARGS *args,
283
+ char *message) {
284
+ return mcdb_one_blob_init(initid, args, message, "multi_compress_db_dictionary_ref(blob)", 20);
285
+ }
286
+
287
+ long long multi_compress_db_dictionary_ref(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
288
+ char *error) {
289
+ uint64_t ref;
290
+ mcdb_status st;
291
+ (void)initid;
292
+ *error = 0;
293
+ if (!args->args[0]) {
294
+ *is_null = 1;
295
+ return 0;
296
+ }
297
+ ref = mcdb_dictionary_ref((const unsigned char *)args->args[0], (size_t)args->lengths[0], &st);
298
+ if (st != MCDB_OK) {
299
+ *is_null = 1;
300
+ return 0;
301
+ }
302
+ *is_null = 0;
303
+ return (long long)ref;
304
+ }
305
+
306
+ mcdb_udf_init_result_t multi_compress_db_dictionary_zstd_id_init(UDF_INIT *initid, UDF_ARGS *args,
307
+ char *message) {
308
+ return mcdb_one_blob_init(initid, args, message,
309
+ "multi_compress_db_dictionary_zstd_id(dictionary)", 20);
310
+ }
311
+
312
+ long long multi_compress_db_dictionary_zstd_id(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
313
+ char *error) {
314
+ uint32_t id;
315
+ (void)initid;
316
+ *error = 0;
317
+ if (!args->args[0] || mcdb_validate_dictionary((const unsigned char *)args->args[0],
318
+ (size_t)args->lengths[0], &id) != MCDB_OK) {
319
+ *is_null = 1;
320
+ return 0;
321
+ }
322
+ *is_null = 0;
323
+ return (long long)id;
324
+ }
325
+
326
+ mcdb_udf_init_result_t multi_compress_db_dictionary_sha256_init(UDF_INIT *initid, UDF_ARGS *args,
327
+ char *message) {
328
+ return mcdb_one_blob_init(initid, args, message,
329
+ "multi_compress_db_dictionary_sha256(dictionary)", MCDB_SHA256_BYTES);
330
+ }
331
+
332
+ char *multi_compress_db_dictionary_sha256(UDF_INIT *initid, UDF_ARGS *args, char *result,
333
+ unsigned long *length, char *is_null, char *error) {
334
+ (void)initid;
335
+ *error = 0;
336
+ if (!args->args[0]) {
337
+ *is_null = 1;
338
+ return NULL;
339
+ }
340
+ mcdb_sha256((const unsigned char *)args->args[0], (size_t)args->lengths[0],
341
+ (unsigned char *)result);
342
+ *length = MCDB_SHA256_BYTES;
343
+ *is_null = 0;
344
+ return result;
345
+ }
346
+
347
+ static mcdb_udf_init_result_t mcdb_dict_init(UDF_INIT *initid, UDF_ARGS *args, char *message,
348
+ const char *signature, unsigned long max_length) {
349
+ mcdb_mysql_dictionary_state *state;
350
+ if (args->arg_count != 4) {
351
+ snprintf(message, 255, "%s takes exactly four arguments", signature);
352
+ return 1;
353
+ }
354
+ args->arg_type[0] = STRING_RESULT;
355
+ args->arg_type[1] = INT_RESULT;
356
+ args->arg_type[2] = STRING_RESULT;
357
+ args->arg_type[3] = STRING_RESULT;
358
+ initid->maybe_null = 1;
359
+ initid->const_item = 0;
360
+ initid->max_length = max_length;
361
+ state = mcdb_mysql_state_new();
362
+ if (!state) {
363
+ strcpy(message, "multi_compress: could not allocate zstd dictionary cache");
364
+ return 1;
365
+ }
366
+ initid->ptr = (char *)state;
367
+ return 0;
368
+ }
369
+
370
+ mcdb_udf_init_result_t multi_compress_db_is_valid_dict_init(UDF_INIT *initid, UDF_ARGS *args,
371
+ char *message) {
372
+ return mcdb_dict_init(
373
+ initid, args, message,
374
+ "multi_compress_db_is_valid_dict(blob, dictionary_id, dictionary_sha256, dictionary)", 1);
375
+ }
376
+
377
+ mcdb_udf_init_result_t multi_compress_db_decompress_dict_init(UDF_INIT *initid, UDF_ARGS *args,
378
+ char *message) {
379
+ return mcdb_dict_init(
380
+ initid, args, message,
381
+ "multi_compress_db_decompress_dict(blob, dictionary_id, dictionary_sha256, dictionary)",
382
+ MCDB_MAX_OUTPUT);
383
+ }
384
+
385
+ void multi_compress_db_is_valid_dict_deinit(UDF_INIT *initid) {
386
+ mcdb_mysql_state_free((mcdb_mysql_dictionary_state *)initid->ptr);
387
+ initid->ptr = NULL;
388
+ }
389
+
390
+ void multi_compress_db_decompress_dict_deinit(UDF_INIT *initid) {
391
+ mcdb_mysql_state_free((mcdb_mysql_dictionary_state *)initid->ptr);
392
+ initid->ptr = NULL;
393
+ }
394
+
395
+ static mcdb_status mcdb_mysql_decode_v2(UDF_INIT *initid, UDF_ARGS *args, unsigned char **out,
396
+ size_t *out_len) {
397
+ mcdb_mysql_dictionary_state *state = (mcdb_mysql_dictionary_state *)initid->ptr;
398
+ uint64_t dictionary_ref;
399
+ ZSTD_DDict *ddict;
400
+ uint64_t original_size;
401
+ unsigned char *buffer;
402
+ char err[MCDB_ERRLEN];
403
+ mcdb_status st;
404
+
405
+ if (!args->args[0] || !args->args[1] || !args->args[2] || !args->args[3])
406
+ return MCDB_ERR_DICTIONARY_REQUIRED;
407
+ if (args->lengths[2] != MCDB_SHA256_BYTES || !mcdb_mysql_parse_ref(args, &dictionary_ref))
408
+ return MCDB_ERR_DICTIONARY_REF;
409
+ ddict = mcdb_mysql_cached_ddict(state, dictionary_ref, (const unsigned char *)args->args[2],
410
+ (const unsigned char *)args->args[3], (size_t)args->lengths[3]);
411
+ if (!ddict)
412
+ return MCDB_ERR_DICTIONARY_ID;
413
+ st = mcdb_validate_v2_header((const unsigned char *)args->args[0], (size_t)args->lengths[0],
414
+ &original_size, NULL);
415
+ if (st != MCDB_OK)
416
+ return st;
417
+ buffer = (unsigned char *)malloc(original_size ? (size_t)original_size : 1);
418
+ if (!buffer)
419
+ return MCDB_ERR_ALLOC;
420
+ st = mcdb_decode_v2_into((const unsigned char *)args->args[0], (size_t)args->lengths[0],
421
+ dictionary_ref, (const unsigned char *)args->args[3],
422
+ (size_t)args->lengths[3], state->dctx, ddict, buffer,
423
+ (size_t)original_size, out_len, err);
424
+ if (st != MCDB_OK) {
425
+ free(buffer);
426
+ return st;
427
+ }
428
+ *out = buffer;
429
+ return MCDB_OK;
430
+ }
431
+
432
+ long long multi_compress_db_is_valid_dict(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
433
+ char *error) {
434
+ unsigned char *out = NULL;
435
+ size_t out_len = 0;
436
+ mcdb_status st;
437
+ *error = 0;
438
+ if (!args->args[0] || !args->args[1] || !args->args[2] || !args->args[3]) {
439
+ *is_null = 1;
440
+ return 0;
441
+ }
442
+ st = mcdb_mysql_decode_v2(initid, args, &out, &out_len);
443
+ free(out);
444
+ *is_null = 0;
445
+ return st == MCDB_OK ? 1 : 0;
446
+ }
447
+
448
+ char *multi_compress_db_decompress_dict(UDF_INIT *initid, UDF_ARGS *args, char *result,
449
+ unsigned long *length, char *is_null, char *error) {
450
+ mcdb_mysql_dictionary_state *state = (mcdb_mysql_dictionary_state *)initid->ptr;
451
+ unsigned char *out = NULL;
452
+ size_t out_len = 0;
453
+ mcdb_status st;
454
+ (void)result;
455
+ *error = 0;
456
+ if (!args->args[0] || !args->args[1] || !args->args[2] || !args->args[3]) {
457
+ *is_null = 1;
458
+ return NULL;
459
+ }
460
+ free(state->output);
461
+ state->output = NULL;
462
+ st = mcdb_mysql_decode_v2(initid, args, &out, &out_len);
463
+ if (st != MCDB_OK) {
464
+ free(out);
465
+ *is_null = 1;
466
+ return NULL;
467
+ }
468
+ state->output = (char *)out;
469
+ *length = (unsigned long)out_len;
470
+ *is_null = 0;
471
+ return state->output;
472
+ }
@@ -0,0 +1,34 @@
1
+ #ifndef MCDB_MYSQL_UDF_ABI_57_H
2
+ #define MCDB_MYSQL_UDF_ABI_57_H
3
+
4
+ typedef char my_bool;
5
+
6
+ enum Item_result {
7
+ STRING_RESULT = 0,
8
+ REAL_RESULT,
9
+ INT_RESULT,
10
+ ROW_RESULT,
11
+ DECIMAL_RESULT
12
+ };
13
+
14
+ typedef struct st_udf_args {
15
+ unsigned int arg_count;
16
+ enum Item_result *arg_type;
17
+ char **args;
18
+ unsigned long *lengths;
19
+ char *maybe_null;
20
+ char **attributes;
21
+ unsigned long *attribute_lengths;
22
+ char *extension;
23
+ } UDF_ARGS;
24
+
25
+ typedef struct st_udf_init {
26
+ my_bool maybe_null;
27
+ unsigned int decimals;
28
+ unsigned long max_length;
29
+ char *ptr;
30
+ my_bool const_item;
31
+ void *extension;
32
+ } UDF_INIT;
33
+
34
+ #endif
@@ -0,0 +1,60 @@
1
+ #include "mcdb_format.h"
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+
7
+ static unsigned char *read_all(const char *path, size_t *len) {
8
+ FILE *f = fopen(path, "rb");
9
+ if (!f) {
10
+ perror("open");
11
+ return NULL;
12
+ }
13
+ fseek(f, 0, SEEK_END);
14
+ long sz = ftell(f);
15
+ fseek(f, 0, SEEK_SET);
16
+ unsigned char *buf = (unsigned char *)malloc(sz > 0 ? (size_t)sz : 1);
17
+ size_t got = fread(buf, 1, (size_t)sz, f);
18
+ fclose(f);
19
+ *len = got;
20
+ return buf;
21
+ }
22
+
23
+ int main(int argc, char **argv) {
24
+ if (argc != 3) {
25
+ fprintf(stderr, "usage: %s decode|valid <file.mcdb>\n", argv[0]);
26
+ return 2;
27
+ }
28
+
29
+ size_t in_len = 0;
30
+ unsigned char *in = read_all(argv[2], &in_len);
31
+ if (!in)
32
+ return 2;
33
+
34
+ if (strcmp(argv[1], "valid") == 0) {
35
+ uint64_t sz;
36
+ mcdb_status st = mcdb_validate_header(in, in_len, &sz);
37
+ unsigned char *out = NULL;
38
+ size_t out_len = 0;
39
+ char err[MCDB_ERRLEN];
40
+ if (st == MCDB_OK)
41
+ st = mcdb_decode(in, in_len, &out, &out_len, err);
42
+ free(out);
43
+ free(in);
44
+ return st == MCDB_OK ? 0 : 1;
45
+ }
46
+
47
+ unsigned char *out = NULL;
48
+ size_t out_len = 0;
49
+ char err[MCDB_ERRLEN];
50
+ mcdb_status st = mcdb_decode(in, in_len, &out, &out_len, err);
51
+ if (st != MCDB_OK) {
52
+ fprintf(stderr, "%s\n", err);
53
+ free(in);
54
+ return 1;
55
+ }
56
+ fwrite(out, 1, out_len, stdout);
57
+ free(out);
58
+ free(in);
59
+ return 0;
60
+ }