xref: /6.6.0/couchstore/src/views/reducers.cc (revision f798e0b7)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
3/**
4 * @copyright 2013 Couchbase, Inc.
5 *
6 * @author Filipe Manana  <filipe@couchbase.com>
7 * @author Fulu Li        <fulu@couchbase.com>
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
10 * use this file except in compliance with the License. You may obtain a copy of
11 * the License at
12 *
13 *  http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 * License for the specific language governing permissions and limitations under
19 * the License.
20 **/
21
22#include "../bitfield.h"
23
24#include <platform/cb_malloc.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <inttypes.h>
29#include "bitmap.h"
30#include "keys.h"
31#include "reductions.h"
32#include "values.h"
33#include "reducers.h"
34#include <platform/cbassert.h>
35#include "../couch_btree.h"
36
37typedef enum {
38    VIEW_REDUCER_SUCCESS                = 0,
39    VIEW_REDUCER_ERROR_NOT_A_NUMBER     = 1,
40    VIEW_REDUCER_ERROR_BAD_STATS_OBJECT = 2
41} builtin_reducer_error_t;
42
43static const char *builtin_reducer_error_msg[] = {
44    NULL,
45    "Value is not a number",
46    "Invalid _stats JSON object"
47};
48
49typedef struct {
50    void  *parent_ctx;
51    void  *mapreduce_ctx;
52} reducer_ctx_t;
53
54typedef couchstore_error_t (*reducer_fn_t)(const mapreduce_json_list_t *,
55                                           const mapreduce_json_list_t *,
56                                           reducer_ctx_t *,
57                                           sized_buf *);
58
59typedef struct {
60    /* For builtin reduce operations that fail, this is the Key of the KV pair
61       that caused the failure. For example, for a _sum reducer, if there's
62       a value that is not a number, the reducer will abort and populate the
63       field 'error_key' with the value's corresponding key and set 'error' to
64       VIEW_REDUCER_ERROR_NOT_A_NUMBER. */
65    const char              *error_key;
66    builtin_reducer_error_t  builtin_error;
67
68    /* For custom (JavaScript) reduce operations, this is the error message received
69       from the JavaScript engine. */
70    char                    *error_msg;
71    mapreduce_error_t        mapreduce_error;
72
73    unsigned                 num_reducers;
74    reducer_fn_t             *reducers;
75    reducer_ctx_t            *reducer_contexts;
76} reducer_private_t;
77
78
79#define DOUBLE_FMT "%.15g"
80#define scan_stats(buf, sum, count, min, max, sumsqr) \
81        sscanf(buf, "{\"sum\":%lg,\"count\":%" SCNu64 ",\"min\":%lg,\"max\":%lg,\"sumsqr\":%lg}",\
82               &sum, &count, &min, &max, &sumsqr)
83
84#define sprint_stats(buf, sum, count, min, max, sumsqr) \
85        sprintf(buf, "{\"sum\":%g,\"count\":%" PRIu64 ",\"min\":%g,\"max\":%g,\"sumsqr\":%g}",\
86                sum, count, min, max, sumsqr)
87
88static void free_json_key_list(mapreduce_json_list_t& list);
89
90static int json_to_str(const mapreduce_json_t *buf, char str[32])
91{
92    if (buf->length < 1 || buf->length > 31) {
93        return 0;
94    }
95    memcpy(str, buf->json, buf->length);
96    str[buf->length] = '\0';
97
98    return 1;
99}
100
101
102static int json_to_double(const mapreduce_json_t *buf, double *out_num)
103{
104    char str[32];
105    char *end;
106
107    if (!json_to_str(buf, str)) {
108        return 0;
109    }
110    *out_num = strtod(str, &end);
111
112    return ((end > str) && (*end == '\0'));
113}
114
115
116static int json_to_uint64(const mapreduce_json_t *buf, uint64_t *out_num)
117{
118    char str[32];
119    char *end;
120
121    if (!json_to_str(buf, str)) {
122        return 0;
123    }
124    *out_num = strtoull(str, &end, 10);
125
126    return ((end > str) && (*end == '\0'));
127}
128
129
130couchstore_error_t view_id_btree_reduce(char *dst,
131                                        size_t *size_r,
132                                        const nodelist *leaflist,
133                                        int count,
134                                        void *ctx)
135{
136    view_id_btree_reduction_t *r = NULL;
137    uint64_t subtree_count = 0;
138    couchstore_error_t errcode = COUCHSTORE_SUCCESS;
139    const nodelist *i;
140
141    (void) ctx;
142    r = (view_id_btree_reduction_t *) cb_malloc(sizeof(view_id_btree_reduction_t));
143    if (r == NULL) {
144        errcode = COUCHSTORE_ERROR_ALLOC_FAIL;
145        goto alloc_error;
146    }
147    memset(&r->partitions_bitmap, 0, sizeof(bitmap_t));
148
149    for (i = leaflist; i != NULL && count > 0; i = i->next, count--) {
150        set_bit(&r->partitions_bitmap,
151                decode_view_btree_partition(i->data.buf, i->data.size));
152        subtree_count++;
153    }
154    r->kv_count = subtree_count;
155    errcode = encode_view_id_btree_reduction(r, dst, size_r);
156
157alloc_error:
158    free_view_id_btree_reduction(r);
159
160    return errcode;
161}
162
163
164couchstore_error_t view_id_btree_rereduce(char *dst,
165                                          size_t *size_r,
166                                          const nodelist *itmlist,
167                                          int count,
168                                          void *ctx)
169{
170    view_id_btree_reduction_t *r = NULL;
171    uint64_t subtree_count = 0;
172    couchstore_error_t errcode = COUCHSTORE_SUCCESS;
173    const nodelist *i;
174
175    (void) ctx;
176    r = (view_id_btree_reduction_t *) cb_malloc(sizeof(view_id_btree_reduction_t));
177    if (r == NULL) {
178        errcode = COUCHSTORE_ERROR_ALLOC_FAIL;
179        goto alloc_error;
180    }
181    memset(&r->partitions_bitmap, 0, sizeof(bitmap_t));
182    for (i = itmlist; i != NULL && count > 0; i = i->next, count--) {
183        view_id_btree_reduction_t *r2 = NULL;
184        errcode = decode_view_id_btree_reduction(i->pointer->reduce_value.buf, &r2);
185        if (errcode != COUCHSTORE_SUCCESS) {
186            goto alloc_error;
187        }
188        union_bitmaps(&r->partitions_bitmap, &r2->partitions_bitmap);
189        subtree_count += r2->kv_count;
190        free_view_id_btree_reduction(r2);
191    }
192    r->kv_count = subtree_count;
193    errcode = encode_view_id_btree_reduction(r, dst, size_r);
194
195alloc_error:
196    free_view_id_btree_reduction(r);
197
198    return errcode;
199}
200
201
202static couchstore_error_t builtin_sum_reducer(const mapreduce_json_list_t *keys,
203                                              const mapreduce_json_list_t *values,
204                                              reducer_ctx_t *ctx,
205                                              sized_buf *buf)
206{
207    double n, sum = 0.0;
208    int i, size;
209    char red[512];
210
211    for (i = 0; i < values->length; ++i) {
212        mapreduce_json_t *value = &values->values[i];
213
214        if (json_to_double(value, &n)) {
215            sum += n;
216        } else {
217            reducer_private_t *priv = (reducer_private_t *) ctx->parent_ctx;
218
219            priv->builtin_error = VIEW_REDUCER_ERROR_NOT_A_NUMBER;
220            if (keys == NULL) {
221                /* rereduce */
222                return COUCHSTORE_ERROR_REDUCER_FAILURE;
223            } else {
224                /* reduce */
225                mapreduce_json_t *key = &keys->values[i];
226                char *error_key = (char *) cb_malloc(key->length + 1);
227
228                if (error_key == NULL) {
229                    return COUCHSTORE_ERROR_ALLOC_FAIL;
230                }
231                memcpy(error_key, key->json, key->length);
232                error_key[key->length] = '\0';
233                priv->error_key = (const char *) error_key;
234
235                return COUCHSTORE_ERROR_REDUCER_FAILURE;
236            }
237        }
238    }
239
240    size = sprintf(red, DOUBLE_FMT, sum);
241    cb_assert(size > 0);
242    buf->buf = (char *) cb_malloc(size);
243    if (buf->buf == NULL) {
244        return COUCHSTORE_ERROR_ALLOC_FAIL;
245    }
246    memcpy(buf->buf, red, size);
247    buf->size = size;
248
249    return COUCHSTORE_SUCCESS;
250}
251
252
253static couchstore_error_t builtin_count_reducer(const mapreduce_json_list_t *keys,
254                                                const mapreduce_json_list_t *values,
255                                                reducer_ctx_t *ctx,
256                                                sized_buf *buf)
257{
258    uint64_t count = 0, n;
259    int i, size;
260    char red[512];
261
262    for (i = 0; i < values->length; ++i) {
263        if (keys == NULL) {
264            /* rereduce */
265            mapreduce_json_t *value = &values->values[i];
266
267            if (json_to_uint64(value, &n)) {
268                count += n;
269            } else {
270                reducer_private_t *priv = (reducer_private_t *) ctx->parent_ctx;
271                priv->builtin_error = VIEW_REDUCER_ERROR_NOT_A_NUMBER;
272                return COUCHSTORE_ERROR_REDUCER_FAILURE;
273            }
274        } else {
275            /* reduce */
276            count++;
277        }
278    }
279
280    size = sprintf(red, "%" PRIu64, count);
281    cb_assert(size > 0);
282    buf->buf = (char *) cb_malloc(size);
283    if (buf->buf == NULL) {
284        return COUCHSTORE_ERROR_ALLOC_FAIL;
285    }
286    memcpy(buf->buf, red, size);
287    buf->size = size;
288
289    return COUCHSTORE_SUCCESS;
290}
291
292
293static couchstore_error_t builtin_stats_reducer(const mapreduce_json_list_t *keys,
294                                                const mapreduce_json_list_t *values,
295                                                reducer_ctx_t *ctx,
296                                                sized_buf *buf)
297{
298    double n;
299    int i, size, scanned;
300    stats_t s, reduced;
301    char red[4096];
302
303    memset(&s, 0, sizeof(s));
304
305    for (i = 0; i < values->length; ++i) {
306        mapreduce_json_t *value = &values->values[i];
307
308        if (keys == NULL) {
309            /* rereduce */
310            char *value_buf = (char *) cb_malloc(value->length + 1);
311
312            if (value_buf == NULL) {
313                return COUCHSTORE_ERROR_ALLOC_FAIL;
314            }
315
316            memcpy(value_buf, value->json, value->length);
317            value_buf[value->length] = '\0';
318            scanned = scan_stats(value_buf,
319                                 reduced.sum, reduced.count, reduced.min,
320                                 reduced.max, reduced.sumsqr);
321            cb_free(value_buf);
322            if (scanned == 5) {
323                if (reduced.min < s.min || s.count == 0) {
324                    s.min = reduced.min;
325                }
326                if (reduced.max > s.max || s.count == 0) {
327                    s.max = reduced.max;
328                }
329                s.count += reduced.count;
330                s.sum += reduced.sum;
331                s.sumsqr += reduced.sumsqr;
332            } else {
333                reducer_private_t *priv = (reducer_private_t *) ctx->parent_ctx;
334                priv->builtin_error = VIEW_REDUCER_ERROR_BAD_STATS_OBJECT;
335                return COUCHSTORE_ERROR_REDUCER_FAILURE;
336            }
337        } else {
338            /* reduce */
339            if (json_to_double(value, &n)) {
340                s.sum += n;
341                s.sumsqr += n * n;
342                if (s.count++ == 0) {
343                    s.min = s.max = n;
344                } else if (n > s.max) {
345                    s.max = n;
346                } else if (n < s.min) {
347                    s.min = n;
348                }
349            } else {
350                reducer_private_t *priv = (reducer_private_t *) ctx->parent_ctx;
351
352                priv->builtin_error = VIEW_REDUCER_ERROR_NOT_A_NUMBER;
353                if (keys == NULL) {
354                    /* rereduce */
355                    return COUCHSTORE_ERROR_REDUCER_FAILURE;
356                } else {
357                    /* reduce */
358                    mapreduce_json_t *key = &keys->values[i];
359                    char *error_key = (char *) cb_malloc(key->length + 1);
360
361                    if (error_key == NULL) {
362                        return COUCHSTORE_ERROR_ALLOC_FAIL;
363                    }
364                    memcpy(error_key, key->json, key->length);
365                    error_key[key->length] = '\0';
366                    priv->error_key = (const char *) error_key;
367
368                    return COUCHSTORE_ERROR_REDUCER_FAILURE;
369                }
370            }
371        }
372    }
373
374    size = sprint_stats(red, s.sum, s.count, s.min, s.max, s.sumsqr);
375    cb_assert(size > 0);
376    buf->buf = (char *) cb_malloc(size);
377    if (buf->buf == NULL) {
378        return COUCHSTORE_ERROR_ALLOC_FAIL;
379    }
380    memcpy(buf->buf, red, size);
381    buf->size = size;
382
383    return COUCHSTORE_SUCCESS;
384}
385
386
387static couchstore_error_t js_reducer(const mapreduce_json_list_t *keys,
388                                     const mapreduce_json_list_t *values,
389                                     reducer_ctx_t *ctx,
390                                     sized_buf *buf)
391{
392    mapreduce_error_t ret;
393    char *error_msg = NULL;
394    reducer_private_t *priv = (reducer_private_t *) ctx->parent_ctx;
395
396    if (keys == NULL) {
397        /* rereduce */
398        mapreduce_json_t *result = NULL;
399
400        ret = mapreduce_rereduce(ctx->mapreduce_ctx, 1, values, &result, &error_msg);
401        if (ret != MAPREDUCE_SUCCESS) {
402            priv->error_msg = error_msg;
403            priv->mapreduce_error = ret;
404            return COUCHSTORE_ERROR_REDUCER_FAILURE;
405        }
406        buf->buf = (char *) cb_malloc(result->length);
407        if (buf->buf == NULL) {
408            cb_free(result->json);
409            cb_free(result);
410            return COUCHSTORE_ERROR_ALLOC_FAIL;
411        }
412        buf->size = result->length;
413        memcpy(buf->buf, result->json, result->length);
414        cb_free(result->json);
415        cb_free(result);
416    } else {
417        /* reduce */
418        mapreduce_json_list_t *results = NULL;
419
420        ret = mapreduce_reduce_all(ctx->mapreduce_ctx,
421                                   keys,
422                                   values,
423                                   &results,
424                                   &error_msg);
425        if (ret != MAPREDUCE_SUCCESS) {
426            priv->error_msg = error_msg;
427            priv->mapreduce_error = ret;
428            return COUCHSTORE_ERROR_REDUCER_FAILURE;
429        }
430        cb_assert(results->length == 1);
431        buf->buf = (char *) cb_malloc(results->values[0].length);
432        if (buf->buf == NULL) {
433            mapreduce_free_json_list(results);
434            return COUCHSTORE_ERROR_ALLOC_FAIL;
435        }
436        buf->size = results->values[0].length;
437        memcpy(buf->buf, results->values[0].json, results->values[0].length);
438        mapreduce_free_json_list(results);
439    }
440
441    return COUCHSTORE_SUCCESS;
442}
443
444static void free_json_key_list(mapreduce_json_list_t& list) {
445    int i;
446
447    for (i = list.length - 1; i > 0; --i) {
448        if (list.values[i].json == list.values[i - 1].json) {
449            list.values[i].length = 0;
450        }
451    }
452
453    for (i = 0; i < list.length; ++i) {
454        if (list.values[i].length != 0) {
455            cb_free(list.values[i].json);
456        }
457    }
458    cb_free(list.values);
459}
460
461view_reducer_ctx_t *make_view_reducer_ctx(const char *functions[],
462                                          unsigned num_functions,
463                                          char **error_msg)
464{
465    unsigned i;
466    reducer_private_t *priv = (reducer_private_t*)cb_calloc(1, sizeof(*priv));
467    view_reducer_ctx_t *ctx = (view_reducer_ctx_t*)cb_calloc(1, sizeof(*ctx));
468
469    if (ctx == NULL || priv == NULL) {
470        goto error;
471    }
472
473    priv->num_reducers = num_functions;
474    priv->reducers = (reducer_fn_t*)cb_calloc(num_functions, sizeof(reducer_fn_t));
475    if (priv->reducers == NULL) {
476        goto error;
477    }
478
479    priv->reducer_contexts = (reducer_ctx_t*)
480                             cb_calloc(num_functions, sizeof(reducer_ctx_t));
481    if (priv->reducer_contexts == NULL) {
482        goto error;
483    }
484
485    for (i = 0; i < num_functions; ++i) {
486        priv->reducer_contexts[i].parent_ctx = (void *) priv;
487
488        if (strcmp(functions[i], "_count") == 0) {
489            priv->reducers[i] = builtin_count_reducer;
490        } else if (strcmp(functions[i], "_sum") == 0) {
491            priv->reducers[i] = builtin_sum_reducer;
492        } else if (strcmp(functions[i], "_stats") == 0) {
493            priv->reducers[i] = builtin_stats_reducer;
494        } else {
495            mapreduce_error_t mapred_error;
496            void *mapred_ctx = NULL;
497
498            priv->reducers[i] = js_reducer;
499            /* TODO: use single reduce context for all JS functions */
500            mapred_error = mapreduce_start_reduce_context(&functions[i], 1,
501                                                          &mapred_ctx,
502                                                          error_msg);
503            if (mapred_error != MAPREDUCE_SUCCESS) {
504                goto error;
505            }
506            priv->reducer_contexts[i].mapreduce_ctx = mapred_ctx;
507        }
508    }
509
510    priv->builtin_error = VIEW_REDUCER_SUCCESS;
511    priv->mapreduce_error = MAPREDUCE_SUCCESS;
512    ctx->priv = priv;
513    *error_msg = NULL;
514
515    return ctx;
516
517error:
518    if (priv != NULL) {
519        if (priv->reducer_contexts != NULL) {
520            for (i = 0; i < num_functions; ++i) {
521                mapreduce_free_context(priv->reducer_contexts[i].mapreduce_ctx);
522            }
523            cb_free(priv->reducer_contexts);
524        }
525        cb_free(priv->reducers);
526        cb_free(priv);
527    }
528    cb_free(ctx);
529
530    return NULL;
531}
532
533
534void free_view_reducer_ctx(view_reducer_ctx_t *ctx)
535{
536    unsigned i;
537    reducer_private_t *priv;
538
539    if (ctx == NULL) {
540        return;
541    }
542
543    priv = (reducer_private_t *) ctx->priv;
544    for (i = 0; i < priv->num_reducers; ++i) {
545        mapreduce_free_context(priv->reducer_contexts[i].mapreduce_ctx);
546    }
547    cb_free(priv->reducer_contexts);
548    cb_free(priv->reducers);
549    cb_free(priv);
550    cb_free((void *) ctx->error);
551    cb_free(ctx);
552}
553
554
555static void add_error_message(view_reducer_ctx_t *red_ctx, int rereduce)
556{
557   reducer_private_t *priv = (reducer_private_t *) red_ctx->priv;
558   char *error_msg;
559
560   if (red_ctx->error != NULL) {
561       cb_free((void *) red_ctx->error);
562       red_ctx->error = NULL;
563   }
564
565   if (priv->builtin_error != VIEW_REDUCER_SUCCESS) {
566       const char *base_msg = builtin_reducer_error_msg[priv->builtin_error];
567
568       cb_assert(priv->mapreduce_error == MAPREDUCE_SUCCESS);
569       if (!rereduce && (priv->error_key != NULL)) {
570           error_msg = (char *) cb_malloc(strlen(base_msg) + 12 + strlen(priv->error_key));
571           cb_assert(error_msg != NULL);
572           sprintf(error_msg, "%s (key %s)", base_msg, priv->error_key);
573       } else {
574           error_msg = cb_strdup(base_msg);
575           cb_assert(error_msg != NULL);
576       }
577       if (priv->error_key != NULL) {
578           cb_free((void *) priv->error_key);
579           priv->error_key = NULL;
580       }
581   } else {
582       cb_assert(priv->mapreduce_error != MAPREDUCE_SUCCESS);
583       if (priv->error_msg != NULL) {
584           error_msg = priv->error_msg;
585       } else {
586           if (priv->mapreduce_error == MAPREDUCE_TIMEOUT) {
587               error_msg = cb_strdup("function timeout");
588               cb_assert(error_msg != NULL);
589           } else {
590               error_msg = (char*)cb_malloc(64);
591               cb_assert(error_msg != NULL);
592               sprintf(error_msg, "mapreduce error: %d", priv->mapreduce_error);
593           }
594       }
595   }
596
597   red_ctx->error = (const char *) error_msg;
598}
599
600
601couchstore_error_t view_btree_reduce(char *dst,
602                                     size_t *size_r,
603                                     const nodelist *leaflist,
604                                     int count,
605                                     void *ctx)
606{
607    view_reducer_ctx_t *red_ctx = (view_reducer_ctx_t *) ctx;
608    reducer_private_t *priv = (reducer_private_t *) red_ctx->priv;
609    unsigned i;
610    reducer_fn_t reducer;
611    const nodelist *n;
612    int c;
613    couchstore_error_t ret = COUCHSTORE_SUCCESS;
614
615    /* The reduce function is only called (in btree_modify.cc) if there
616       are any items */
617    cb_assert(count > 0);
618    cb_assert(leaflist != NULL);
619
620    std::vector<view_btree_value_t> values;
621    try {
622        values.resize(count);
623    } catch (const std::bad_alloc&) {
624        return COUCHSTORE_ERROR_ALLOC_FAIL;
625    }
626    view_btree_reduction_t red{};
627    mapreduce_json_list_t key_list{};
628    mapreduce_json_list_t value_list{};
629
630    for (n = leaflist, c = 0; n != NULL && c < count; n = n->next, ++c) {
631        ret = decode_view_btree_value(n->data.buf, n->data.size, values[c]);
632        if (ret != COUCHSTORE_SUCCESS) {
633            goto out;
634        }
635        set_bit(&red.partitions_bitmap, values[c].partition);
636        red.kv_count += values[c].num_values;
637    }
638
639    value_list.values = (mapreduce_json_t*)cb_calloc(red.kv_count,
640                                                     sizeof(mapreduce_json_t));
641    if (value_list.values == NULL) {
642        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
643        goto out;
644    }
645    key_list.values = (mapreduce_json_t*)cb_calloc(red.kv_count,
646                                                   sizeof(mapreduce_json_t));
647    if (key_list.values == NULL) {
648        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
649        goto out;
650    }
651
652    for (n = leaflist, c = 0; n != NULL && c < count; n = n->next, ++c) {
653        auto& v = values[c];
654        sized_buf json_key{};
655        auto ret =
656                decode_view_btree_json_key(n->key.buf, n->key.size, json_key);
657        if (ret != COUCHSTORE_SUCCESS) {
658            goto out;
659        }
660        for (i = 0; i < v.num_values; ++i) {
661            value_list.values[value_list.length].length = v.values[i].size;
662            value_list.values[value_list.length].json = v.values[i].buf;
663            value_list.length++;
664            key_list.values[key_list.length].length = json_key.size;
665            key_list.values[key_list.length].json = json_key.buf;
666            key_list.length++;
667        }
668    }
669
670    if (priv->num_reducers > 0) {
671        red.reduce_values =
672                (sized_buf*)cb_calloc(priv->num_reducers, sizeof(sized_buf));
673        if (red.reduce_values == NULL) {
674            ret = COUCHSTORE_ERROR_ALLOC_FAIL;
675            goto out;
676        }
677    }
678
679    red.num_values = priv->num_reducers;
680    for (i = 0; i < priv->num_reducers; ++i) {
681        sized_buf buf;
682        reducer = priv->reducers[i];
683
684        ret = (*reducer)(
685                &key_list, &value_list, &priv->reducer_contexts[i], &buf);
686        if (ret != COUCHSTORE_SUCCESS) {
687            add_error_message(red_ctx, 0);
688            goto out;
689        }
690
691        red.reduce_values[i] = buf;
692    }
693
694    ret = encode_view_btree_reduction(&red, dst, size_r);
695
696 out:
697     if (red.reduce_values != NULL) {
698         for (i = 0; i < red.num_values; ++i) {
699             cb_free(red.reduce_values[i].buf);
700         }
701         cb_free(red.reduce_values);
702     }
703    free_json_key_list(key_list);
704    cb_free(value_list.values);
705
706    return ret;
707}
708
709couchstore_error_t view_btree_rereduce(char *dst,
710                                       size_t *size_r,
711                                       const nodelist *leaflist,
712                                       int count,
713                                       void *ctx)
714{
715    view_reducer_ctx_t *red_ctx = (view_reducer_ctx_t *) ctx;
716    reducer_private_t *priv = (reducer_private_t *) red_ctx->priv;
717    unsigned i;
718    reducer_fn_t reducer;
719    const nodelist *n;
720    int c;
721    couchstore_error_t ret = COUCHSTORE_SUCCESS;
722
723    std::vector<mapreduce_json_t> json_values_list;
724    std::vector<view_btree_reduction_t> reductions;
725    view_btree_reduction_t red{};
726    red.num_values = priv->num_reducers;
727    try {
728        red.buffer.resize(red.num_values * sizeof(sized_buf));
729        json_values_list.resize(count);
730        reductions.resize(count);
731    } catch (const std::bad_alloc&) {
732        return COUCHSTORE_ERROR_ALLOC_FAIL;
733    }
734    red.reduce_values = reinterpret_cast<sized_buf*>(red.buffer.data());
735
736    // The json_values_list vector is large enough to hold count values, the
737    // value_list.length is updated as actual json values are assigned further
738    // down.
739    mapreduce_json_list_t value_list{};
740    value_list.values = json_values_list.data();
741
742
743    for (n = leaflist, c = 0; n != NULL && c < count; n = n->next, ++c) {
744        ret = decode_view_btree_reduction(n->pointer->reduce_value.buf,
745                                          n->pointer->reduce_value.size,
746                                          reductions[c]);
747        if (ret != COUCHSTORE_SUCCESS) {
748            return ret;
749        }
750
751        union_bitmaps(&red.partitions_bitmap, &reductions[c].partitions_bitmap);
752        cb_assert(reductions[c].num_values == priv->num_reducers);
753        red.kv_count += reductions[c].kv_count;
754    }
755
756    for (i = 0; i < red.num_values; ++i) {
757        sized_buf buf{};
758
759        for (n = leaflist, c = 0; n != NULL && c < count; n = n->next, ++c) {
760            view_btree_reduction_t& r = reductions[c];
761
762            value_list.values[value_list.length].json = r.reduce_values[i].buf;
763            value_list.values[value_list.length].length =
764                    r.reduce_values[i].size;
765            // Increase the length for each assigned pointer
766            value_list.length++;
767        }
768
769        reducer = priv->reducers[i];
770        ret = (*reducer)(NULL, &value_list, &priv->reducer_contexts[i], &buf);
771        if (ret != COUCHSTORE_SUCCESS) {
772            add_error_message(red_ctx, 1);
773            return ret;
774        }
775
776        value_list.length = 0;
777        red.reduce_values[i] = buf;
778    }
779
780    ret = encode_view_btree_reduction(&red, dst, size_r);
781
782    for (i = 0; i < red.num_values; ++i) {
783        cb_free(red.reduce_values[i].buf);
784    }
785
786    return ret;
787}
788