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 #ifndef LCB_SUBDOCAPI_H 19 #define LCB_SUBDOCAPI_H 20 #include <cstdint> 21 namespace Subdoc { 22 23 /** 24 * The following error codes are returned when a certain sub-document 25 * manipulation function could not be executed. Traditional memcached 26 * errors may also be returned. 27 */ 28 class Error { 29 public: 30 enum Code { 31 SUCCESS = 0x00, /* PROTOCOL_BINARY_RESPONSE_SUCCESS*/ 32 /** Document exists, but the given path was not found in the document */ 33 PATH_ENOENT = 0x501, 34 /** There was a conflict between the data and the path */ 35 PATH_MISMATCH = 0x502, 36 /** The path is not a valid path (i.e. does not parse correctly) */ 37 PATH_EINVAL = 0x503, 38 /** The document reference exists, but is not JSON */ 39 DOC_NOTJSON = 0x504, 40 /**The requested operation required the value did not already exist, but it exists */ 41 DOC_EEXISTS = 0x505, 42 /**The path requested is too long/deep to traverse */ 43 PATH_E2BIG = 0x506, 44 /**The number to increment was too big (could not fit in an int64_t) */ 45 NUM_E2BIG = 0x507, 46 /**Delta is too big (bigger than INT64_MAX) */ 47 DELTA_E2BIG = 0x508, 48 /**Invalid value for insertion. Inserting this value would invalidate 49 * the JSON document */ 50 VALUE_CANTINSERT = 0x509, 51 /** Document too deep to parse */ 52 DOC_ETOODEEP = 0x50A, 53 54 /* MEMCACHED ERROR CODES */ 55 GLOBAL_UNKNOWN_COMMAND = 0x81, 56 GLOBAL_ENOMEM = 0x82, 57 GLOBAL_ENOSUPPORT = 0x83, 58 GLOBAL_EINVAL = 0x04, 59 }; 60 61 Code m_code; 62 Error(Code c = SUCCESS) : m_code(c) {} 63 operator int() const { return static_cast<int>(m_code); } 64 bool success() const { return m_code == SUCCESS; } 65 const char *description() const; 66 }; 67 68 // Provide definitions for older code (temporary) 69 #define SUBDOC_XERR(X) \ 70 X(SUCCESS) \ 71 X(PATH_ENOENT) \ 72 X(PATH_MISMATCH) \ 73 X(PATH_EINVAL) \ 74 X(DOC_NOTJSON) \ 75 X(DOC_EEXISTS) \ 76 X(PATH_E2BIG) \ 77 X(NUM_E2BIG) \ 78 X(DELTA_E2BIG) \ 79 X(VALUE_CANTINSERT) \ 80 X(DOC_ETOODEEP) \ 81 X(GLOBAL_UNKNOWN_COMMAND) \ 82 X(GLOBAL_EINVAL) 83 84 /**@name Paths 85 * A Sub-Document _PATH_ is a path to the container of the item you want to 86 * access. Every JSON primitive is stored either as an array element or a 87 * dictionary value. In the case of an array element, the _path_ is the path 88 * to the numeric index of the array; in the case of a dictionary value, the 89 * _path_ is the path to the string key for the value to be accessed (or 90 * modified). 91 * 92 * Path components are separated by a period (`.`). To escape literal `.` 93 * characters, encapsulate the given component in backticks. 94 * 95 * Any other tokens in the path must match _exactly_ the way they might be 96 * represented in the document and must be valid JSON. For example, if an 97 * element in the path contains a literal quote, the quote must be escaped 98 * like so: 99 * 100 * @code 101 * foo.qu\"oted.path 102 * @endcode 103 */ 104 105 106 107 /** 108 * @name Commands 109 * 110 * Each of these commands operates on a subdoc_PATH. The actual semantics 111 * of the path depends on the operation. However, in general: 112 * 113 * _Dictionary Paths_ are expected to point to a specific dictionary _key_. For 114 * update operations, the existing value is replaced. For removal operations 115 * the key and value are removed. For 116 * 117 * 118 * @{ 119 */ 120 class Command { 121 public: 122 enum Code { 123 /* These operations are common because they operate on the _value_ only: */ 124 /** Get the value located in the path */ 125 GET = 0x00, 126 127 /** Simply check the path exists */ 128 EXISTS = 0x01, 129 130 /** Replace the value, if the path exists */ 131 REPLACE = 0x02, 132 133 /** Remove the value, if the path exists */ 134 REMOVE = 0x03, 135 136 /* Dictionary operations. Only valid if PATH points to a dictionary. 137 * The _P variants are similar to `mkdir -p` and will create intermediate 138 * path entries if they do not exist. For example, consider an empty 139 * document, an `ADD` command for `foo.bar.baz` will fail (since `foo.bar` 140 * does not exist), however an `ADD_P` command will succeed, creating 141 * `foo` and `bar` as dictionaries, resulting in a document that looks 142 * like this: 143 * {"foo":{"bar":{"baz":VALUE}}} 144 */ 145 146 /** Add or replace a value for the given path */ 147 DICT_UPSERT = 0x04, 148 DICT_UPSERT_P = 0x84, 149 150 /** Add a value for the given path. Fail if the value already exists */ 151 DICT_ADD = 0x05, 152 DICT_ADD_P = 0x85, 153 154 /* Array operations. Only valid if PATH points to an array */ 155 156 /* Note, there is no insert/upsert for an array, since this would require 157 * padding surrounding elements with something else, which is probably not 158 * what a user wants */ 159 160 /* The _P variants will create intermediate path elements, if they do 161 * not exist */ 162 ARRAY_PREPEND = 0x06, 163 ARRAY_PREPEND_P = 0x86, 164 165 ARRAY_APPEND = 0x07, 166 ARRAY_APPEND_P = 0x87, 167 168 /**Adds a value to a list, ensuring that the value does not already exist. 169 * Values added can only be primitives, and the list itself must already 170 * only contain primitives. If any of these is violated, the error 171 * SUBDOC_PATH_MISMATCH is returned. */ 172 ARRAY_ADD_UNIQUE = 0x08, 173 ARRAY_ADD_UNIQUE_P = 0x88, 174 175 176 /* In the protocol this should contain a 64-bit integer 177 * 178 * If the number itself does not fit into a uint64_t (if unsigned) or an 179 * int64_t (if signed), a SUBDOC_NUM_E2BIG error is returned. 180 * 181 * If the resulting item does exist, but is not a signed or unsigned integer, 182 * then a SUBDOC_PATH_MISMATCH error is returned. This is the case for 183 * 'floats' and 'exponents' as well. Only whole integers are supported. 184 */ 185 INCREMENT = 0x09, 186 INCREMENT_P = 0x89, 187 DECREMENT = 0x0A, 188 DECREMENT_P = 0x8A, 189 190 INVALID = 0xff, 191 FLAG_MKDIR_P = 0x80 192 }; 193 194 uint8_t code; 195 Command(uint8_t code = GET) : code(code) {} 196 operator uint8_t() const { return code; } 197 198 bool is_mkdir_p() const { return (code & FLAG_MKDIR_P) != 0; } 199 }; 200 201 #define SUBDOC_XOPCODES(X) \ 202 X(GET) \ 203 X(EXISTS) \ 204 X(REPLACE) \ 205 X(REMOVE) \ 206 X(DICT_UPSERT) \ 207 X(DICT_UPSERT_P) \ 208 X(DICT_ADD) \ 209 X(DICT_ADD_P) \ 210 X(ARRAY_PREPEND) \ 211 X(ARRAY_PREPEND_P) \ 212 X(ARRAY_APPEND) \ 213 X(ARRAY_APPEND_P) \ 214 X(ARRAY_ADD_UNIQUE) \ 215 X(ARRAY_ADD_UNIQUE_P) \ 216 X(INCREMENT) \ 217 X(INCREMENT_P) \ 218 X(DECREMENT) \ 219 X(DECREMENT_P) 220 /**@}*/ 221 } 222 223 #define X(b) static const Subdoc::Error::Code SUBDOC_STATUS_##b = Subdoc::Error::b; 224 SUBDOC_XERR(X) 225 #undef X 226 227 static const int SUBDOC_CMD_FLAG_MKDIR_P = 0x80; 228 #define X(b) static const Subdoc::Command::Code SUBDOC_CMD_##b = Subdoc::Command::b; 229 SUBDOC_XOPCODES(X) 230 #undef X 231 static const Subdoc::Command::Code SUBDOC_CMD_DELETE = SUBDOC_CMD_REMOVE; 232 233 #endif 234