1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

Code cleanup in JSON array-of-object reading, add unit tests.

This commit is contained in:
Sergei Petrunia
2026-06-17 15:39:22 +03:00
parent 20c38aea48
commit a138093efd
3 changed files with 449 additions and 226 deletions

View File

@@ -123,6 +123,11 @@ public:
ha_rows max_index_blocks;
ha_rows max_row_blocks;
ulong call_number;
static
Multi_range_read_const_call_record *parse(MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf);
};
/*
@@ -136,6 +141,10 @@ public:
bool eq_ref;
bool is_covering;
ALL_READ_COST cost;
static
cost_index_read_call_record* parse(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf);
};
/*
@@ -148,6 +157,11 @@ public:
char *min_key;
char *max_key;
ha_rows records;
static
records_in_range_call_record* parse(MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf);
};
/*
@@ -187,24 +201,8 @@ ST_FIELD_INFO optimizer_context_capture_info[]= {
static void append_table_or_view_name(const TABLE_LIST *tbl, String *buf);
static void append_base_table_name(const TABLE *table, String *buf);
static int parse_check_obj_start_in_array(json_engine_t *je, String *err_buf,
const char *err_msg);
static int parse_table_context(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf,
table_context_for_replay *table_ctx);
static int parse_index_context(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf,
index_context_for_replay *index_ctx);
static int parse_range_context(MEM_ROOT *mem_root, json_engine_t *je, String *err_buf,
Multi_range_read_const_call_record *range_ctx);
static int parse_index_read_cost_context(MEM_ROOT*, json_engine_t *je,
String *err_buf,
cost_index_read_call_record *out);
static bool parse_range_cost_estimate(MEM_ROOT*, json_engine_t *je,
String *err_buf, Cost_estimate *cost);
static int parse_records_in_range_context(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf,
records_in_range_call_record *rir_ctx);
static char *strdup_root(MEM_ROOT *root, const String *str)
{
@@ -1188,6 +1186,9 @@ public:
index_context_for_replay() : rec_per_key(current_thd->mem_root) {}
char *idx_name= NULL;
Mem_root_dynamic_array<double> rec_per_key;
static index_context_for_replay *parse(MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf);
};
/*
@@ -1213,6 +1214,10 @@ public:
List<Multi_range_read_const_call_record> ranges_list;
List<cost_index_read_call_record> irc_list;
List<records_in_range_call_record> rir_list;
static
table_context_for_replay *parse(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf);
};
/*
@@ -1266,7 +1271,10 @@ public:
}
};
// psergey: reads an array of doubles..
/*
Read JSON array of double values.
We support the values being strings (in quotes) that store doubles.
*/
class Read_rec_per_key : public Read_array
{
MEM_ROOT *mem_root;
@@ -1295,99 +1303,6 @@ public:
}
};
/*
Read an array of JSON objects representing object T.
Create instances of T and collect them in a List<T>
*/
template <typename T> class Read_array_into_list : public Read_array
{
MEM_ROOT *mem_root;
List<T> *list_ctx;
int (*parse_context_fn)(MEM_ROOT *, json_engine_t *, String *, T *);
public:
Read_array_into_list(MEM_ROOT *mem_root_arg, List<T> *list_ctx_arg,
int (*parse_context_fn_arg)(MEM_ROOT *, json_engine_t *,
String *, T *))
: mem_root(mem_root_arg), list_ctx(list_ctx_arg),
parse_context_fn(parse_context_fn_arg)
{
}
int read_container(json_engine_t *je, const char *name, String *err_buf)
override
{
int rc;
while (1)
{
T *ctx= new T();
if (unlikely(!ctx))
return 1; // OOM
if ((rc= parse_context_fn(mem_root, je, err_buf, ctx)))
break; // Parse error
if (list_ctx->push_back(ctx, mem_root))
return 1; // OOM
}
return rc;
}
};
/*
check if the next element being parsed is an object within an array.
fill the err_buf with err_msg if the parsing check fails.
@return
0 OK
1 Parse Error
-1 EOF
*/
static int parse_check_obj_start_in_array(json_engine_t *je, String *err_buf,
const char *err_msg)
{
if (json_scan_next(je))
return 1;
if (je->state != JST_VALUE)
{
if (je->state == JST_ARRAY_END)
return -1; // EOF
else
return 1; // An error
}
if (json_scan_next(je) || je->state != JST_OBJ_START)
{
err_buf->append(err_msg, strlen(err_msg));
return 1;
}
return 0;
}
/*
parse a single context object from a json array of contexts.
The conext object should contain the elements that are
defined in the argument array
@return
0 OK
1 Parse Error
-1 EOF
*/
static int parse_context_obj_from_json_array(json_engine_t *je,
String *err_buf,
const char *err_msg,
Read_named_member *array)
{
if (int rc= parse_check_obj_start_in_array(je, err_buf, err_msg))
return rc;
return json_read_object(je, array, err_buf);
}
/*
Parses the table context of the JSON structure
@@ -1402,47 +1317,52 @@ static int parse_context_obj_from_json_array(json_engine_t *je,
1 Parse Error
-1 EOF
*/
static int parse_table_context(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf,
table_context_for_replay *table_ctx)
table_context_for_replay*
table_context_for_replay::parse(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf)
{
const char *err_msg= "Expected an object in the \"tables\" array";
auto *table_ctx= new (mem_root) table_context_for_replay;
Read_named_member array[]= {
{"name", Read_string(mem_root, &table_ctx->name), false},
{"file_stat_records",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&table_ctx->file_stat_records),
false},
{"data_file_length",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&table_ctx->data_file_length),
false},
{"index_file_length",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&table_ctx->index_file_length),
false},
{"mean_rec_length",
Read_non_neg_integer<ulong, ULONG_MAX>(&table_ctx->mean_rec_length),
false},
{"read_cost_io", Read_double(&table_ctx->read_cost_io), false},
{"read_cost_cpu", Read_double(&table_ctx->read_cost_cpu), false},
{"indexes",
Read_array_into_list<index_context_for_replay>(
mem_root, &table_ctx->index_list, parse_index_context),
true},
{"multi_range_read_info_const_calls",
Read_array_into_list<Multi_range_read_const_call_record>(
mem_root, &table_ctx->ranges_list, parse_range_context),
true},
{"cost_for_index_read_calls",
Read_array_into_list<cost_index_read_call_record>(
mem_root, &table_ctx->irc_list, parse_index_read_cost_context),
true},
{"records_in_range_calls",
Read_array_into_list<records_in_range_call_record>(
mem_root, &table_ctx->rir_list, parse_records_in_range_context),
true},
{NULL, Read_double(NULL), true}};
Read_named_member members[]=
{
{"name", Read_string(mem_root, &table_ctx->name), false},
{"file_stat_records",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&table_ctx->file_stat_records),
false},
{"data_file_length",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&table_ctx->data_file_length),
false},
{"index_file_length",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&table_ctx->index_file_length),
false},
{"mean_rec_length",
Read_non_neg_integer<ulong, ULONG_MAX>(&table_ctx->mean_rec_length),
false},
{"read_cost_io", Read_double(&table_ctx->read_cost_io), false},
{"read_cost_cpu", Read_double(&table_ctx->read_cost_cpu), false},
{"indexes",
Read_object_array<index_context_for_replay>(
mem_root, &table_ctx->index_list, index_context_for_replay::parse),
true},
{"multi_range_read_info_const_calls",
Read_object_array<Multi_range_read_const_call_record>(
mem_root, &table_ctx->ranges_list,
Multi_range_read_const_call_record::parse),
true},
{"cost_for_index_read_calls",
Read_object_array<cost_index_read_call_record>(
mem_root, &table_ctx->irc_list, cost_index_read_call_record::parse),
true},
{"records_in_range_calls",
Read_object_array<records_in_range_call_record>(
mem_root, &table_ctx->rir_list, records_in_range_call_record::parse),
true},
{NULL, Read_double(NULL), true}
};
return parse_context_obj_from_json_array(je, err_buf, err_msg, array);
if (json_read_object(je, members, err_buf))
return NULL; // Error
return table_ctx;
}
/*
@@ -1458,19 +1378,25 @@ static int parse_table_context(MEM_ROOT *mem_root, json_engine_t *je,
1 Parse Error
-1 EOF
*/
static int parse_index_context(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf,
index_context_for_replay *index_ctx)
{
const char *err_msg= "Expected an object in the indexes array";
Read_named_member array[]= {
index_context_for_replay*
index_context_for_replay::parse(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf)
{
auto index_ctx= new (mem_root) index_context_for_replay;
if (!index_ctx)
return NULL;
Read_named_member members[]= {
{"index_name", Read_string(mem_root, &index_ctx->idx_name), false},
{"rec_per_key", Read_rec_per_key(mem_root, &index_ctx->rec_per_key),
false},
{NULL, Read_double(NULL), true}};
return parse_context_obj_from_json_array(je, err_buf, err_msg, array);
if (json_read_object(je, members, err_buf))
return NULL; // Error
return index_ctx;
}
/*
@@ -1486,30 +1412,37 @@ static int parse_index_context(MEM_ROOT *mem_root, json_engine_t *je,
1 Parse Error
-1 EOF
*/
static int parse_range_context(MEM_ROOT *mem_root, json_engine_t *je, String *err_buf,
Multi_range_read_const_call_record *out)
Multi_range_read_const_call_record *
Multi_range_read_const_call_record::parse(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf)
{
const char *err_msg= "Expected an object in the multi_range_read_info_const_calls array";
auto out= new Multi_range_read_const_call_record;
if (!out)
return NULL;
Read_named_member array[]= {
{"index_name", Read_string(mem_root, &out->idx_name), false},
{"ranges", Read_array_of_strings(mem_root, &out->range_list), false},
{"num_rows", Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->rows),
false},
{"cost", Read_range_cost_estimate(mem_root, &out->cost), false},
{"mrr_mode", Read_non_neg_integer<uint, UINT_MAX>(&out->mrr_mode),
false},
{"max_index_blocks",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->max_index_blocks),
false},
{"max_row_blocks",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->max_row_blocks),
false},
{"call_number",
Read_non_neg_integer<ulong, ULONG_MAX>(&out->call_number), false},
{NULL, Read_double(NULL), true}};
Read_named_member members[]=
{
{"index_name", Read_string(mem_root, &out->idx_name), false},
{"ranges", Read_array_of_strings(mem_root, &out->range_list), false},
{"num_rows", Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->rows),
false},
{"cost", Read_range_cost_estimate(mem_root, &out->cost), false},
{"mrr_mode", Read_non_neg_integer<uint, UINT_MAX>(&out->mrr_mode),
false},
{"max_index_blocks",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->max_index_blocks),
false},
{"max_row_blocks",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->max_row_blocks),
false},
{"call_number",
Read_non_neg_integer<ulong, ULONG_MAX>(&out->call_number), false},
{NULL, Read_double(NULL), true}
};
return parse_context_obj_from_json_array(je, err_buf, err_msg, array);
if (json_read_object(je, members, err_buf))
return NULL; // Error
return out;
}
/*
@@ -1562,33 +1495,40 @@ static bool parse_range_cost_estimate(MEM_ROOT*, json_engine_t *je,
1 Parse Error
-1 EOF
*/
static int parse_index_read_cost_context(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf,
cost_index_read_call_record *out)
cost_index_read_call_record*
cost_index_read_call_record::parse(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf)
{
const char *err_msg= "Expected an object in the index_read_costs array";
auto out= new (mem_root) cost_index_read_call_record;
if (!out)
return NULL;
Read_named_member array[]= {
{"index_name", Read_string(mem_root, &out->idx_name), false},
{"num_records",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->records), false},
{"eq_ref", Read_non_neg_integer<bool, 1>(&out->eq_ref), false},
{"covering", Read_non_neg_integer<bool, 1>(&out->is_covering), false},
{"index_cost_io", Read_double(&out->cost.index_cost.io), false},
{"index_cost_cpu", Read_double(&out->cost.index_cost.cpu), false},
{"row_cost_io", Read_double(&out->cost.row_cost.io), false},
{"row_cost_cpu", Read_double(&out->cost.row_cost.cpu), false},
{"max_index_blocks",
Read_non_neg_integer<longlong, LONGLONG_MAX>(
&out->cost.max_index_blocks),
false},
{"max_row_blocks",
Read_non_neg_integer<longlong, LONGLONG_MAX>(&out->cost.max_row_blocks),
false},
{"copy_cost", Read_double(&out->cost.copy_cost), false},
{NULL, Read_double(NULL), true}};
Read_named_member members[]=
{
{"index_name", Read_string(mem_root, &out->idx_name), false},
{"num_records",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->records), false},
{"eq_ref", Read_non_neg_integer<bool, 1>(&out->eq_ref), false},
{"covering", Read_non_neg_integer<bool, 1>(&out->is_covering), false},
{"index_cost_io", Read_double(&out->cost.index_cost.io), false},
{"index_cost_cpu", Read_double(&out->cost.index_cost.cpu), false},
{"row_cost_io", Read_double(&out->cost.row_cost.io), false},
{"row_cost_cpu", Read_double(&out->cost.row_cost.cpu), false},
{"max_index_blocks",
Read_non_neg_integer<longlong, LONGLONG_MAX>(
&out->cost.max_index_blocks),
false},
{"max_row_blocks",
Read_non_neg_integer<longlong, LONGLONG_MAX>(&out->cost.max_row_blocks),
false},
{"copy_cost", Read_double(&out->cost.copy_cost), false},
{NULL, Read_double(NULL), true}
};
return parse_context_obj_from_json_array(je, err_buf, err_msg, array);
if (json_read_object(je, members, err_buf))
return NULL; //error
return out;
}
/*
@@ -1604,23 +1544,27 @@ static int parse_index_read_cost_context(MEM_ROOT *mem_root, json_engine_t *je,
1 Parse Error
-1 EOF
*/
static int parse_records_in_range_context(MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf,
records_in_range_call_record *out)
records_in_range_call_record*
records_in_range_call_record::parse(MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf)
{
const char *err_msg= "Expected an object in the records_in_range array";
auto out= new (mem_root) records_in_range_call_record;
if (!out)
return NULL;
Read_named_member array[]= {
{"key_number", Read_non_neg_integer<uint, UINT_MAX>(&out->keynr),
false},
{"min_key", Read_string(mem_root, &out->min_key), false},
{"max_key", Read_string(mem_root, &out->max_key), false},
{"num_records",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->records), false},
{NULL, Read_double(NULL), true}};
return parse_context_obj_from_json_array(je, err_buf, err_msg, array);
Read_named_member members[]=
{
{"key_number", Read_non_neg_integer<uint, UINT_MAX>(&out->keynr), false},
{"min_key", Read_string(mem_root, &out->min_key), false},
{"max_key", Read_string(mem_root, &out->max_key), false},
{"num_records",
Read_non_neg_integer<ha_rows, ULONGLONG_MAX>(&out->records), false},
{NULL, Read_double(NULL), true}
};
if (json_read_object(je, members, err_buf))
return NULL;
return out;
}
Optimizer_context_replay::Optimizer_context_replay(THD *thd_arg) : thd(thd_arg)
@@ -2056,11 +2000,15 @@ bool Optimizer_context_replay::parse()
char *var_name= thd->variables.optimizer_replay_context;
LEX_CSTRING varname= {var_name, strlen(var_name)};
Read_named_member array[]= {{"tables",
Read_array_into_list<table_context_for_replay>(
thd->mem_root, &ctx_list, parse_table_context),
false},
{NULL, Read_double(NULL), true}};
Read_named_member members[]=
{
{"tables",
Read_object_array<table_context_for_replay>(thd->mem_root, &ctx_list,
table_context_for_replay::parse),
false
},
{NULL, Read_double(NULL), true}
};
DBUG_ENTER("Optimizer_context_replay::parse");
@@ -2096,7 +2044,7 @@ bool Optimizer_context_replay::parse()
goto err;
}
if (json_read_object(&je, array, &err_buf))
if (json_read_object(&je, members, &err_buf))
goto err;
#ifndef DBUG_OFF

