xref: /3.0.2-MP2/couchstore/src/views/util.c (revision d8a1d022)
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 *
8 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 * use this file except in compliance with the License. You may obtain a copy of
10 * the License at
11 *
12 *  http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 * License for the specific language governing permissions and limitations under
18 * the License.
19 **/
20
21#include <stdlib.h>
22#include <string.h>
23#include "util.h"
24#include "../util.h"
25#include "../bitfield.h"
26#include "collate_json.h"
27
28
29int view_key_cmp(const sized_buf *key1, const sized_buf *key2,
30                 const void *user_ctx)
31{
32    uint16_t json_key1_len = decode_raw16(*((raw_16 *) key1->buf));
33    uint16_t json_key2_len = decode_raw16(*((raw_16 *) key2->buf));
34    sized_buf json_key1;
35    sized_buf json_key2;
36    sized_buf doc_id1;
37    sized_buf doc_id2;
38    int res;
39
40    (void)user_ctx;
41
42    json_key1.buf = key1->buf + sizeof(uint16_t);
43    json_key1.size = json_key1_len;
44    json_key2.buf = key2->buf + sizeof(uint16_t);
45    json_key2.size = json_key2_len;
46
47    res = CollateJSON(&json_key1, &json_key2, kCollateJSON_Unicode);
48
49    if (res == 0) {
50        doc_id1.buf = key1->buf + sizeof(uint16_t) + json_key1.size;
51        doc_id1.size = key1->size - sizeof(uint16_t) - json_key1.size;
52        doc_id2.buf = key2->buf + sizeof(uint16_t) + json_key2.size;
53        doc_id2.size = key2->size - sizeof(uint16_t) - json_key2.size;
54
55        res = ebin_cmp(&doc_id1, &doc_id2);
56    }
57
58    return res;
59}
60
61
62int view_id_cmp(const sized_buf *key1, const sized_buf *key2,
63                const void *user_ctx)
64{
65    (void)user_ctx;
66    return ebin_cmp(key1, key2);
67}
68
69
70int read_view_record(FILE *in, void **buf, void *ctx)
71{
72    uint32_t len, vlen;
73    uint16_t klen;
74    uint8_t op = 0;
75    view_file_merge_record_t *rec;
76    view_file_merge_ctx_t *merge_ctx = (view_file_merge_ctx_t *) ctx;
77
78    /* On disk format is a bit weird, but it's compatible with what
79       Erlang's file_sorter module requires. */
80
81    if (fread(&len, sizeof(len), 1, in) != 1) {
82        if (feof(in)) {
83            return 0;
84        } else {
85            return FILE_MERGER_ERROR_FILE_READ;
86        }
87    }
88    if (merge_ctx->type == INCREMENTAL_UPDATE_VIEW_RECORD) {
89        if (fread(&op, sizeof(rec->op), 1, in) != 1) {
90            return FILE_MERGER_ERROR_FILE_READ;
91        }
92    }
93    if (fread(&klen, sizeof(klen), 1, in) != 1) {
94        return FILE_MERGER_ERROR_FILE_READ;
95    }
96
97    klen = ntohs(klen);
98    vlen = len - sizeof(klen) - klen;
99    if (merge_ctx->type == INCREMENTAL_UPDATE_VIEW_RECORD) {
100        vlen -= sizeof(op);
101    }
102
103    rec = (view_file_merge_record_t *) malloc(sizeof(*rec) + klen + vlen);
104    if (rec == NULL) {
105        return FILE_MERGER_ERROR_ALLOC;
106    }
107
108    rec->op = op;
109    rec->ksize = klen;
110    rec->vsize = vlen;
111
112    if (fread(VIEW_RECORD_KEY(rec), klen + vlen, 1, in) != 1) {
113        free(rec);
114        return FILE_MERGER_ERROR_FILE_READ;
115    }
116
117    *buf = (void *) rec;
118
119    return klen + vlen;
120}
121
122
123file_merger_error_t write_view_record(FILE *out, void *buf, void *ctx)
124{
125    view_file_merge_record_t *rec = (view_file_merge_record_t *) buf;
126    uint16_t klen = htons((uint16_t) rec->ksize);
127    uint32_t len;
128    view_file_merge_ctx_t *merge_ctx = (view_file_merge_ctx_t *) ctx;
129
130    len = (uint32_t)  sizeof(klen) + rec->ksize + rec->vsize;
131    if (merge_ctx->type == INCREMENTAL_UPDATE_VIEW_RECORD) {
132        len += (uint32_t) sizeof(rec->op);
133    }
134
135    if (fwrite(&len, sizeof(len), 1, out) != 1) {
136        return FILE_MERGER_ERROR_FILE_WRITE;
137    }
138    if (merge_ctx->type == INCREMENTAL_UPDATE_VIEW_RECORD) {
139        if (fwrite(&rec->op, sizeof(rec->op), 1, out) != 1) {
140            return FILE_MERGER_ERROR_FILE_WRITE;
141        }
142    }
143    if (fwrite(&klen, sizeof(klen), 1, out) != 1) {
144        return FILE_MERGER_ERROR_FILE_WRITE;
145    }
146    if (fwrite(VIEW_RECORD_KEY(rec), rec->ksize + rec->vsize, 1, out) != 1) {
147        return FILE_MERGER_ERROR_FILE_WRITE;
148    }
149
150    return FILE_MERGER_SUCCESS;
151}
152
153
154int compare_view_records(const void *r1, const void *r2, void *ctx)
155{
156    view_file_merge_ctx_t *merge_ctx = (view_file_merge_ctx_t *) ctx;
157    view_file_merge_record_t *rec1 = (view_file_merge_record_t *) r1;
158    view_file_merge_record_t *rec2 = (view_file_merge_record_t *) r2;
159    sized_buf k1, k2;
160
161    k1.size = rec1->ksize;
162    k1.buf = VIEW_RECORD_KEY(rec1);
163
164    k2.size = rec2->ksize;
165    k2.buf = VIEW_RECORD_KEY(rec2);
166
167    return merge_ctx->key_cmp_fun(&k1, &k2, merge_ctx->user_ctx);
168}
169
170
171size_t dedup_view_records_merger(file_merger_record_t **records, size_t len, void *ctx)
172{
173    size_t i;
174    size_t max = 0;
175    (void) ctx;
176
177    for (i = 1; i < len; i++) {
178        if (records[max]->filenum < records[i]->filenum) {
179            max = i;
180        }
181    }
182
183    return max;
184}
185
186
187void free_view_record(void *record, void *ctx)
188{
189    (void) ctx;
190    free(record);
191}
192
193
194LIBCOUCHSTORE_API
195char *couchstore_read_line(FILE *in, char *buf, int size)
196{
197    size_t len;
198
199    if (fgets(buf, size, in) != buf) {
200        return NULL;
201    }
202
203    len = strlen(buf);
204    if ((len >= 1) && (buf[len - 1] == '\n')) {
205        buf[len - 1] = '\0';
206    }
207
208    return buf;
209}
210
211
212LIBCOUCHSTORE_API
213uint64_t couchstore_read_int(FILE *in, char *buf, size_t size,
214                                                  couchstore_error_t *ret)
215{
216    uint64_t val;
217    *ret = COUCHSTORE_SUCCESS;
218
219    if (couchstore_read_line(in, buf, size) != buf) {
220        *ret = COUCHSTORE_ERROR_READ;
221        return 0;
222    }
223
224    if (sscanf(buf, "%"SCNu64, &val) != 1) {
225        *ret = COUCHSTORE_ERROR_READ;
226        return 0;
227    }
228
229    return val;
230}
231
232
233void set_error_info(const view_btree_info_t *info,
234                    const char *red_error,
235                    couchstore_error_t ret,
236                    view_error_t *error_info)
237{
238    char buf[4096];
239    size_t len = 0;
240
241    if (ret == COUCHSTORE_SUCCESS) {
242        return;
243    }
244
245    /* TODO: add more human friendly messages for other error types */
246    switch (ret) {
247    case COUCHSTORE_ERROR_REDUCTION_TOO_LARGE:
248        len = snprintf(buf, 4096, "(view %d) reduction too large, ret = %d",
249                                                                info->view_id,
250                                                                ret);
251        buf[len] = '\0';
252
253    default:
254        len = snprintf(buf, 4096, "(view %d) failed, ret = %d", info->view_id, ret);
255        buf[len] = '\0';
256    }
257
258    if (len) {
259        error_info->error_msg = (const char *) strdup(buf);
260    }
261
262    if (red_error) {
263        error_info->error_msg = (const char *) strdup(red_error);
264    }
265
266    if (info->num_reducers) {
267        error_info->view_name = (const char *) strdup(info->names[0]);
268    }
269}
270