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