View File

@@ -231,6 +231,35 @@ public:
}
};
/*
Read a JSON object into a value of type T.
Object construction is done in reader_func, which is expected to
constcut object T and invoke json_read_object() for it.
*/
template <class T>
class Read_object : public Read_value
{
MEM_ROOT *mem_root;
T **ptr;
using reader_func_t= T* (*) (MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf);
reader_func_t reader_func;
public:
Read_object(MEM_ROOT *mem_arg, T **ptr_arg, reader_func_t reader_arg) :
mem_root(mem_arg), ptr(ptr_arg), reader_func(reader_arg) {}
bool read_value(json_engine_t *je, const char *value_name,
String *err_buf) override
{
*ptr = reader_func(mem_root, je, err_buf);
/* NULL return value means an error */
return (*ptr == NULL);
}
};
/*
Extends the Read_value interface to read an array of elements.
@@ -239,7 +268,8 @@ public:
*/
class Read_array : public Read_value
{
int before_read(json_engine_t *je, const char *value_name, String *err_buf)
int read_array_start(json_engine_t *je, const char *value_name,
String *err_buf)
{
if (json_scan_next(je) || je->state != JST_ARRAY_START)
{
@@ -250,14 +280,14 @@ class Read_array : public Read_value
}
return 0;
}
// here rc == -1 means we've caught array_end...
int after_read(int rc) { return rc > 0; }
public:
bool read_value(json_engine_t *je, const char *value_name,
String *err_buf) override
{
int rc= before_read(je, value_name, err_buf);
int rc= read_array_start(je, value_name, err_buf);
if (rc <= 0)
rc= read_container(je, value_name, err_buf);
return after_read(rc);
@@ -266,8 +296,7 @@ public:
String *err_buf)= 0;
};
//
// psergey-todo: why cannot this use Read_array_into_list ?
class Read_array_of_strings : public Read_array
{
MEM_ROOT *mem_root;
@@ -298,6 +327,64 @@ public:
}
};
/*
Parse a JSON array of objects info a List<T>.
The constructor accepts reader_func argument which reads one JSON object
into an object of type T.
*/
template <class T>
class Read_object_array : public Read_array
{
MEM_ROOT *mem_root;
List<T> *list_ctx;
/*
A function to read individual array element. It's an object of type T.
*/
using reader_func_t= T* (*) (MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf);
reader_func_t reader_func;
public:
Read_object_array(MEM_ROOT *mem_root_arg, List<T> *list_arg, reader_func_t reader_arg)
: mem_root(mem_root_arg), list_ctx(list_arg), reader_func(reader_arg)
{}
int read_container(json_engine_t *je, const char *name, String *err_buf)
override
{
DBUG_ASSERT(je->state == JST_ARRAY_START);
int rc= json_scan_next(je);
while (1)
{
if (je->state == JST_ARRAY_END)
return -1; // End of array
/* We get JST_VALUE state before each element. Check and consume it */
if (je->state != JST_VALUE)
return 1; // Error
if (json_scan_next(je))
return 1;
/* Read the element */
T *elem= reader_func(mem_root, je, err_buf);
if (!elem)
return 1; // Error
if (list_ctx->push_back(elem, mem_root))
return 1;
/* Consume JST_OBJ_END that is left after reading an object */
if (je->state == JST_OBJ_END && json_scan_next(je))
return 1; // Error
}
return rc;
}
};
}; /* namespace json_reader */
#endif

