1 #ifndef SQL_JSON_PATH_INCLUDED 2 #define SQL_JSON_PATH_INCLUDED 3 4 /* Copyright (c) 2015, 2021, Oracle and/or its affiliates. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License, version 2.0, 8 as published by the Free Software Foundation. 9 10 This program is also distributed with certain software (including 11 but not limited to OpenSSL) that is licensed under separate terms, 12 as designated in a particular file or component or in included license 13 documentation. The authors of MySQL hereby grant you an additional 14 permission to link the program and your derivative works with the 15 separately licensed software that they have included with MySQL. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License, version 2.0, for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program; if not, write to the Free Software 24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ 25 26 /* 27 @file json_path.h 28 29 This file contains interface support for the JSON path abstraction. 30 The path abstraction is described by the functional spec 31 attached to WL#7909. 32 */ 33 34 #include "my_global.h" 35 #include "prealloced_array.h" // Prealloced_array 36 #include "sql_string.h" // String 37 38 #include <string> 39 40 class Json_string; 41 42 enum enum_json_path_leg_type 43 { 44 jpl_member, 45 jpl_array_cell, 46 jpl_member_wildcard, 47 jpl_array_cell_wildcard, 48 jpl_ellipsis 49 }; 50 51 /* 52 One path leg in a JSON path expression. 53 54 A path leg describes either a key/value pair in an object 55 or a 0-based index into an array. 56 */ 57 class Json_path_leg 58 { 59 private: 60 enum_json_path_leg_type m_leg_type; 61 62 size_t m_array_cell_index; 63 64 std::string m_member_name; 65 66 public: Json_path_leg(enum_json_path_leg_type leg_type)67 explicit Json_path_leg(enum_json_path_leg_type leg_type) 68 : m_leg_type(leg_type), m_array_cell_index(0), m_member_name() 69 {} Json_path_leg(size_t array_cell_index)70 explicit Json_path_leg(size_t array_cell_index) 71 : m_leg_type(jpl_array_cell), m_array_cell_index(array_cell_index), 72 m_member_name() 73 {} Json_path_leg(const std::string & member_name)74 Json_path_leg(const std::string &member_name) 75 : m_leg_type(jpl_member), m_array_cell_index(0), 76 m_member_name(member_name) 77 {} 78 79 /** Accessors */ 80 enum_json_path_leg_type get_type() const; 81 size_t get_member_name_length() const; 82 const char *get_member_name() const; 83 size_t get_array_cell_index() const; 84 85 /** Turn into a human-readable string. */ 86 bool to_string(String *buf) const; 87 }; 88 89 90 /** 91 A path expression which can be used to seek to 92 a position inside a JSON value. 93 */ 94 class Json_seekable_path 95 { 96 public: ~Json_seekable_path()97 virtual ~Json_seekable_path() {} 98 99 /** Return the number of legs in this searchable path */ 100 virtual size_t leg_count() const =0; 101 102 /** 103 Get the ith (numbered from 0) leg 104 105 @param[in] index 0-based index into the searchable path 106 107 @return NULL if the index is out of range. Otherwise, a pointer to the 108 corresponding Json_path_leg. 109 */ 110 virtual const Json_path_leg *get_leg_at(const size_t index) const =0; 111 112 /** 113 Return true if the path contains an ellipsis token 114 */ 115 virtual bool contains_ellipsis() const =0; 116 117 }; 118 119 /* 120 A JSON path expression. 121 122 From the user's point of view, 123 a path expression is a string literal with the following structure. 124 We parse this structure into a Json_path class: 125 126 <code><pre> 127 128 pathExpression ::= scope pathLeg (pathLeg)* 129 130 scope ::= [ columnReference ] dollarSign 131 132 columnReference ::= 133 [ 134 [ databaseIdentifier period ] 135 tableIdentifier period 136 ] 137 columnIdentifier 138 139 databaseIdentifier ::= sqlIdentifier 140 tableIdentifier ::= sqlIdentifier 141 columnIdentifier ::= sqlIdentifier 142 143 pathLeg ::= member | arrayLocation | doubleAsterisk 144 145 member ::= period (keyName | asterisk) 146 147 arrayLocation ::= 148 leftBracket 149 (non-negative-integer | asterisk) 150 rightBracket 151 152 keyName ::= ECMAScript-identifier | ECMAScript-string-literal 153 154 doubleAsterisk ::= ** 155 156 </pre></code> 157 */ 158 class Json_path : public Json_seekable_path 159 { 160 private: 161 typedef Prealloced_array<Json_path_leg, 8, false> Path_leg_vector; 162 Path_leg_vector m_path_legs; 163 164 /** 165 Reinitialize this to be an empty path expression. 166 */ 167 void initialize(); 168 169 /** 170 Fills in this Json_path from a path expression. 171 172 The caller must specify 173 whether the path expression begins with a column identifier or whether 174 it just begins with $. Stops parsing on the first error. Returns true if 175 the path is parsed successfully. If parsing fails, then the state of this 176 Json_path is undefined. 177 178 @param[in] begins_with_column_id True if the path begins with a column id. 179 180 @param[in] path_length The length of the path expression. 181 182 @param[in] path_expression The string form of the path expression. 183 184 @param[out] status True if the path parsed successfully. False otherwise. 185 186 @return The pointer advanced to around where the error, if any, occurred. 187 */ 188 const char *parse_path(const bool begins_with_column_id, 189 const size_t path_length, 190 const char *path_expression, 191 bool *status); 192 193 /** 194 Parse a single path leg and add it to the evolving Json_path. 195 196 @param[in] charptr The current pointer into the path expression. 197 198 @param[in] endptr The end of the path expression. 199 200 @param[out] status The status variable to be filled in. 201 202 @return The pointer advanced past the consumed leg. 203 */ 204 const char *parse_path_leg(const char *charptr, const char *endptr, 205 bool *status); 206 207 /** 208 Parse a single ellipsis leg and add it to the evolving Json_path. 209 210 @param[in] charptr The current pointer into the path expression. 211 212 @param[in] endptr The end of the path expression. 213 214 @param[out] status The status variable to be filled in. 215 216 @return The pointer advanced past the consumed leg. 217 */ 218 const char *parse_ellipsis_leg(const char *charptr, const char *endptr, 219 bool *status); 220 221 /** 222 Parse a single array leg and add it to the evolving Json_path. 223 224 @param[in] charptr The current pointer into the path expression. 225 226 @param[in] endptr The end of the path expression. 227 228 @param[out] status The status variable to be filled in. 229 230 @return The pointer advanced past the consumed leg. 231 */ 232 const char *parse_array_leg(const char *charptr, const char *endptr, 233 bool *status); 234 235 /** 236 Parse a single member leg and add it to the evolving Json_path. 237 238 @param[in] charptr The current pointer into the path expression. 239 240 @param[in] endptr The end of the path expression. 241 242 @param[out] status The status variable to be filled in. 243 244 @return The pointer advanced past the consumed leg. 245 */ 246 const char *parse_member_leg(const char *charptr, const char *endptr, 247 bool *status); 248 249 public: 250 Json_path(); 251 ~Json_path(); 252 253 /** Return the number of legs in this path */ 254 size_t leg_count() const; 255 256 /** 257 Get the ith (numbered from 0) leg 258 259 @param[in] index 0-based index into the path 260 261 @return NULL if the index is out of range. Otherwise, a pointer to the 262 corresponding Json_path. 263 */ 264 const Json_path_leg *get_leg_at(const size_t index) const; 265 266 /** 267 Add a path leg to the end of this path. 268 @param[in] leg the leg to add 269 @return false on success, true on error 270 */ 271 bool append(const Json_path_leg &leg); 272 273 /** 274 Pop the last leg element. 275 276 This effectively lets the path point at the container of the original, 277 i.e. an array or an object. 278 279 @result the last leg popped off 280 */ 281 Json_path_leg pop(); 282 283 /** 284 Resets this to an empty path with no legs. 285 */ 286 void clear(); 287 288 /** 289 Return true if the path contains a wildcard 290 or ellipsis token 291 */ 292 bool contains_wildcard_or_ellipsis() const; 293 294 /** 295 Return true if the path contains an ellipsis token 296 */ 297 bool contains_ellipsis() const; 298 299 /** Turn into a human-readable string. */ 300 bool to_string(String *buf) const; 301 302 friend bool parse_path(const bool begins_with_column_id, 303 const size_t path_length, 304 const char *path_expression, 305 Json_path *path, 306 size_t *bad_index); 307 }; 308 309 310 /** 311 A lightweight path expression. This exists so that paths can be cloned 312 from the path legs of other paths without allocating heap memory 313 to copy those legs into. 314 */ 315 class Json_path_clone : public Json_seekable_path 316 { 317 private: 318 typedef Prealloced_array<const Json_path_leg *, 8, false> Path_leg_pointers; 319 Path_leg_pointers m_path_legs; 320 321 public: 322 Json_path_clone(); 323 ~Json_path_clone(); 324 325 /** Return the number of legs in this cloned path */ 326 size_t leg_count() const; 327 328 /** 329 Get the ith (numbered from 0) leg 330 331 @param[in] index 0-based index into the cloned path 332 333 @return NULL if the index is out of range. Otherwise, a pointer to the 334 corresponding Json_path_leg. 335 */ 336 const Json_path_leg *get_leg_at(const size_t index) const; 337 338 /** 339 Add a path leg to the end of this cloned path. 340 @param[in] leg the leg to add 341 @return false on success, true on error 342 */ 343 bool append(const Json_path_leg *leg); 344 345 /** 346 Clear this clone and then add all of the 347 legs from another path. 348 349 @param[in,out] other The source path 350 @return false on success, true on error 351 */ 352 bool set(Json_seekable_path *source); 353 354 /** 355 Pop the last leg element. 356 357 @result the last leg popped off 358 */ 359 const Json_path_leg * pop(); 360 361 /** 362 Resets this to an empty path with no legs. 363 */ 364 void clear(); 365 366 /** 367 Return true if the path contains an ellipsis token 368 */ 369 bool contains_ellipsis() const; 370 371 }; 372 373 374 /** 375 Initialize a Json_path from a path expression. 376 377 The caller must specify whether the path expression begins with a 378 column identifier or whether it begins with $. Stops parsing on the 379 first error. It initializes the Json_path and returns false if the 380 path is parsed successfully. Otherwise, it returns false. In that 381 case, the output bad_index argument will contain an index into the 382 path expression. The parsing failed near that index. 383 384 @param[in] begins_with_column_id True if the path begins with a column id. 385 @param[in] path_length The length of the path expression. 386 @param[in] path_expression The string form of the path expression. 387 @param[out] path The Json_path object to be initialized. 388 @param[out] bad_index If null is returned, the parsing failed around here. 389 @return false on success, true on error 390 */ 391 bool parse_path(const bool begins_with_column_id, const size_t path_length, 392 const char *path_expression, Json_path *path, 393 size_t *bad_index); 394 395 #endif /* SQL_JSON_PATH_INCLUDED */ 396