mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 18:36:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			288 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef JSON_TABLE_INCLUDED
 | |
| #define JSON_TABLE_INCLUDED
 | |
| 
 | |
| /* Copyright (c) 2020, MariaDB Corporation. All rights reserved.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1335  USA */
 | |
| 
 | |
| 
 | |
| #include <json_lib.h>
 | |
| 
 | |
| class Json_table_column;
 | |
| 
 | |
| /*
 | |
|   The Json_table_nested_path represents the 'current nesting' level
 | |
|   for a set of JSON_TABLE columns.
 | |
|   Each column (Json_table_column instance) is linked with corresponding
 | |
|   'nested path' object and gets its piece of JSON to parse during the computation
 | |
|   phase.
 | |
|   The root 'nested_path' is always present as a part of Table_function_json_table,
 | |
|   then other 'nested_paths' can be created and linked into a tree structure when new
 | |
|   'NESTED PATH' is met. The nested 'nested_paths' are linked with 'm_nested', the same-level
 | |
|   'nested_paths' are linked with 'm_next_nested'.
 | |
|   So for instance
 | |
|     JSON_TABLE( '...', '$[*]'
 | |
|        COLUMNS( a INT PATH '$.a' ,
 | |
|           NESTED PATH '$.b[*]' COLUMNS (b INT PATH '$',
 | |
|                                         NESTED PATH '$.c[*]' COLUMNS(x INT PATH '$')),
 | |
|           NESTED PATH '$.n[*]' COLUMNS (z INT PATH '$'))
 | |
|   results in 4 'nested_path' created:
 | |
|                  root          nested_b       nested_c     nested_n
 | |
|   m_path           '$[*]'         '$.b[*]'        '$.c[*]'     '$.n[*]
 | |
|   m_nested          &nested_b     &nested_c       NULL         NULL
 | |
|   n_next_nested     NULL          &nested_n       NULL         NULL
 | |
| 
 | |
|   and 4 columns created:
 | |
|               a          b            x            z
 | |
|   m_nest    &root      &nested_b    &nested_c    &nested_n
 | |
| */
 | |
| 
 | |
| class Json_table_nested_path : public Sql_alloc
 | |