View File

@@ -26,9 +26,165 @@
#include "../sql/sql_json_lib.h"
#include "../sql/sql_json_lib.cc"
#include "json_lib.h"
using namespace json_reader;
/**************************************************************************
* Nested objects/arrays parsing tests
**************************************************************************/
class Level1_struct
{
public:
char *idx_name;
};
/*
Parse a JSON object into Level1_struct.
*/
Level1_struct* parse_level1_struct(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf)
{
//const char *err_msg= "Expected an object in the indexes array";
char *str;
Read_named_member array[]= {
{"index_name", Read_string(mem_root, &str), false},
{NULL, Read_double(NULL), true}};
int rc;
rc= json_read_object(je, array, err_buf);
if (rc)
return NULL;
return new Level1_struct{str};
}
class Level2_struct
{
public:
Level1_struct *child1;
Level1_struct *child2;
};
/*
Parse JSON into Level2_struct.
Level2_struct is structure with another two structures in it.
*/
static
Level2_struct* parse_level2_struct(MEM_ROOT *mem_root,
json_engine_t *je,
String *err_buf)
{
Level1_struct *ps1;
Level1_struct *ps2;
Read_named_member memb[]=
{
{"object1",
Read_object<Level1_struct>(mem_root, &ps1, parse_level1_struct),
false
},
{"object2",
Read_object<Level1_struct>(mem_root, &ps2, parse_level1_struct),
false
},
{NULL, Read_double(NULL), true}
};
int rc;
rc= json_read_object(je, memb, err_buf);
if (rc)
return NULL;
return new Level2_struct{ps1, ps2};
}
/*
Parse a JSON object that has an array of Level1_struct objects.
*/
int parse_array_of_level1_struct(MEM_ROOT *mem_root, json_engine_t *je,
String *err_buf, const char *js_doc,
List<Level1_struct> *index_list)
{
json_scan_start(je, &my_charset_utf8mb4_bin, (const uchar *) js_doc,
(const uchar *) js_doc + strlen(js_doc));
Read_named_member array[]= {
{"indexes",
Read_object_array<Level1_struct>(mem_root, index_list,
parse_level1_struct),
true},
{NULL, Read_double(NULL), true}};
int rc= json_read_object(je, array, err_buf);
return rc;
}
void test_parse_array_of_objects(MEM_ROOT *mem_root, json_engine_t *je)
{
String err_buf;
int rc;
const char *two_elems_doc=
"{ \n"
" \"indexes\" : [ \n"
" { \n"
" \"index_name\":\"index1\" \n"
" }, \n"
" { \n"
" \"index_name\":\"index2\" \n"
" } \n"
" ] \n"
"}";
List<Level1_struct> two_elems_list;
rc= parse_array_of_level1_struct(mem_root, je, &err_buf, two_elems_doc,
&two_elems_list);
ok(!rc, "Array of two objects parsed successfully");
ok(two_elems_list.elements == 2, ".. Got two objects");
const char *no_elems_doc=
"{ \n"
" \"indexes\" : [ \n"
" ] \n"
"}";
List<Level1_struct> no_elems_list;
rc= parse_array_of_level1_struct(mem_root, je, &err_buf, no_elems_doc,
&no_elems_list);
ok(!rc, "Empty array of objects parsed successfully");
ok(two_elems_list.elements == 2, ".. Got an empty list");
}
void test_parse_nested_object(MEM_ROOT *mem_root, json_engine_t *je)
{
String err_buf;
const char *js_doc=
"{ \n"
" \"object1\" : \n"
" { \n"
" \"index_name\":\"index1\" \n"
" }, \n"
" \"object2\" : \n"
" { \n"
" \"index_name\":\"index2\" \n"
" } \n"
" } \n"
"}";
json_scan_start(je, &my_charset_utf8mb4_bin, (const uchar *) js_doc,
(const uchar *) js_doc + strlen(js_doc));
Level2_struct *data= parse_level2_struct(mem_root, je, &err_buf);
ok(data!=NULL, "Nested object successfully");
ok(!strcmp(data->child1->idx_name, "index1"), "Child object 1 is ok");
ok(!strcmp(data->child2->idx_name, "index2"), "Child object 2 is ok");
}
int main(int args, char **argv)
{
MY_INIT(argv[0]);
@@ -80,6 +236,38 @@ int main(int args, char **argv)
ok(!strcmp(str, field_val), "Got correct value for grinning face");
}
diag("Test reading array-of-strings");
{
const char *js_doc="{ \"arr\" : [\"foo\", \"bar\", \"baz\"] }";
json_scan_start(&je, &my_charset_utf8mb4_bin, (const uchar *) js_doc,
(const uchar *) js_doc + strlen(js_doc));
List<char> str_array;
Read_named_member array[]= {
{"arr", Read_array_of_strings(&alloc, &str_array), false},
{NULL, Read_double(NULL), false }
};
rc= json_read_object(&je, array, &err_buf);
ok(!rc, "Success parsing 3-element array");
ok(str_array.elements == 3, "Got 3-element array");
}
{
const char *js_doc="{ \"arr\" : [] }";
json_scan_start(&je, &my_charset_utf8mb4_bin, (const uchar *) js_doc,
(const uchar *) js_doc + strlen(js_doc));
List<char> str_array;
Read_named_member array[]= {
{"arr", Read_array_of_strings(&alloc, &str_array), false},
{NULL, Read_double(NULL), false }
};
rc= json_read_object(&je, array, &err_buf);
ok(!rc, "Success parsing empty array");
ok(str_array.elements == 0, "Got an empty array");
}
test_parse_array_of_objects(&alloc, &je);
test_parse_nested_object(&alloc, &je);
free_root(&alloc, 0);
diag("Done");