xref: /6.6.0/subjson/subdoc/operations.cc (revision 3d8f7444)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3*     Copyright 2015 Couchbase, Inc
4*
5*   Licensed under the Apache License, Version 2.0 (the "License");
6*   you may not use this file except in compliance with the License.
7*   You may obtain a copy of the License at
8*
9*       http://www.apache.org/licenses/LICENSE-2.0
10*
11*   Unless required by applicable law or agreed to in writing, software
12*   distributed under the License is distributed on an "AS IS" BASIS,
13*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*   See the License for the specific language governing permissions and
15*   limitations under the License.
16*/
17
18#define INCLUDE_SUBDOC_STRING_SRC
19#define INCLUDE_SUBDOC_NTOHLL
20#define NOMINMAX // For Visual Studio
21
22#include "operations.h"
23#include "validate.h"
24#include "util.h"
25#include <errno.h>
26#include <inttypes.h>
27#include <string>
28#include <limits>
29
30using Subdoc::Loc;
31using Subdoc::Error;
32using Subdoc::Operation;
33using Subdoc::Path;
34using Subdoc::Match;
35using Subdoc::Command;
36
37static Loc loc_COMMA(",", 1);
38static Loc loc_QUOTE("\"", 1);
39static Loc loc_COMMA_QUOTE(",\"", 2);
40static Loc loc_QUOTE_COLON("\":", 2);
41
42/**
43 * Performs common matching using the currently designated path. Note that
44 * unlike do_get(), a missing match is not an error. This is the reason do_get()
45 * and do_match_common() are separate functions, as do_get() (i.e. a simple get
46 * operation) considers a non-complete match as a failure.
47 *
48 * @return success if no parse error or mismatch happened, failure otherwise.
49 */
50Error
51Operation::do_match_common(Match::SearchOptions options)
52{
53    m_match.extra_options = options;
54    m_match.exec_match(m_doc, m_path, m_jsn);
55
56    if (m_match.matchres == JSONSL_MATCH_TYPE_MISMATCH) {
57        return Error::PATH_MISMATCH;
58    } else if (m_match.status != JSONSL_ERROR_SUCCESS) {
59        if (m_match.status == JSONSL_ERROR_LEVELS_EXCEEDED) {
60            return Error::DOC_ETOODEEP;
61        } else {
62            return Error::DOC_NOTJSON;
63        }
64    } else {
65        return Error::SUCCESS;
66    }
67}
68
69Error
70Operation::do_get()
71{
72    if (m_match.matchres != JSONSL_MATCH_COMPLETE) {
73        return Error::PATH_ENOENT;
74    }
75
76    m_result->m_match = m_match.loc_deepest;
77    return Error::SUCCESS;
78}
79
80/* Start at the beginning of the buffer, stripping first comma */
81#define STRIP_FIRST_COMMA 1
82
83/* Start at the end of the buffer, stripping last comma */
84#define STRIP_LAST_COMMA 2
85
86static void
87strip_comma(Loc *loc, int mode)
88{
89    unsigned ii;
90    if (mode == STRIP_FIRST_COMMA) {
91        for (ii = 0; ii < loc->length; ii++) {
92            if (loc->at[ii] == ',') {
93                loc->at += (ii + 1);
94                loc->length -= (ii + 1);
95                return;
96            }
97        }
98    } else {
99        for (ii = loc->length; ii; ii--) {
100            if (loc->at[ii-1] == ',') {
101                loc->length = ii-1;
102                return;
103            }
104        }
105    }
106}
107
108Error
109Operation::do_remove()
110{
111    Error rv = do_match_common(Match::GET_FOLLOWING_SIBLINGS);
112
113    if (!rv.success()) {
114        return rv;
115    } else if (m_match.matchres != JSONSL_MATCH_COMPLETE) {
116        return Error::PATH_ENOENT;
117    }
118
119    // Deepest is the actual match
120    const Loc& match_loc = m_match.loc_deepest;
121    /*
122     * If match is the _last_ item, we need to strip the _last_ comma from
123     * doc[0] (since doc[1] will never have a trailing comma); e.g.
124     *  NEWDOC[0] = { ..., ..., ... (---> , <---)
125     *  [deleted]
126     *  NEWDOC[1] = } // Never any commas here!
127     *
128     * If match is the _first_ item, we need to strip the _first_ comma
129     * from newdoc[1]:
130     *
131     *  NEWDOC[0] = { // never any commas here!
132     *  [deleted]
133     *  NEWDOC[1] = (---> , <---) ... , ... }
134     *
135     *
136     *
137     * If match is any other item, we need to strip the _last_ comma from
138     * doc[0], which is the comma that preceded this item.
139     */
140
141    /* Remove the matches, starting from the beginning of the key */
142    if (!m_match.loc_key.empty()) {
143        newdoc_at(0).end_at_begin(m_doc, m_match.loc_key, Loc::NO_OVERLAP);
144    } else {
145        newdoc_at(0).end_at_begin(m_doc, match_loc, Loc::NO_OVERLAP);
146    }
147
148    newdoc_at(1).begin_at_end(m_doc, match_loc, Loc::NO_OVERLAP);
149
150    if (m_match.num_siblings) {
151        if (m_match.is_last()) {
152            /*
153             * NEWDOC[0] = [a,b,c, <-- Strip here
154             * MATCH     = d
155             * NEWDOC[1] = ]
156             */
157            strip_comma(&newdoc_at(0), STRIP_LAST_COMMA);
158        } else {
159            /*
160             * NEWDOC[0] = [a,b,
161             * MATCH     = c
162             * NEWDOC[1] = Strip here -->, d]
163             */
164            strip_comma(&newdoc_at(1), STRIP_FIRST_COMMA);
165        }
166    }
167
168    m_result->m_newlen = 2;
169    return Error::SUCCESS;
170}
171
172Error
173Operation::do_store_dict()
174{
175    // Check that the last element is not an array first.
176    if (m_optype != Command::REPLACE &&
177            path()[path().size()-1].ptype == JSONSL_PATH_NUMERIC) {
178        return Error::PATH_EINVAL;
179    }
180    if (m_match.matchres != JSONSL_MATCH_COMPLETE) {
181        if (m_optype == Command::REPLACE) {
182            // Nothing to replace
183            return Error::PATH_ENOENT;
184        }
185        if (!m_match.immediate_parent_found) {
186            if (m_optype.is_mkdir_p()) {
187                return do_mkdir_p(MKDIR_P_DICT);
188            } else {
189                return Error::PATH_ENOENT;
190            }
191        }
192        // Otherwise, the immediate parent is found, and we can
193        // continue to add/upsert
194    } else if (m_match.matchres == JSONSL_MATCH_COMPLETE) {
195        if (m_optype.base() == Command::DICT_ADD) {
196            return Error::DOC_EEXISTS;
197        }
198    }
199
200    if (m_match.matchres == JSONSL_MATCH_COMPLETE) {
201        const Loc& match_loc = m_match.loc_deepest;
202        /* 1. Remove the old value from the first segment */
203        newdoc_at(0).end_at_begin(m_doc, match_loc, Loc::NO_OVERLAP);
204
205        /* 2. Insert the new value */
206        newdoc_at(1) = m_userval;
207
208        /* 3. Insert the rest of the document */
209        newdoc_at(2).begin_at_end(m_doc, match_loc, Loc::NO_OVERLAP);
210        m_result->m_newlen = 3;
211
212    } else if (m_match.immediate_parent_found) {
213        // Deepest is parent
214        const Loc& parent_loc = m_match.loc_deepest;
215
216        newdoc_at(0).end_at_end(m_doc, parent_loc, Loc::NO_OVERLAP);
217        /*TODO: The key might have a literal '"' in it, which has been escaped? */
218        if (m_match.num_siblings) {
219            newdoc_at(1) = loc_COMMA_QUOTE; /* ," */
220        } else {
221            newdoc_at(1) = loc_QUOTE /* " */;
222        }
223
224        /* Create the actual key: */
225        auto& comp = m_path->back();
226        newdoc_at(2).at = comp.pstr;
227        newdoc_at(2).length = comp.len;
228
229        /* Close the quote and add the dictionary key */
230        newdoc_at(3) = loc_QUOTE_COLON; /* ": */
231        /* new value */
232        newdoc_at(4) = m_userval;
233        /* Closing tokens */
234        newdoc_at(5).begin_at_end(m_doc, parent_loc, Loc::OVERLAP);
235        m_result->m_newlen = 6;
236    }
237    return Error::SUCCESS;
238}
239
240/**
241 * This function will inspect the match to find the deepest most parent
242 * and add missing entries in the document from the path. It will then
243 * insert the given value (either as a value to the dictionary key, or
244 * as an array with a single element (being the value) as the value for
245 * the dictionary key.
246 *
247 * In all cases, intermediate non-existing path elements must all refer
248 * to dictionary keys, not array elements (since array elements are in
249 * sequence there is no course of action if the array is empty and the
250 * path refers to a middle element).
251 *
252 * @param mode How to insert the value
253 * @return status
254 */
255Error
256Operation::do_mkdir_p(MkdirPMode mode)
257{
258    unsigned ii;
259    const Loc& parent_loc = m_match.loc_deepest;
260    newdoc_at(0).end_at_end(m_doc, parent_loc, Loc::NO_OVERLAP);
261
262    /* doc_new LAYOUT:
263     *
264     * [0] = HEADER
265     * [1] = _P header (bkbuf_extra)
266     * [2] = USER TEXT
267     * [3] = _P trailer (bkbuf_extra)
268     * [4] = TRAILER
269     */
270
271    /* figure out the components missing */
272    /* THIS IS RESERVED FOR doc_new[1]! */
273    if (m_match.num_siblings) {
274        m_result->m_bkbuf += ',';
275    }
276
277    /* Insert the first item. This is a dictionary key without any object
278     * wrapper: */
279    const Path::Component* comp = &m_path->get_component(m_match.match_level);
280    if (comp->ptype == JSONSL_PATH_NUMERIC) {
281        // If it's *not* a dictionary key, don't insert it!
282        return Error::PATH_ENOENT;
283    }
284    m_result->m_bkbuf += '"';
285    m_result->m_bkbuf.append(comp->pstr, comp->len);
286    m_result->m_bkbuf += "\":";
287
288    /* The next set of components must be created as entries within the
289     * newly created key */
290    for (ii = m_match.match_level + 1; ii < m_path->size(); ii++) {
291        comp = &m_path->get_component(ii);
292        if (comp->ptype != JSONSL_PATH_STRING) {
293            return Error::PATH_ENOENT;
294        }
295        m_result->m_bkbuf += "{\"";
296        m_result->m_bkbuf.append(comp->pstr, comp->len);
297        m_result->m_bkbuf += "\":";
298    }
299    if (mode == MKDIR_P_ARRAY) {
300        m_result->m_bkbuf += '[';
301    }
302
303    newdoc_at(1).length = m_result->m_bkbuf.size();
304
305    if (mode == MKDIR_P_ARRAY) {
306        m_result->m_bkbuf += ']';
307    }
308
309    for (ii = m_match.match_level+1; ii < m_path->size(); ii++) {
310        m_result->m_bkbuf += '}';
311    }
312    newdoc_at(3).length = m_result->m_bkbuf.size() - newdoc_at(1).length;
313
314    /* Set the buffers */
315    newdoc_at(1).at = m_result->m_bkbuf.data();
316    newdoc_at(3).at = m_result->m_bkbuf.data() + newdoc_at(1).length;
317    newdoc_at(2) = m_userval;
318
319    newdoc_at(4).begin_at_end(m_doc, parent_loc, Loc::OVERLAP);
320    m_result->m_newlen = 5;
321
322    return Error::SUCCESS;
323}
324
325/* Inserts a single element into an empty array */
326Error
327Operation::insert_singleton_element()
328{
329    const Loc& array_loc = m_match.loc_deepest;
330    /* First segment is ... [ */
331    newdoc_at(0).end_at_begin(m_doc, array_loc, Loc::OVERLAP);
332    /* User: */
333    newdoc_at(1) = m_userval;
334    /* Last segment is ... ] */
335    newdoc_at(2).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
336
337    m_result->m_newlen = 3;
338    return Error::SUCCESS;
339}
340
341Error
342Operation::do_list_prepend()
343{
344    Error rv;
345
346    // Search for first element, perform the search, then pop it
347    if (m_path->add_array_index(0) != JSONSL_ERROR_SUCCESS) {
348        return Error::PATH_E2BIG;
349    }
350
351    rv = do_match_common(Match::GET_MATCH_ONLY);
352    m_path->pop();
353
354    if (!rv.success()) {
355        return rv;
356    }
357
358    if (m_match.matchres != JSONSL_MATCH_COMPLETE) {
359        if (m_match.immediate_parent_found) {
360            return insert_singleton_element();
361        } else if (m_optype.is_mkdir_p()) {
362            return do_mkdir_p(MKDIR_P_ARRAY);
363        } else {
364            return Error::PATH_ENOENT;
365        }
366    }
367
368    /* LAYOUT:
369     * NEWDOC[0] [....
370     * NEWDOC[1] = USER
371     * NEWDOC[2] = ,
372     * NEWDOC[3] = REST
373     */
374
375    const Loc& orig_firstloc = m_match.loc_deepest;
376    newdoc_at(0).end_at_begin(m_doc, orig_firstloc, Loc::NO_OVERLAP);
377    newdoc_at(1) = m_userval;
378    newdoc_at(2) = loc_COMMA;
379    newdoc_at(3).begin_at_begin(m_doc, orig_firstloc);
380
381    m_result->m_newlen = 4;
382    return Error::SUCCESS;
383}
384
385Error
386Operation::do_list_append()
387{
388    Error rv;
389
390    // Find the match itself, no magic needed!
391    bool is_unique = m_optype.base() == Command::ARRAY_ADD_UNIQUE;
392    if (is_unique) {
393        m_match.ensure_unique = m_userval;
394    }
395
396    rv = do_match_common(Match::GET_MATCH_ONLY);
397    if (!rv.success()) {
398        // Parse error (ENOENT excluded)
399        return rv;
400    }
401
402    if (m_match.matchres != JSONSL_MATCH_COMPLETE) {
403        // Not complete. Determine if mkdir_p should be used
404        if (m_optype.is_mkdir_p()) {
405            return do_mkdir_p(MKDIR_P_ARRAY);
406        } else {
407            return Error::PATH_ENOENT;
408        }
409    }
410
411    // All other errors should be handled above
412    SUBDOC_ASSERT(m_match.matchres == JSONSL_MATCH_COMPLETE);
413
414    // Incorrect terminal type!
415    if (m_match.type != JSONSL_T_LIST) {
416        return Error::PATH_MISMATCH;
417    }
418
419    if (is_unique && m_match.unique_item_found) {
420        // Unique item already exists
421        return Error::DOC_EEXISTS;
422    }
423    const Loc& array_loc = m_match.loc_deepest;
424
425    if (m_match.num_children == 0) {
426        // Special case, no children; append and prepend are the same here!
427        return insert_singleton_element();
428    }
429
430    /* Last element */
431    newdoc_at(0).end_at_end(m_doc, array_loc, Loc::NO_OVERLAP);
432    /* Insert comma */
433    newdoc_at(1) = loc_COMMA;
434    /* User */
435    newdoc_at(2) = m_userval;
436    /* Parent end */
437    newdoc_at(3).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
438
439    m_result->m_newlen = 4;
440    return Error::SUCCESS;
441}
442
443Error
444Operation::do_insert()
445{
446    auto& lastcomp = m_path->get_component(m_path->size()-1);
447    if (lastcomp.ptype != JSONSL_PATH_NUMERIC) {
448        return Error::PATH_EINVAL;
449    }
450    if (lastcomp.is_neg) {
451        // Negative paths are invalid for insert operations
452        return Error::PATH_EINVAL;
453    }
454
455    Error status = do_match_common(Match::GET_MATCH_ONLY);
456    if (!status.success()) {
457        return status;
458    }
459
460    if (m_match.matchres == JSONSL_MATCH_COMPLETE) {
461        const Loc& match_loc = m_match.loc_deepest;
462
463        /*
464         * DOCNEW[0] = ... [
465         * DOCNEW[1] = USER
466         * DOCNEW[2] = ,
467         * DOCNEW[3] = MATCH
468         */
469        m_result->m_newlen = 4;
470        newdoc_at(0).end_at_begin(m_doc, match_loc, Loc::NO_OVERLAP);
471        newdoc_at(1) = m_userval;
472        newdoc_at(2) = loc_COMMA;
473        newdoc_at(3).begin_at_begin(m_doc, match_loc);
474        return Error::SUCCESS;
475
476    } else if (m_match.immediate_parent_found) {
477        const Loc& array_loc = m_match.loc_deepest;
478        if (m_match.num_siblings == 0 && lastcomp.idx == 0) {
479            // Equivalent to prepend/push_first
480            /*
481             * DOCNEW[0] = ... [
482             * DOCNEW[1] = USER
483             * DOCNEW[2] = ] ...
484             */
485            m_result->m_newlen = 3;
486            newdoc_at(0).end_at_begin(m_doc, array_loc, Loc::OVERLAP);
487            newdoc_at(1) = m_userval;
488            newdoc_at(2).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
489            return Error::SUCCESS;
490
491        } else if (lastcomp.idx == m_match.num_siblings) {
492            // Equivalent to append/push_last
493            /*
494             * (assume DOC = [a,b,c,d,e]
495             * DOCNEW[0] = e (last char before ']')
496             * DOCNEW[1] = , (since there are items in the list)
497             * DOCNEW[2] = USER
498             * DOCNEW[3] = ]
499             */
500            m_result->m_newlen = 4;
501            newdoc_at(0).end_at_end(m_doc, array_loc, Loc::NO_OVERLAP);
502            newdoc_at(1) = loc_COMMA;
503            newdoc_at(2) = m_userval;
504            newdoc_at(3).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
505            return Error::SUCCESS;
506
507        } else {
508            return Error::PATH_ENOENT;
509        }
510    } else {
511        return Error::PATH_ENOENT;
512    }
513}
514
515static Error
516parse_int64(const Loc& orig, int64_t& outval)
517{
518    const char *cur = orig.at;
519    size_t n = orig.length;
520
521    if (!n) {
522        return Error::VALUE_EMPTY; // Empty
523    }
524    if (*cur == '-') {
525        cur++;
526        if (!--n) {
527            return Error::DELTA_EINVAL;
528        }
529    }
530    if (*cur == '0') {
531        // Can't start with a zero. It's either a leading zero or an empty
532        // delta
533        return Error::DELTA_EINVAL;
534    }
535
536    outval = 0;
537    for (size_t ii = 0; ii < n; ii++) {
538        if (!isdigit(cur[ii])) {
539            return Error::DELTA_EINVAL; // Not a number!
540        }
541        // Get the numeric value of the digit, with '0' being the lowest
542        // value digit character in the ascii table, and with digits appearing
543        // in order, such that '9' (0x39) - '0' (0x30) == 0
544
545        uint64_t newval = (outval * 10) + (cur[ii] - '0');
546        if (newval < static_cast<uint64_t>(outval) ||
547                newval > std::numeric_limits<int64_t>::max()) {
548            // mismatch
549            return Error::DELTA_EINVAL;
550        }
551        outval = static_cast<int64_t>(newval);
552    }
553    if (*orig.at == '-') {
554        outval *= -1;
555    }
556    return Error::SUCCESS;
557}
558
559Error
560Operation::do_arith_op()
561{
562    Error status;
563    int64_t delta;
564    int64_t numres = 0;
565    // Verify the digit first
566
567    status = parse_int64(m_userval, delta);
568    if (!status.success()) {
569        return status;
570    }
571
572    /* Find the number first */
573    status = do_match_common(m_optype.is_mkdir_p() ?
574            Match::GET_FOLLOWING_SIBLINGS : Match::GET_MATCH_ONLY);
575
576    if (status != Error::SUCCESS) {
577        return status;
578    }
579
580    if (m_match.matchres == JSONSL_MATCH_COMPLETE) {
581        const Loc& num_loc = m_match.loc_deepest;
582        if (m_match.type != JSONSL_T_SPECIAL) {
583            return Error::PATH_MISMATCH;
584        } else if (m_match.sflags & ~(JSONSL_SPECIALf_NUMERIC)) {
585            return Error::PATH_MISMATCH;
586        } else  {
587            errno = 0;
588            numres = strtoll(num_loc.at, NULL, 10);
589
590            if (errno == ERANGE) {
591                return Error::NUM_E2BIG;
592            }
593
594            /* Calculate what to place inside the buffer. We want to be gentle here
595             * and not force 64 bit C arithmetic to confuse users, so use proper
596             * integer overflow/underflow with a 64 (or rather, 63) bit limit. */
597            if (delta >= 0 && numres >= 0) {
598                if (std::numeric_limits<int64_t>::max() - delta < numres) {
599                    return Error::DELTA_OVERFLOW;
600                }
601            } else if (delta < 0 && numres < 0) {
602                if (delta < std::numeric_limits<int64_t>::min() - numres) {
603                    return Error::DELTA_OVERFLOW;
604                }
605            }
606
607            numres += delta;
608            m_result->m_numbuf = std::to_string(numres);
609        }
610    } else {
611        if (!m_optype.is_mkdir_p() && !m_match.immediate_parent_found) {
612            return Error::PATH_ENOENT;
613        }
614
615        if (m_match.type != JSONSL_T_OBJECT) {
616            return Error::PATH_ENOENT;
617        }
618
619        m_result->m_numbuf = std::to_string(delta);
620        m_userval.at = m_result->m_numbuf.data();
621        m_userval.length = m_result->m_numbuf.size();
622        m_optype = Command::DICT_ADD_P;
623        if ((status = do_store_dict()) != Error::SUCCESS) {
624            return status;
625        }
626        m_result->m_match = m_match.loc_deepest = m_userval;
627        return Error::SUCCESS;
628    }
629
630    // Unlike other ops, we modify the match value itself, as this is an
631    // in/out parameter
632
633    /* Preamble */
634    newdoc_at(0).end_at_begin(m_doc, m_match.loc_deepest, Loc::NO_OVERLAP);
635
636    /* New number */
637    newdoc_at(1).at = m_result->m_numbuf.data();
638    newdoc_at(1).length = m_result->m_numbuf.size();
639
640    /* Postamble */
641    newdoc_at(2).begin_at_end(m_doc, m_match.loc_deepest, Loc::NO_OVERLAP);
642    m_result->m_newlen = 3;
643
644    m_match.loc_deepest.at = m_result->m_numbuf.data();
645    m_match.loc_deepest.length = m_result->m_numbuf.size();
646    m_result->m_match = m_match.loc_deepest;
647    return Error::SUCCESS;
648}
649
650Error
651Operation::validate(int mode, int depth)
652{
653    if (!m_userval.empty()) {
654        int rv = Validator::validate(m_userval, m_jsn, depth, mode);
655        switch (rv) {
656        case JSONSL_ERROR_SUCCESS:
657            return Error::SUCCESS;
658        case JSONSL_ERROR_LEVELS_EXCEEDED:
659            return Error::VALUE_ETOODEEP;
660        default:
661            return Error::VALUE_CANTINSERT;
662        }
663    } else {
664        return Error::VALUE_EMPTY;
665    }
666}
667
668int
669Operation::get_maxdepth(DepthMode mode) const
670{
671    if (mode == DepthMode::PATH_HAS_NEWKEY) {
672        return (Limits::MAX_COMPONENTS + 1) - m_path->size();
673    } else {
674        return Limits::MAX_COMPONENTS - m_path->size();
675    }
676}
677
678Error
679Operation::op_exec(const char *pth, size_t npth)
680{
681    int rv = m_path->parse(pth, npth);
682    Error status;
683
684    if (rv != 0) {
685        if (rv == JSONSL_ERROR_LEVELS_EXCEEDED) {
686            return Error::PATH_E2BIG;
687        } else {
688            return Error::PATH_EINVAL;
689        }
690    }
691
692    switch (m_optype) {
693    case Command::GET:
694    case Command::EXISTS:
695        status = do_match_common(Match::GET_MATCH_ONLY);
696        if (status != Error::SUCCESS) {
697            return status;
698        }
699        return do_get();
700
701    case Command::DICT_ADD:
702    case Command::DICT_ADD_P:
703    case Command::DICT_UPSERT:
704    case Command::DICT_UPSERT_P:
705    case Command::REPLACE:
706        if (m_path->size() == 1) {
707            /* Can't perform these operations on the root element since they
708             * will invalidate the JSON or are otherwise meaningless. */
709            return Error::VALUE_CANTINSERT;
710        }
711
712        status = validate(Validator::PARENT_DICT, get_maxdepth(PATH_HAS_NEWKEY));
713        if (!status.success()) {
714            return status;
715        }
716        status = do_match_common(Match::GET_MATCH_ONLY);
717        if (!status.success()) {
718            return status;
719        }
720        return do_store_dict();
721
722    case Command::REMOVE:
723        if (m_path->size() == 1) {
724            // Can't remove root element!
725            return Error::VALUE_CANTINSERT;
726        }
727        return do_remove();
728
729    case Command::ARRAY_PREPEND:
730    case Command::ARRAY_PREPEND_P:
731        status = validate(Validator::PARENT_ARRAY, get_maxdepth(PATH_IS_PARENT));
732        if (!status.success()) {
733            return status;
734        }
735        return do_list_prepend();
736
737    case Command::ARRAY_APPEND:
738    case Command::ARRAY_APPEND_P:
739    case Command::ARRAY_ADD_UNIQUE:
740    case Command::ARRAY_ADD_UNIQUE_P: {
741        int validmode = Validator::PARENT_ARRAY;
742        if (m_optype.base() == Command::ARRAY_ADD_UNIQUE) {
743            // Uniqueness must contain a single, primitive value.
744            validmode |= Validator::VALUE_PRIMITIVE | Validator::VALUE_SINGLE;
745        }
746
747        status = validate(validmode, get_maxdepth(PATH_IS_PARENT));
748        if (!status.success()) {
749            return status;
750        }
751        return do_list_append();
752    }
753
754    case Command::ARRAY_INSERT:
755        status = validate(Validator::PARENT_ARRAY, get_maxdepth(PATH_HAS_NEWKEY));
756        if (!status.success()) {
757            return status;
758        }
759        return do_insert();
760
761    case Command::COUNTER:
762    case Command::COUNTER_P:
763        // no need to check for depth here, since if the path itself is too
764        // big, it will fail during path parse-time
765        return do_arith_op();
766
767    default:
768        return Error::GLOBAL_ENOSUPPORT;
769
770    }
771}
772
773Operation::Operation()
774: m_path(new Path()),
775  m_jsn(Match::jsn_alloc()),
776  m_optype(Command::GET),
777  m_result(NULL)
778{
779}
780
781Operation::~Operation()
782{
783    clear();
784    delete m_path;
785    Match::jsn_free(m_jsn);
786}
787
788void
789Operation::clear()
790{
791    m_path->clear();
792    m_match.clear();
793    m_userval.length = 0;
794    m_userval.at = NULL;
795    m_result = NULL;
796    m_optype = Command::GET;
797}
798
799Operation *
800subdoc_op_alloc()
801{
802    return new Operation();
803}
804
805void
806subdoc_op_free(Operation *op)
807{
808    delete op;
809}
810
811/* Misc */
812const char *
813Error::description() const
814{
815    switch (m_code) {
816    case Error::SUCCESS:
817        return "Success";
818    case Error::PATH_ENOENT:
819        return "Requested path does not exist in document";
820    case Error::PATH_MISMATCH:
821        return "The path specified treats an existing document entry as the wrong type";
822    case Error::PATH_EINVAL:
823        return "Path syntax error";
824    case Error::DOC_NOTJSON:
825        return "The document is not JSON";
826    case Error::DOC_EEXISTS:
827        return "The requested path already exists";
828    case Error::PATH_E2BIG:
829        return "The path is too big";
830    case Error::NUM_E2BIG:
831        return "The number specified by the path is too big";
832    case Error::DELTA_EINVAL:
833        return "Invalid delta: Either not a number, 0, or not within int64 range";
834    case Error::DELTA_OVERFLOW:
835        return "Performing the counter operation would result in an underflow or overflow";
836    case Error::VALUE_CANTINSERT:
837        return "The new value cannot be inserted in the context of the path, as it would invalidate the JSON";
838    case Error::VALUE_EMPTY:
839        return "Expected non-empty value for command";
840    case Error::VALUE_ETOODEEP:
841        return "Adding this value would make the document too deep";
842    case Error::GLOBAL_ENOMEM:
843        return "Couldn't allocate memory";
844    case Error::GLOBAL_ENOSUPPORT:
845        return "Operation not implemented";
846    case Error::GLOBAL_UNKNOWN_COMMAND:
847        return "Unrecognized command code";
848    case Error::DOC_ETOODEEP:
849        return "Document is too deep to parse";
850    default:
851        return "Unknown error code";
852    }
853}
854