| {
 | |
| public:
 | |
|   json_path_t m_path;  /* The JSON Path to get the rows from */
 | |
|   bool m_null; // TRUE <=> producing a NULL-complemented row.
 | |
| 
 | |
|   /*** Construction interface ***/
 | |
|   Json_table_nested_path():
 | |
|     m_null(TRUE), m_nested(NULL), m_next_nested(NULL)
 | |
|   {}
 | |
| 
 | |
|   int set_path(THD *thd, const LEX_CSTRING &path);
 | |
| 
 | |
|   /*** Methods for performing a scan ***/
 | |
|   void scan_start(CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
 | |
|   int scan_next();
 | |
|   bool check_error(const char *str);
 | |
| 
 | |
|   /*** Members for getting the values we've scanned to ***/
 | |
|   const uchar *get_value() { return m_engine.value_begin; }
 | |
|   const uchar *get_value_end() { return m_engine.s.str_end; }
 | |
| 
 | |
|   /* Counts the rows produced. Used by FOR ORDINALITY columns */
 | |
|   longlong m_ordinality_counter;
 | |
| 
 | |
|   int print(THD *thd, Field ***f, String *str,
 | |
|             List_iterator_fast<Json_table_column> &it,
 | |
|             Json_table_column **last_column);
 | |
| private:
 | |
|   /* The head of the list of nested NESTED PATH statements. */
 | |
|   Json_table_nested_path *m_nested;
 | |
| 
 | |
|   /* in the above list items are linked with the */
 | |
|   Json_table_nested_path *m_next_nested;
 | |
| 
 | |
|   /*** Members describing NESTED PATH structure ***/
 | |
|   /* Parent nested path. The "root" path has this NULL */
 | |
|   Json_table_nested_path *m_parent;
 | |
| 
 | |
|   /*** Members describing current JSON Path scan state ***/
 | |
|   /* The JSON Parser and JSON Path evaluator */
 | |
|   json_engine_t m_engine;
 | |
| 
 | |
|   /* The path the parser is currently pointing to */
 | |
|   json_path_t m_cur_path;
 | |
| 
 | |
|   /* The child NESTED PATH we're currently scanning */
 | |
|   Json_table_nested_path *m_cur_nested;
 | |
| 
 | |
|   static bool column_in_this_or_nested(const Json_table_nested_path *p,
 | |
|                                        const Json_table_column *jc);
 | |
|   friend class Table_function_json_table;
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|   @brief
 | |
|     Describes the column definition in JSON_TABLE(...) syntax.
 | |
| 
 | |
|   @detail
 | |
|     Has methods for printing/handling errors but otherwise it's a static
 | |
|     object.
 | |
| */
 | |
| 
 | |
| class Json_table_column : public Sql_alloc
 | |
| {
 | |
| public:
 | |
|   enum enum_type
 | |
|   {
 | |
|     FOR_ORDINALITY,
 | |
|     PATH,
 | |
|     EXISTS_PATH
 | |
|   };
 | |
| 
 | |
|   enum enum_on_type
 | |
|   {
 | |
|     ON_EMPTY,
 | |
|     ON_ERROR
 | |
|   };
 | |
| 
 | |
|   enum enum_on_response
 | |
|   {
 | |
|     RESPONSE_NOT_SPECIFIED,
 | |
|     RESPONSE_ERROR,
 | |
|     RESPONSE_NULL,
 | |
|     RESPONSE_DEFAULT
 | |
|   };
 | |
| 
 | |
|   struct On_response
 | |
|   {
 | |
|   public:
 | |
|     Json_table_column::enum_on_response m_response;
 | |
|     Item *m_default;
 | |
|     int respond(Json_table_column *jc, Field *f, uint error_num);
 | |
|     int print(const char *name, String *str) const;
 | |
|     bool specified() const { return m_response != RESPONSE_NOT_SPECIFIED; }
 | |
|   };
 | |
| 
 | |
|   enum_type m_column_type;
 | |
|   bool m_format_json;
 | |
|   json_path_t m_path;
 | |
|   On_response m_on_error;
 | |
|   On_response m_on_empty;
 | |
|   Create_field *m_field;
 | |
|   Json_table_nested_path *m_nest;
 | |
|   CHARSET_INFO *m_explicit_cs;
 | |
| 
 | |
|   void set(enum_type ctype)
 | |
|   {
 | |
|     m_column_type= ctype;
 | |
|   }
 | |
|   int set(THD *thd, enum_type ctype, const LEX_CSTRING &path, CHARSET_INFO *cs);
 | |
|   Json_table_column(Create_field *f, Json_table_nested_path *nest) :
 | |
|     m_field(f), m_nest(nest), m_explicit_cs(NULL)
 | |
|   {
 | |
|     m_on_error.m_response= RESPONSE_NOT_SPECIFIED;
 | |
|     m_on_empty.m_response= RESPONSE_NOT_SPECIFIED;
 | |
|   }
 | |
|   int print(THD *tnd, Field **f, String *str);
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Class represents the table function, the function
 | |
|   that returns the table as a result so supposed to appear
 | |
|   in the FROM list of the SELECT statement.
 | |
|   At the moment there is only one such function JSON_TABLE,
 | |
|   so the class named after it, but should be refactored
 | |
|   into the hierarchy root if we create more of that functions.
 | |
| 
 | |
|   As the parser finds the table function in the list it
 | |
|   creates an instance of Table_function_json_table storing it
 | |
|   into the TABLE_LIST::table_function.
 | |
|   Then the ha_json_table instance is created based on it in
 | |
|   the create_table_for_function().
 | |
| 
 | |
|   == Replication: whether JSON_TABLE is deterministic ==
 | |
| 
 | |
|   In sql_yacc.yy, we set BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION whenever
 | |
|   JSON_TABLE is used. The reasoning behind this is as follows:
 | |
| 
 | |
|   In the current MariaDB code, evaluation of JSON_TABLE is deterministic,
 | |
|   that is, for a given input string JSON_TABLE will always produce the same
 | |
|   set of rows in the same order.  However one can think of JSON documents
 | |
|   that one can consider indentical which will produce different output.
 | |
|   In order to be feature-proof and withstand changes like:
 | |
|   - sorting JSON object members by name (like MySQL does)
 | |
|   - changing the way duplicate object members are handled
 | |
|   we mark the function as SBR-unsafe.
 | |
|   (If there is ever an issue with this, marking the function as SBR-safe
 | |
|    is a non-intrusive change we will always be able to make)
 | |
| */
 | |
| 
 | |
| class Table_function_json_table : public Sql_alloc
 | |
| {
 | |
| public:
 | |
|   /*** Basic properties of the original JSON_TABLE(...) ***/
 | |
|   Item *m_json; /* The JSON value to be parsed. */
 | |
| 
 | |
|   /* The COLUMNS(...) part representation. */
 | |
|   Json_table_nested_path m_nested_path;
 | |
| 
 | |
|   /* The list of table column definitions. */
 | |
|   List<Json_table_column> m_columns;
 | |
| 
 | |
|   /*** Name resolution functions ***/
 | |
|   bool setup(THD *thd, TABLE_LIST *sql_table, SELECT_LEX *s_lex);
 | |
| 
 | |
|   int walk_items(Item_processor processor, bool walk_subquery,
 | |
|                  void *argument);
 | |
| 
 | |
|   /*** Functions for interaction with the Query Optimizer ***/
 | |
|   void fix_after_pullout(TABLE_LIST *sql_table,
 | |
|                          st_select_lex *new_parent, bool merge);
 | |
|   void update_used_tables() { m_json->update_used_tables(); }
 | |
| 
 | |
|   table_map used_tables() const { return m_json->used_tables(); }
 | |
|   bool join_cache_allowed() const
 | |
|   {
 | |
|     /*
 | |
|       Can use join cache when we have an outside reference.
 | |
|       If there's dependency on any other table or randomness,
 | |
|       cannot use it.
 | |
|     */
 | |
|     return !(used_tables() & ~OUTER_REF_TABLE_BIT);
 | |
|   }
 | |
|   void get_estimates(ha_rows *out_rows,
 | |
|                      double *scan_time, double *startup_cost);
 | |
| 
 | |
|   int print(THD *thd, TABLE_LIST *sql_table,
 | |
|             String *str, enum_query_type query_type);
 | |
| 
 | |
|   /*** Construction interface to be used from the parser ***/
 | |
|   Table_function_json_table(Item *json):
 | |
|     m_json(json),
 | |
|     m_context_setup_done(false)
 | |
|   {
 | |
|     cur_parent= &m_nested_path;
 | |
|     last_sibling_hook= &m_nested_path.m_nested;
 | |
|   }
 | |
| 
 | |
|   void start_nested_path(Json_table_nested_path *np);
 | |
|   void end_nested_path();
 | |
|   Json_table_nested_path *get_cur_nested_path() { return cur_parent; }
 | |
|   void set_name_resolution_context(Name_resolution_context *arg)
 | |
|   {
 | |
|     m_context= arg;
 | |
|   }
 | |
| 
 | |
|   /* SQL Parser: current column in JSON_TABLE (...) syntax */
 | |
|   Json_table_column *m_cur_json_table_column;
 | |
| 
 | |
| private:
 | |
|   /* Context to be used for resolving the first argument. */
 | |
|   Name_resolution_context *m_context;
 | |
| 
 | |
|   bool m_context_setup_done;
 | |
| 
 | |
|   /* Current NESTED PATH level being parsed */
 | |
|   Json_table_nested_path *cur_parent;
 | |
| 
 | |
|   /*
 | |
|     Pointer to the list tail where we add the next NESTED PATH.
 | |
|     It points to the cur_parnt->m_nested for the first nested
 | |
|     and prev_nested->m_next_nested for the coesequent ones.
 | |
|   */
 | |
|   Json_table_nested_path **last_sibling_hook;
 | |
| };
 | |
| 
 | |
| bool push_table_function_arg_context(LEX *lex, MEM_ROOT *alloc);
 | |
| 
 | |
| TABLE *create_table_for_function(THD *thd, TABLE_LIST *sql_table);
 | |
| 
 | |
| table_map add_table_function_dependencies(List<TABLE_LIST> *join_list,
 | |
|                                           table_map nest_tables);
 | |
| 
 | |
| #endif /* JSON_TABLE_INCLUDED */
 | |
| 
 | 
