xref: /6.6.0/subjson/subdoc/operations.cc (revision 65d3f053)
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
385static bool
386is_json_ws(char c) {
387    // (i.e. non-whitespace) character. RFC 7159 says:
388    // Insignificant whitespace is allowed before or after any of the six
389    // structural characters.
390    //
391    //   ws = *(
392    //           %x20 /              ; Space
393    //           %x09 /              ; Horizontal tab
394    //           %x0A /              ; Line feed or New line
395    //           %x0D )              ; Carriage return
396
397    switch (c) {
398    case 0x20:
399    case 0x09:
400    case 0x0A:
401    case 0x0D:
402        return true;
403    default:
404        return false;
405    }
406}
407
408Error
409Operation::do_empty_append()
410{
411    // Empty path. Do a custom parse/insertion
412    const char *a_end = (m_doc.at + m_doc.length) - 1;
413
414    // Find terminating bracket
415    for (; a_end != m_doc.at && isspace(*a_end); --a_end) {
416    }
417
418    if (a_end == m_doc.at || *a_end != ']') {
419        switch (*a_end) {
420        case ']':
421        case '}':
422            return Error::PATH_MISMATCH;
423        default:
424            return Error::DOC_NOTJSON;
425        }
426    }
427
428    // Find last comma, element, or beginning of the array.
429    // Rather than fully parse json, simply seek to the last significant
430    // JSON character
431    const char *e_comma = a_end - 1;
432    for (; e_comma != m_doc.at && is_json_ws(*e_comma); --e_comma) {
433    }
434
435    newdoc_at(0).assign(m_doc.at, (e_comma - m_doc.at) + 1);
436
437    if (*e_comma == '[' || *e_comma == ',') {
438        newdoc_at(1) = m_userval;
439        newdoc_at(2).assign(a_end, 1);
440        m_result->m_newlen = 3;
441    } else if (!isspace(*e_comma)) {
442        newdoc_at(1) = loc_COMMA;
443        newdoc_at(2) = m_userval;
444        newdoc_at(3).assign(a_end, 1);
445        m_result->m_newlen = 4;
446    } else {
447        // Couldn't find a non-space, ',' or '[' character
448        return Error::DOC_NOTJSON;
449    }
450    return Error::SUCCESS;
451}
452
453Error
454Operation::do_list_append()
455{
456
457    if (m_path->size() == 1 && m_optype.base() == Command::ARRAY_APPEND) {
458        // i.e. only the root element
459        return do_empty_append();
460    }
461
462    Error rv;
463
464    // Find the match itself, no magic needed!
465    bool is_unique = m_optype.base() == Command::ARRAY_ADD_UNIQUE;
466    if (is_unique) {
467        m_match.ensure_unique = m_userval;
468    }
469
470    rv = do_match_common(Match::GET_MATCH_ONLY);
471    if (!rv.success()) {
472        // Parse error (ENOENT excluded)
473        return rv;
474    }
475
476    if (m_match.matchres != JSONSL_MATCH_COMPLETE) {
477        // Not complete. Determine if mkdir_p should be used
478        if (m_optype.is_mkdir_p()) {
479            return do_mkdir_p(MKDIR_P_ARRAY);
480        } else {
481            return Error::PATH_ENOENT;
482        }
483    }
484
485    // All other errors should be handled above
486    SUBDOC_ASSERT(m_match.matchres == JSONSL_MATCH_COMPLETE);
487
488    // Incorrect terminal type!
489    if (m_match.type != JSONSL_T_LIST) {
490        return Error::PATH_MISMATCH;
491    }
492
493    if (is_unique && m_match.unique_item_found) {
494        // Unique item already exists
495        return Error::DOC_EEXISTS;
496    }
497    const Loc& array_loc = m_match.loc_deepest;
498
499    if (m_match.num_children == 0) {
500        // Special case, no children; append and prepend are the same here!
501        return insert_singleton_element();
502    }
503
504    /* Last element */
505    newdoc_at(0).end_at_end(m_doc, array_loc, Loc::NO_OVERLAP);
506    /* Insert comma */
507    newdoc_at(1) = loc_COMMA;
508    /* User */
509    newdoc_at(2) = m_userval;
510    /* Parent end */
511    newdoc_at(3).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
512
513    m_result->m_newlen = 4;
514    return Error::SUCCESS;
515}
516
517Error
518Operation::do_insert()
519{
520    auto& lastcomp = m_path->get_component(m_path->size()-1);
521    if (lastcomp.ptype != JSONSL_PATH_NUMERIC) {
522        return Error::PATH_EINVAL;
523    }
524    if (lastcomp.is_neg) {
525        // Negative paths are invalid for insert operations
526        return Error::PATH_EINVAL;
527    }
528
529    Error status = do_match_common(Match::GET_MATCH_ONLY);
530    if (!status.success()) {
531        return status;
532    }
533
534    if (m_match.matchres == JSONSL_MATCH_COMPLETE) {
535        const Loc& match_loc = m_match.loc_deepest;
536
537        /*
538         * DOCNEW[0] = ... [
539         * DOCNEW[1] = USER
540         * DOCNEW[2] = ,
541         * DOCNEW[3] = MATCH
542         */
543        m_result->m_newlen = 4;
544        newdoc_at(0).end_at_begin(m_doc, match_loc, Loc::NO_OVERLAP);
545        newdoc_at(1) = m_userval;
546        newdoc_at(2) = loc_COMMA;
547        newdoc_at(3).begin_at_begin(m_doc, match_loc);
548        return Error::SUCCESS;
549
550    } else if (m_match.immediate_parent_found) {
551        const Loc& array_loc = m_match.loc_deepest;
552        if (m_match.num_siblings == 0 && lastcomp.idx == 0) {
553            // Equivalent to prepend/push_first
554            /*
555             * DOCNEW[0] = ... [
556             * DOCNEW[1] = USER
557             * DOCNEW[2] = ] ...
558             */
559            m_result->m_newlen = 3;
560            newdoc_at(0).end_at_begin(m_doc, array_loc, Loc::OVERLAP);
561            newdoc_at(1) = m_userval;
562            newdoc_at(2).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
563            return Error::SUCCESS;
564
565        } else if (lastcomp.idx == m_match.num_siblings) {
566            // Equivalent to append/push_last
567            /*
568             * (assume DOC = [a,b,c,d,e]
569             * DOCNEW[0] = e (last char before ']')
570             * DOCNEW[1] = , (since there are items in the list)
571             * DOCNEW[2] = USER
572             * DOCNEW[3] = ]
573             */
574            m_result->m_newlen = 4;
575            newdoc_at(0).end_at_end(m_doc, array_loc, Loc::NO_OVERLAP);
576            newdoc_at(1) = loc_COMMA;
577            newdoc_at(2) = m_userval;
578            newdoc_at(3).begin_at_end(m_doc, array_loc, Loc::OVERLAP);
579            return Error::SUCCESS;
580
581        } else {
582            return Error::PATH_ENOENT;
583        }
584    } else {
585        return Error::PATH_ENOENT;
586    }
587}
588
589static Error
590parse_int64(const Loc& orig, int64_t& outval)
591{
592    const char *cur = orig.at;
593    size_t n = orig.length;
594
595    if (!n) {
596        return Error::VALUE_EMPTY; // Empty
597    }
598    if (*cur == '-') {
599        cur++;
600        if (!--n) {
601            return Error::DELTA_EINVAL;
602        }
603    }
604    if (*cur == '0') {
605        // Can't start with a zero. It's either a leading zero or an empty
606        // delta
607        return Error::DELTA_EINVAL;
608    }
609
610    outval = 0;
611    for (size_t ii = 0; ii < n; ii++) {
612        if (!isdigit(cur[ii])) {
613            return Error::DELTA_EINVAL; // Not a number!
614        }
615        // Get the numeric value of the digit, with '0' being the lowest
616        // value digit character in the ascii table, and with digits appearing
617        // in order, such that '9' (0x39) - '0' (0x30) == 0
618
619        uint64_t newval = (outval * 10) + (cur[ii] - '0');
620        if (newval < static_cast<uint64_t>(outval) ||
621                newval > std::numeric_limits<int64_t>::max()) {
622            // mismatch
623            return Error::DELTA_EINVAL;
624        }
625        outval = static_cast<int64_t>(newval);
626    }
627    if (*orig.at == '-') {
628        outval *= -1;
629    }
630    return Error::SUCCESS;
631}
632
633Error
634Operation::do_arith_op()
635{
636    Error status;
637    int64_t delta;
638    int64_t numres = 0;
639    // Verify the digit first
640
641    status = parse_int64(m_userval, delta);
642    if (!status.success()) {
643        return status;
644    }
645
646    /* Find the number first */
647    status = do_match_common(m_optype.is_mkdir_p() ?
648            Match::GET_FOLLOWING_SIBLINGS : Match::GET_MATCH_ONLY);
649
650    if (status != Error::SUCCESS) {
651        return status;
652    }
653
654    if (m_match.matchres == JSONSL_MATCH_COMPLETE) {
655        const Loc& num_loc = m_match.loc_deepest;
656        if (m_match.type != JSONSL_T_SPECIAL) {
657            return Error::PATH_MISMATCH;
658        } else if (m_match.sflags & ~(JSONSL_SPECIALf_NUMERIC)) {
659            return Error::PATH_MISMATCH;
660        } else  {
661            errno = 0;
662            numres = strtoll(num_loc.at, NULL, 10);
663
664            if (errno == ERANGE) {
665                return Error::NUM_E2BIG;
666            }
667
668            /* Calculate what to place inside the buffer. We want to be gentle here
669             * and not force 64 bit C arithmetic to confuse users, so use proper
670             * integer overflow/underflow with a 64 (or rather, 63) bit limit. */
671            if (delta >= 0 && numres >= 0) {
672                if (std::numeric_limits<int64_t>::max() - delta < numres) {
673                    return Error::DELTA_OVERFLOW;
674                }
675            } else if (delta < 0 && numres < 0) {
676                if (delta < std::numeric_limits<int64_t>::min() - numres) {
677                    return Error::DELTA_OVERFLOW;
678                }
679            }
680
681            numres += delta;
682            m_result->m_numbuf = std::to_string(numres);
683        }
684    } else {
685        if (!m_optype.is_mkdir_p() && !m_match.immediate_parent_found) {
686            return Error::PATH_ENOENT;
687        }
688
689        if (m_match.type != JSONSL_T_OBJECT) {
690            return Error::PATH_ENOENT;
691        }
692
693        m_result->m_numbuf = std::to_string(delta);
694        m_userval.at = m_result->m_numbuf.data();
695        m_userval.length = m_result->m_numbuf.size();
696        m_optype = Command::DICT_ADD_P;
697        if ((status = do_store_dict()) != Error::SUCCESS) {
698            return status;
699        }
700        m_result->m_match = m_match.loc_deepest = m_userval;
701        return Error::SUCCESS;
702    }
703
704    // Unlike other ops, we modify the match value itself, as this is an
705    // in/out parameter
706
707    /* Preamble */
708    newdoc_at(0).end_at_begin(m_doc, m_match.loc_deepest, Loc::NO_OVERLAP);
709
710    /* New number */
711    newdoc_at(1).at = m_result->m_numbuf.data();
712    newdoc_at(1).length = m_result->m_numbuf.size();
713
714    /* Postamble */
715    newdoc_at(2).begin_at_end(m_doc, m_match.loc_deepest, Loc::NO_OVERLAP);
716    m_result->m_newlen = 3;
717
718    m_match.loc_deepest.at = m_result->m_numbuf.data();
719    m_match.loc_deepest.length = m_result->m_numbuf.size();
720    m_result->m_match = m_match.loc_deepest;
721    return Error::SUCCESS;
722}
723
724Error
725Operation::validate(int mode, int depth)
726{
727    if (!m_userval.empty()) {
728        int rv = Validator::validate(m_userval, m_jsn, depth, mode);
729        switch (rv) {
730        case JSONSL_ERROR_SUCCESS:
731            return Error::SUCCESS;
732        case JSONSL_ERROR_LEVELS_EXCEEDED:
733            return Error::VALUE_ETOODEEP;
734        default:
735            return Error::VALUE_CANTINSERT;
736        }
737    } else {
738        return Error::VALUE_EMPTY;
739    }
740}
741
742int
743Operation::get_maxdepth(DepthMode mode) const
744{
745    if (mode == DepthMode::PATH_HAS_NEWKEY) {
746        return (Limits::MAX_COMPONENTS + 1) - m_path->size();
747    } else {
748        return Limits::MAX_COMPONENTS - m_path->size();
749    }
750}
751
752Error
753Operation::op_exec(const char *pth, size_t npth)
754{
755    int rv = m_path->parse(pth, npth);
756    Error status;
757
758    if (rv != 0) {
759        if (rv == JSONSL_ERROR_LEVELS_EXCEEDED) {
760            return Error::PATH_E2BIG;
761        } else {
762            return Error::PATH_EINVAL;
763        }
764    }
765
766    switch (m_optype) {
767    case Command::GET:
768    case Command::EXISTS:
769        status = do_match_common(Match::GET_MATCH_ONLY);
770        if (status != Error::SUCCESS) {
771            return status;
772        }
773        return do_get();
774
775    case Command::DICT_ADD:
776    case Command::DICT_ADD_P:
777    case Command::DICT_UPSERT:
778    case Command::DICT_UPSERT_P:
779    case Command::REPLACE:
780        if (m_path->size() == 1) {
781            /* Can't perform these operations on the root element since they
782             * will invalidate the JSON or are otherwise meaningless. */
783            return Error::VALUE_CANTINSERT;
784        }
785
786        status = validate(Validator::PARENT_DICT, get_maxdepth(PATH_HAS_NEWKEY));
787        if (!status.success()) {
788            return status;
789        }
790        status = do_match_common(Match::GET_MATCH_ONLY);
791        if (!status.success()) {
792            return status;
793        }
794        return do_store_dict();
795
796    case Command::REMOVE:
797        if (m_path->size() == 1) {
798            // Can't remove root element!
799            return Error::VALUE_CANTINSERT;
800        }
801        return do_remove();
802
803    case Command::ARRAY_PREPEND:
804    case Command::ARRAY_PREPEND_P:
805        status = validate(Validator::PARENT_ARRAY, get_maxdepth(PATH_IS_PARENT));
806        if (!status.success()) {
807            return status;
808        }
809        return do_list_prepend();
810
811    case Command::ARRAY_APPEND:
812    case Command::ARRAY_APPEND_P:
813    case Command::ARRAY_ADD_UNIQUE:
814    case Command::ARRAY_ADD_UNIQUE_P: {
815        int validmode = Validator::PARENT_ARRAY;
816        if (m_optype.base() == Command::ARRAY_ADD_UNIQUE) {
817            // Uniqueness must contain a single, primitive value.
818            validmode |= Validator::VALUE_PRIMITIVE | Validator::VALUE_SINGLE;
819        }
820
821        status = validate(validmode, get_maxdepth(PATH_IS_PARENT));
822        if (!status.success()) {
823            return status;
824        }
825        return do_list_append();
826    }
827
828    case Command::ARRAY_INSERT:
829        status = validate(Validator::PARENT_ARRAY, get_maxdepth(PATH_HAS_NEWKEY));
830        if (!status.success()) {
831            return status;
832        }
833        return do_insert();
834
835    case Command::COUNTER:
836    case Command::COUNTER_P:
837        // no need to check for depth here, since if the path itself is too
838        // big, it will fail during path parse-time
839        return do_arith_op();
840
841    default:
842        return Error::GLOBAL_ENOSUPPORT;
843
844    }
845}
846
847Operation::Operation()
848: m_path(new Path()),
849  m_jsn(Match::jsn_alloc()),
850  m_optype(Command::GET),
851  m_result(NULL)
852{
853}
854
855Operation::~Operation()
856{
857    clear();
858    delete m_path;
859    Match::jsn_free(m_jsn);
860}
861
862void
863Operation::clear()
864{
865    m_path->clear();
866    m_match.clear();
867    m_userval.length = 0;
868    m_userval.at = NULL;
869    m_result = NULL;
870    m_optype = Command::GET;
871}
872
873Operation *
874subdoc_op_alloc()
875{
876    return new Operation();
877}
878
879void
880subdoc_op_free(Operation *op)
881{
882    delete op;
883}
884
885/* Misc */
886const char *
887Error::description() const
888{
889    switch (m_code) {
890    case Error::SUCCESS:
891        return "Success";
892    case Error::PATH_ENOENT:
893        return "Requested path does not exist in document";
894    case Error::PATH_MISMATCH:
895        return "The path specified treats an existing document entry as the wrong type";
896    case Error::PATH_EINVAL:
897        return "Path syntax error";
898    case Error::DOC_NOTJSON:
899        return "The document is not JSON";
900    case Error::DOC_EEXISTS:
901        return "The requested path already exists";
902    case Error::PATH_E2BIG:
903        return "The path is too big";
904    case Error::NUM_E2BIG:
905        return "The number specified by the path is too big";
906    case Error::DELTA_EINVAL:
907        return "Invalid delta: Either not a number, 0, or not within int64 range";
908    case Error::DELTA_OVERFLOW:
909        return "Performing the counter operation would result in an underflow or overflow";
910    case Error::VALUE_CANTINSERT:
911        return "The new value cannot be inserted in the context of the path, as it would invalidate the JSON";
912    case Error::VALUE_EMPTY:
913        return "Expected non-empty value for command";
914    case Error::VALUE_ETOODEEP:
915        return "Adding this value would make the document too deep";
916    case Error::GLOBAL_ENOSUPPORT:
917        return "Operation not implemented";
918    case Error::DOC_ETOODEEP:
919        return "Document is too deep to parse";
920    }
921
922    return "Unknown error code";
923}
924