diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-01 18:15:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-01 18:15:00 +0000 |
commit | a2a2e32c02643a0cec111511220227703fda1cd5 (patch) | |
tree | 69cc2b631234c2a8e026b9cd4d72676c61c594df /sql/json_schema.cc | |
parent | Releasing progress-linux version 1:10.11.8-1~progress7.99u1. (diff) | |
download | mariadb-a2a2e32c02643a0cec111511220227703fda1cd5.tar.xz mariadb-a2a2e32c02643a0cec111511220227703fda1cd5.zip |
Merging upstream version 1:11.4.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sql/json_schema.cc')
-rw-r--r-- | sql/json_schema.cc | 2874 |
1 files changed, 2874 insertions, 0 deletions
diff --git a/sql/json_schema.cc b/sql/json_schema.cc new file mode 100644 index 00000000..8f818abc --- /dev/null +++ b/sql/json_schema.cc @@ -0,0 +1,2874 @@ +/* Copyright (c) 2016, 2022, MariaDB Corporation. + + 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-1301 USA */ + + +#include "mariadb.h" +#include "sql_class.h" +#include "sql_parse.h" // For check_stack_overrun +#include <m_string.h> +#include "json_schema.h" +#include "json_schema_helper.h" +#include "pcre2.h" +static HASH all_keywords_hash; + +static Json_schema_keyword *create_json_schema_keyword(THD *thd) +{ + return new (thd->mem_root) Json_schema_keyword(); +} +static Json_schema_keyword *create_json_schema_type(THD *thd) +{ + return new (thd->mem_root) Json_schema_type(); +} +static Json_schema_keyword *create_json_schema_enum(THD *thd) +{ + return new (thd->mem_root) Json_schema_enum(); +} +static Json_schema_keyword *create_json_schema_const(THD *thd) +{ + return new (thd->mem_root) Json_schema_const(); +} +static Json_schema_keyword *create_json_schema_maximum(THD *thd) +{ + return new (thd->mem_root) Json_schema_maximum(); +} +static Json_schema_keyword *create_json_schema_minimum(THD *thd) +{ + return new (thd->mem_root) Json_schema_minimum(); +} +static Json_schema_keyword *create_json_schema_ex_max(THD *thd) +{ + return new (thd->mem_root) Json_schema_ex_maximum(); +} +static Json_schema_keyword *create_json_schema_ex_min(THD *thd) +{ + return new (thd->mem_root) Json_schema_ex_minimum(); +} +static Json_schema_keyword *create_json_schema_multiple_of(THD *thd) +{ + return new (thd->mem_root) Json_schema_multiple_of(); +} +static Json_schema_keyword *create_json_schema_max_len(THD *thd) +{ + return new (thd->mem_root) Json_schema_max_len(); +} +static Json_schema_keyword *create_json_schema_min_len(THD *thd) +{ + return new (thd->mem_root) Json_schema_min_len(); +} +static Json_schema_keyword *create_json_schema_pattern(THD *thd) +{ + return new (thd->mem_root) Json_schema_pattern(); +} +static Json_schema_keyword *create_json_schema_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_items(); +} +static Json_schema_keyword *create_json_schema_max_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_max_items(); +} +static Json_schema_keyword *create_json_schema_min_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_min_items(); +} +static Json_schema_keyword *create_json_schema_prefix_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_prefix_items(); +} +static Json_schema_keyword *create_json_schema_contains(THD *thd) +{ + return new (thd->mem_root) Json_schema_contains(); +} +static Json_schema_keyword *create_json_schema_max_contains(THD *thd) +{ + return new (thd->mem_root) Json_schema_max_contains(); +} +static Json_schema_keyword *create_json_schema_min_contains(THD *thd) +{ + return new (thd->mem_root) Json_schema_min_contains(); +} +static Json_schema_keyword *create_json_schema_unique_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_unique_items(); +} +static Json_schema_keyword *create_json_schema_additional_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_additional_items(); +} +static Json_schema_keyword *create_json_schema_unevaluated_items(THD *thd) +{ + return new (thd->mem_root) Json_schema_unevaluated_items(); +} +static Json_schema_keyword *create_json_schema_properties(THD *thd) +{ + return new (thd->mem_root) Json_schema_properties(); +} +static Json_schema_keyword *create_json_schema_pattern_properties(THD *thd) +{ + return new (thd->mem_root) Json_schema_pattern_properties(); +} +static Json_schema_keyword *create_json_schema_additional_properties(THD *thd) +{ + return new (thd->mem_root) Json_schema_additional_properties(); +} +static Json_schema_keyword *create_json_schema_unevaluated_properties(THD *thd) +{ + return new (thd->mem_root) Json_schema_unevaluated_properties(); +} +static Json_schema_keyword *create_json_schema_property_names(THD *thd) +{ + return new (thd->mem_root) Json_schema_property_names(); +} +static Json_schema_keyword *create_json_schema_max_prop(THD *thd) +{ + return new (thd->mem_root) Json_schema_max_prop(); +} +static Json_schema_keyword *create_json_schema_min_prop(THD *thd) +{ + return new (thd->mem_root) Json_schema_min_prop(); +} +static Json_schema_keyword *create_json_schema_required(THD *thd) +{ + return new (thd->mem_root) Json_schema_required(); +} +static Json_schema_keyword *create_json_schema_dependent_required(THD *thd) +{ + return new (thd->mem_root) Json_schema_dependent_required(); +} +static Json_schema_keyword *create_json_schema_dependent_schemas(THD *thd) +{ + return new (thd->mem_root) Json_schema_dependent_schemas(); +} +static Json_schema_keyword *create_json_schema_not(THD *thd) +{ + return new (thd->mem_root) Json_schema_not(); +} +static Json_schema_keyword *create_json_schema_all_of(THD *thd) +{ + return new (thd->mem_root) Json_schema_all_of(); +} +static Json_schema_keyword *create_json_schema_any_of(THD *thd) +{ + return new (thd->mem_root) Json_schema_any_of(); +} +static Json_schema_keyword *create_json_schema_one_of(THD *thd) +{ + return new (thd->mem_root) Json_schema_one_of(); +} +static Json_schema_keyword *create_json_schema_if(THD *thd) +{ + return new (thd->mem_root) Json_schema_if(); +} +static Json_schema_keyword *create_json_schema_then(THD *thd) +{ + return new (thd->mem_root) Json_schema_then(); +} +static Json_schema_keyword *create_json_schema_else(THD *thd) +{ + return new (thd->mem_root) Json_schema_else(); +} +static Json_schema_keyword *create_json_schema_annotation(THD *thd) +{ + return new (thd->mem_root) Json_schema_annotation(); +} +static Json_schema_keyword *create_json_schema_format(THD *thd) +{ + return new (thd->mem_root) Json_schema_format(); +} +static Json_schema_keyword *create_json_schema_media_string(THD *thd) +{ + return new (thd->mem_root) Json_schema_media_string(); +} +static Json_schema_keyword *create_json_schema_reference(THD *thd) +{ + return new (thd->mem_root) Json_schema_reference(); +} + + static st_json_schema_keyword_map json_schema_func_array[]= +{ + {{ STRING_WITH_LEN("type") }, create_json_schema_type, JSON_SCHEMA_COMMON_KEYWORD}, + {{ STRING_WITH_LEN("const") }, create_json_schema_const, JSON_SCHEMA_COMMON_KEYWORD}, + {{ STRING_WITH_LEN("enum") }, create_json_schema_enum, JSON_SCHEMA_COMMON_KEYWORD}, + + {{ STRING_WITH_LEN("maximum") }, create_json_schema_maximum, JSON_SCHEMA_NUMBER_KEYWORD}, + {{ STRING_WITH_LEN("minimum") }, create_json_schema_minimum, JSON_SCHEMA_NUMBER_KEYWORD}, + {{ STRING_WITH_LEN("exclusiveMaximum") }, create_json_schema_ex_max, JSON_SCHEMA_NUMBER_KEYWORD}, + {{ STRING_WITH_LEN("exclusiveMinimum") }, create_json_schema_ex_min, JSON_SCHEMA_NUMBER_KEYWORD}, + {{ STRING_WITH_LEN("multipleOf") }, create_json_schema_multiple_of, JSON_SCHEMA_NUMBER_KEYWORD}, + + {{ STRING_WITH_LEN("maxLength") }, create_json_schema_max_len, JSON_SCHEMA_STRING_KEYWORD}, + {{ STRING_WITH_LEN("minLength") }, create_json_schema_min_len, JSON_SCHEMA_STRING_KEYWORD}, + {{ STRING_WITH_LEN("pattern") }, create_json_schema_pattern, JSON_SCHEMA_STRING_KEYWORD}, + + {{ STRING_WITH_LEN("items") }, create_json_schema_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("maxItems") }, create_json_schema_max_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("minItems") }, create_json_schema_min_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("additionalItems") }, create_json_schema_additional_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("unevaluatedItems") }, create_json_schema_unevaluated_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("prefixItems") }, create_json_schema_prefix_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("uniqueItems") }, create_json_schema_unique_items, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("contains") }, create_json_schema_contains, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("maxContains") }, create_json_schema_max_contains, JSON_SCHEMA_ARRAY_KEYWORD}, + {{ STRING_WITH_LEN("minContains") }, create_json_schema_min_contains, JSON_SCHEMA_ARRAY_KEYWORD}, + + {{ STRING_WITH_LEN("properties") }, create_json_schema_properties, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("patternProperties") }, create_json_schema_pattern_properties, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("propertyNames") }, create_json_schema_property_names, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("maxProperties") }, create_json_schema_max_prop, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("minProperties") }, create_json_schema_min_prop, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("dependentRequired") }, create_json_schema_dependent_required, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("dependentSchemas") }, create_json_schema_dependent_schemas, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("required") }, create_json_schema_required, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("additionalProperties") }, create_json_schema_additional_properties, JSON_SCHEMA_OBJECT_KEYWORD}, + {{ STRING_WITH_LEN("unevaluatedProperties") }, create_json_schema_unevaluated_properties, JSON_SCHEMA_OBJECT_KEYWORD}, + + {{ STRING_WITH_LEN("not") }, create_json_schema_not, JSON_SCHEMA_LOGIC_KEYWORD}, + {{ STRING_WITH_LEN("allOf") }, create_json_schema_all_of, JSON_SCHEMA_LOGIC_KEYWORD}, + {{ STRING_WITH_LEN("anyOf") }, create_json_schema_any_of, JSON_SCHEMA_LOGIC_KEYWORD}, + {{ STRING_WITH_LEN("oneOf") }, create_json_schema_one_of, JSON_SCHEMA_LOGIC_KEYWORD}, + + {{ STRING_WITH_LEN("if") }, create_json_schema_if, JSON_SCHEMA_CONDITION_KEYWORD}, + {{ STRING_WITH_LEN("then") }, create_json_schema_then, JSON_SCHEMA_CONDITION_KEYWORD}, + {{ STRING_WITH_LEN("else") }, create_json_schema_else, JSON_SCHEMA_CONDITION_KEYWORD}, + + {{ STRING_WITH_LEN("title") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("description") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("comment") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("$schema") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("deprecated") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("readOnly") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("writeOnly") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("example") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("default") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + {{ STRING_WITH_LEN("$vocabulary") }, create_json_schema_annotation, JSON_SCHEMA_ANNOTATION_KEYWORD}, + + {{ STRING_WITH_LEN("date-time") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("date") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("time") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("duration") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("email") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("idn-email") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("hostname") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("idn-hostname") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("ipv4") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("ipv6") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("uri") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("uri-reference") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("iri") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("iri-reference") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("uuid") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("json-pointer") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("relative-json-pointer") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + {{ STRING_WITH_LEN("regex") }, create_json_schema_format, JSON_SCHEMA_FORMAT_KEYWORD}, + + {{ STRING_WITH_LEN("contentMediaType") }, create_json_schema_media_string, JSON_SCHEMA_MEDIA_KEYWORD}, + {{ STRING_WITH_LEN("conentEncoding") }, create_json_schema_media_string, JSON_SCHEMA_MEDIA_KEYWORD}, + {{ STRING_WITH_LEN("contentSchema") }, create_json_schema_media_string, JSON_SCHEMA_MEDIA_KEYWORD}, + + {{ STRING_WITH_LEN("$ref") }, create_json_schema_reference, JSON_SCHEMA_REFERENCE_KEYWORD}, + {{ STRING_WITH_LEN("$id") }, create_json_schema_reference, JSON_SCHEMA_REFERENCE_KEYWORD}, + {{ STRING_WITH_LEN("$anchor") }, create_json_schema_reference, JSON_SCHEMA_REFERENCE_KEYWORD}, + {{ STRING_WITH_LEN("$defs") }, create_json_schema_reference, JSON_SCHEMA_REFERENCE_KEYWORD}, + {{ STRING_WITH_LEN("$dynamicRef") }, create_json_schema_reference, JSON_SCHEMA_REFERENCE_KEYWORD}, + {{ STRING_WITH_LEN("$dynamicAnchor") }, create_json_schema_reference, JSON_SCHEMA_REFERENCE_KEYWORD}, +}; + +static st_json_schema_keyword_map empty_func_map= + {{ STRING_WITH_LEN("") }, create_json_schema_keyword, JSON_SCHEMA_EMPTY_KEYWORD}; + +/* + When some schemas dont validate, we want to check the annotation + for an alternate schema. Example, when we have "properties" and + "patternProperties", if "properties" does not validate for a certain + keyname, then we want to check if it validates for "patternProperties". + In this case "patterProperties" will be alternate schema for "properties". +*/ +bool Json_schema_keyword::fall_back_on_alternate_schema(const json_engine_t + *je, + const uchar* k_start, + const uchar* k_end) +{ + if (alternate_schema) + { + if (alternate_schema->allowed) + { + if (alternate_schema->validate_as_alternate(je, k_start, k_end)) + return true; + } + else + return true; + } + return false; +} + +bool Json_schema_annotation::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + bool is_invalid_value_type= false, res= false; + + if (this->keyword_map == &(json_schema_func_array[38]) || + this->keyword_map == &(json_schema_func_array[39]) || + this->keyword_map == &(json_schema_func_array[40]) || + this->keyword_map == &(json_schema_func_array[41])) + { + if (je->value_type != JSON_VALUE_STRING) + is_invalid_value_type= true; + } + else if (this->keyword_map == &(json_schema_func_array[42]) || + this->keyword_map == &(json_schema_func_array[43]) || + this->keyword_map == &(json_schema_func_array[44])) + { + if (je->value_type != JSON_VALUE_TRUE && + je->value_type != JSON_VALUE_FALSE) + is_invalid_value_type= true; + } + else if (this->keyword_map == &(json_schema_func_array[45])) + { + if (je->value_type != JSON_VALUE_ARRAY) + is_invalid_value_type= true; + if (json_skip_level(je)) + return true; + } + else if (this->keyword_map == &(json_schema_func_array[46])) + return false; + + if (is_invalid_value_type) + { + res= true; + String keyword(0); + keyword.append((const char*)key_start, (int)(key_end-key_start)); + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), keyword.ptr()); + } + return res; +} + +bool Json_schema_format::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_STRING) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "format"); + } + return false; +} + +bool Json_schema_type::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + return !((1 << je->value_type) & type); +} + +bool Json_schema_type::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type == JSON_VALUE_ARRAY) + { + int level= je->stack_p; + while (json_scan_next(je)==0 && je->stack_p >= level) + { + if (json_read_value(je)) + return true; + json_assign_type(&type, je); + } + return je->s.error ? true : false; + } + else if (je->value_type == JSON_VALUE_STRING) + { + return json_assign_type(&type, je); + } + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "type"); + return true; + } +} + +bool Json_schema_const::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je; + curr_je= *je; + const char *start= (char*)curr_je.value; + const char *end= (char*)curr_je.value+curr_je.value_len; + json_engine_t temp_je= *je; + json_engine_t temp_je_2; + String a_res("", 0, curr_je.s.cs); + int err= 0; + + if (type != curr_je.value_type) + return true; + + if (curr_je.value_type <= JSON_VALUE_NUMBER) + { + if (!json_value_scalar(&temp_je)) + { + if (json_skip_level(&temp_je)) + { + curr_je= temp_je; + return true; + } + end= (char*)temp_je.s.c_str; + } + String val((char*)temp_je.value, end-start, temp_je.s.cs); + + json_scan_start(&temp_je_2, temp_je.s.cs, (const uchar *) val.ptr(), + (const uchar *) val.end()); + + if (temp_je.value_type != JSON_VALUE_STRING) + { + if (json_read_value(&temp_je_2)) + { + curr_je= temp_je; + return true; + } + json_get_normalized_string(&temp_je_2, &a_res, &err); + if (err) + return true; + } + else + a_res.append(val.ptr(), val.length(), temp_je.s.cs); + + if (a_res.length() == strlen(const_json_value) && + !strncmp((const char*)const_json_value, a_res.ptr(), + a_res.length())) + return false; + return true; + } + return false; +} + +bool Json_schema_const::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + const char *start= (char*)je->value, *end= (char*)je->value+je->value_len; + json_engine_t temp_je; + String a_res("", 0, je->s.cs); + int err; + + type= je->value_type; + + if (!json_value_scalar(je)) + { + if (json_skip_level(je)) + return true; + end= (char*)je->s.c_str; + } + + String val((char*)je->value, end-start, je->s.cs); + + json_scan_start(&temp_je, je->s.cs, (const uchar *) val.ptr(), + (const uchar *) val.end()); + if (je->value_type != JSON_VALUE_STRING) + { + if (json_read_value(&temp_je)) + return true; + json_get_normalized_string(&temp_je, &a_res, &err); + if (err) + return true; + } + else + a_res.append(val.ptr(), val.length(), je->s.cs); + + this->const_json_value= (char*)alloc_root(thd->mem_root, + a_res.length()+1); + if (!const_json_value) + return true; + + const_json_value[a_res.length()]= '\0'; + strncpy(const_json_value, (const char*)a_res.ptr(), a_res.length()); + + return false; +} + +bool Json_schema_enum::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t temp_je; + temp_je= *je; + + String norm_str((char*)"",0, je->s.cs); + + String a_res("", 0, je->s.cs); + int err= 1; + + if (temp_je.value_type > JSON_VALUE_NUMBER) + { + if (!(enum_scalar & (1 << temp_je.value_type))) + return true; + else + return false; + } + json_get_normalized_string(&temp_je, &a_res, &err); + if (err) + return true; + + norm_str.append((const char*)a_res.ptr(), a_res.length(), je->s.cs); + + if (my_hash_search(&this->enum_values, (const uchar*)(norm_str.ptr()), + strlen((const char*)(norm_str.ptr())))) + return false; + else + return true; +} + +bool Json_schema_enum::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int count= 0; + if (my_hash_init(PSI_INSTRUMENT_ME, + &this->enum_values, + je->s.cs, 1024, 0, 0, (my_hash_get_key) get_key_name, + NULL, 0)) + return true; + + if (je->value_type == JSON_VALUE_ARRAY) + { + int curr_level= je->stack_p; + while(json_scan_next(je) == 0 && curr_level <= je->stack_p) + { + if (json_read_value(je)) + return true; + count++; + if (je->value_type > JSON_VALUE_NUMBER) + { + if (!(enum_scalar & (1 << je->value_type))) + enum_scalar|= 1 << je->value_type; + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "enum"); + return true; + } + } + else + { + char *norm_str; + int err= 1; + String a_res("", 0, je->s.cs); + + json_get_normalized_string(je, &a_res, &err); + if (err) + return true; + + norm_str= (char*)alloc_root(thd->mem_root, + a_res.length()+1); + if (!norm_str) + return true; + else + { + norm_str[a_res.length()]= '\0'; + strncpy(norm_str, (const char*)a_res.ptr(), a_res.length()); + if (!my_hash_search(&this->enum_values, (uchar*)norm_str, + strlen(norm_str))) + { + if (my_hash_insert(&this->enum_values, (uchar*)norm_str)) + return true; + } + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "enum"); + return true; + } + } + } + } + if (!count) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "enum"); + return true; + } + return false; + } + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "enum"); + return true; + } +} + +bool Json_schema_maximum::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + return false; + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + return (val <= value) ? false : true; +} + +bool Json_schema_maximum::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maximum"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + value= val; + + return false; +} + +bool Json_schema_minimum::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + return false; + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + return val >= value ? false : true; +} + +bool Json_schema_minimum::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minimum"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + value= val; + + return false; +} + +bool Json_schema_ex_minimum::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + return false; + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + return (val > value) ? false : true; +} + +bool Json_schema_ex_minimum::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "exclusiveMinimum"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + value= val; + + return false; +} + +bool Json_schema_ex_maximum::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + return false; + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + return (val < value) ? false : true; +} + +bool Json_schema_ex_maximum::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "exclusiveMaximum"); + return true; + } + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + value= val; + + return false; +} + +bool Json_schema_multiple_of::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + return false; + if (je->num_flags & JSON_NUM_FRAC_PART) + return true; + + longlong val= je->s.cs->strntoll((char *) je->value, + je->value_len, 10, &end, &err); + + return val % multiple_of; +} + +bool Json_schema_multiple_of::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER || (je->num_flags & JSON_NUM_FRAC_PART)) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "multipleOf"); + return true; + } + + longlong val= je->s.cs->strntoll((char *) je->value, + je->value_len, 10, &end, &err); + if (val <= 0) + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "multipleOf"); + multiple_of= val; + + return false; +} + + +bool Json_schema_max_len::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + if (je->value_type != JSON_VALUE_STRING) + return false; + return (uint)(je->value_len) <= value ? false : true; +} + +bool Json_schema_max_len::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxLength"); + return true; + } + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxLength"); + return true; + } + value= (int)val; + + return false; +} + +bool Json_schema_min_len::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + if (je->value_type != JSON_VALUE_STRING) + return false; + return (uint)(je->value_len) >= value ? false : true; +} + +bool Json_schema_min_len::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minLength"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minLength"); + return true; + } + value= (int)val; + + return false; +} + +bool Json_schema_pattern::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + bool pattern_matches= false; + + /* + We want to match a single pattern against multiple + string when (see below): + 1) with "pattern" when there are different json strings + to be validated against one pattern. + 2) with "propertyNames", where there is one pattern but + multiple property names to be validated against one + pattern + */ + if (!k_start && !k_end) + { + /* 1) */ + if (je->value_type != JSON_VALUE_STRING) + return false; + str->str_value.set_or_copy_aligned((const char*)je->value, + (size_t)je->value_len, je->s.cs); + } + else + { + /* 2) */ + str->str_value.set_or_copy_aligned((const char*)k_start, + (size_t)(k_end-k_start), je->s.cs); + } + if (re.recompile(pattern)) + return true; + if (re.exec(str, 0, 0)) + return true; + pattern_matches= re.match(); + + return pattern_matches ? false : true; +} + +bool Json_schema_pattern::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_STRING) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "pattern"); + return true; + } + + my_repertoire_t repertoire= my_charset_repertoire(je->s.cs); + pattern= thd->make_string_literal((const char*)je->value, + je->value_len, repertoire); + str= new (thd->mem_root) Item_string(thd, "", (uint) 0, je->s.cs); + re.init(je->s.cs, 0); + re.unset_flag(PCRE2_CASELESS); + + return false; +} + +bool Json_schema_max_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + uint count= 0; + json_engine_t curr_je; + + curr_je= *je; + + if (curr_je.value_type != JSON_VALUE_ARRAY) + return false; + + int level= curr_je.stack_p; + while(json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + if (json_read_value(&curr_je)) + return true; + count++; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + return count > value ? true : false; +} + +bool Json_schema_max_items::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxItems"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxItems"); + return true; + } + value= (int)val; + + return false; +} + +bool Json_schema_min_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + uint count= 0; + json_engine_t curr_je; + + curr_je= *je; + + if (curr_je.value_type != JSON_VALUE_ARRAY) + return false; + + int level= curr_je.stack_p; + while(json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + if (json_read_value(&curr_je)) + return true; + count++; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + return count < value ? true : false; +} + +bool Json_schema_min_items::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minItems"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxLength"); + return true; + } + value= (int)val; + + return false; +} + +bool Json_schema_max_contains::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxContains"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxContains"); + return true; + } + value= val; + return false; +} + + +bool Json_schema_min_contains::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minContains"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + value= val; + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minContains"); + return true; + } + + return false; +} + + +bool Json_schema_contains::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + uint contains_count=0; + json_engine_t curr_je; curr_je= *je; + int level= je->stack_p; + bool validated= true; + + if (curr_je.value_type != JSON_VALUE_ARRAY) + return false; + + while(json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + if (json_read_value(&curr_je)) + return true; + validated= true; + if (validate_schema_items(&curr_je, &contains)) + validated= false; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + if (validated) + contains_count++; + } + + if ((max_contains ? contains_count <= max_contains->value : + contains_count>0) && + (min_contains ? contains_count >= min_contains->value : + contains_count>0)) + return false; + + return true; +} + +bool Json_schema_contains::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "contains"); + return true; + } + return create_object_and_handle_keyword(thd, je, &contains, all_keywords); +} + + +bool Json_schema_items::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type == JSON_VALUE_FALSE) + { + set_allowed(false); + return false; + } + if (je->value_type == JSON_VALUE_OBJECT) + { + return create_object_and_handle_keyword(thd, je, &items_schema, + all_keywords); + } + else if (je->value_type != JSON_VALUE_TRUE) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "items"); + return true; + } + return false; +} + +bool Json_schema_items::validate_as_alternate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + /* + The indexes in prefix array were less than that in the json array. + So validate remaining using the json schema + */ + return validate_schema_items(je, &items_schema); +} + +bool Json_schema_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + + /* + There was no "prefixItesm", so we validate all values in the + array using one schema. + */ + int level= je->stack_p, count=0; + bool is_false= false; + json_engine_t curr_je= *je; + + if (je->value_type != JSON_VALUE_ARRAY) + return false; + + if (!allowed) + is_false= true; + + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + if (json_read_value(&curr_je)) + return true; + count++; + if (validate_schema_items(&curr_je, &items_schema)) + return true; + } + + return is_false ? (!count ? false : true) : false; +} + +bool Json_schema_prefix_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + int level= je->stack_p; + json_engine_t curr_je= *je; + List_iterator <List<Json_schema_keyword>> it1 (prefix_items); + List<Json_schema_keyword> *curr_prefix; + + if (curr_je.value_type != JSON_VALUE_ARRAY) + return false; + + while(curr_je.s.c_str < curr_je.s.str_end && json_scan_next(&curr_je)==0 && + curr_je.stack_p >= level) + { + if (json_read_value(&curr_je)) + return true; + if (!(curr_prefix=it1++)) + { + if (fall_back_on_alternate_schema(&curr_je)) + return true; + else + { + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + } + else + { + if (validate_schema_items(&curr_je, &(*curr_prefix))) + return true; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + } + return false; +} + +bool Json_schema_prefix_items::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_ARRAY) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "prefixItems"); + return true; + } + + int level= je->stack_p; + while(json_scan_next(je)==0 && je->stack_p >= level) + { + json_engine_t temp_je; + char *begin, *end; + int len; + + if (json_read_value(je)) + return true; + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "items"); + return true; + } + begin= (char*)je->value; + + if (json_skip_level(je)) + return true; + + end= (char*)je->s.c_str; + len= (int)(end-begin); + + json_scan_start(&temp_je, je->s.cs, (const uchar *) begin, + (const uchar *)begin+len); + List<Json_schema_keyword> *keyword_list= + new (thd->mem_root) List<Json_schema_keyword>; + + if (!keyword_list) + return true; + if (create_object_and_handle_keyword(thd, &temp_je, keyword_list, + all_keywords)) + return true; + + prefix_items.push_back(keyword_list, thd->mem_root); + } + + return false; +} + +bool Json_schema_unique_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + HASH unique_items; + List <char> norm_str_list; + json_engine_t curr_je= *je; + int res= true, level= curr_je.stack_p, scalar_val= 0; + + if (curr_je.value_type != JSON_VALUE_ARRAY) + return false; + + if (my_hash_init(PSI_INSTRUMENT_ME, &unique_items, curr_je.s.cs, + 1024, 0, 0, (my_hash_get_key) get_key_name, NULL, 0)) + return true; + + while(json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + int err= 1; + char *norm_str; + String a_res("", 0, curr_je.s.cs); + + if (json_read_value(&curr_je)) + goto end; + + json_get_normalized_string(&curr_je, &a_res, &err); + + if (err) + goto end; + + norm_str= (char*)malloc(a_res.length()+1); + if (!norm_str) + goto end; + + norm_str[a_res.length()]= '\0'; + strncpy(norm_str, (const char*)a_res.ptr(), a_res.length()); + norm_str_list.push_back(norm_str); + + if (curr_je.value_type > JSON_VALUE_NUMBER) + { + if (scalar_val & 1 << curr_je.value_type) + goto end; + scalar_val|= 1 << curr_je.value_type; + } + else + { + if (!my_hash_search(&unique_items, (uchar*)norm_str, + strlen(((const char*)norm_str)))) + { + if (my_hash_insert(&unique_items, (uchar*)norm_str)) + goto end; + } + else + goto end; + } + a_res.set("", 0, curr_je.s.cs); + } + + res= false; + + end: + if (!norm_str_list.is_empty()) + { + List_iterator<char> it(norm_str_list); + char *curr_norm_str; + while ((curr_norm_str= it++)) + free(curr_norm_str); + norm_str_list.empty(); + } + my_hash_free(&unique_items); + return res; +} + +bool Json_schema_unique_items::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type == JSON_VALUE_TRUE) + is_unique= true; + else if (je->value_type == JSON_VALUE_FALSE) + is_unique= false; + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "uniqueItems"); + return true; + } + return false; +} + + +bool Json_schema_max_prop::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + uint properties_count= 0; + json_engine_t curr_je= *je; + int curr_level= je->stack_p; + + if (curr_je.value_type != JSON_VALUE_OBJECT) + return false; + + while (json_scan_next(&curr_je)== 0 && je->stack_p >= curr_level) + { + switch (curr_je.state) + { + case JST_KEY: + { + if (json_read_value(&curr_je)) + return true; + properties_count++; + + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + } + } + return properties_count > value ? true : false; +} + +bool Json_schema_max_prop::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxProperties"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxProperties"); + return true; + } + value= (int)val; + + return false; +} + +bool Json_schema_min_prop::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + uint properties_count= 0; + int curr_level= je->stack_p; + json_engine_t curr_je= *je; + + if (curr_je.value_type != JSON_VALUE_OBJECT) + return false; + + while (json_scan_next(&curr_je)== 0 && je->stack_p >= curr_level) + { + switch (curr_je.state) + { + case JST_KEY: + { + if (json_read_value(&curr_je)) + return true; + properties_count++; + + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + } + } + return properties_count < value ? true : false; +} + +bool Json_schema_min_prop::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int err= 0; + char *end; + + if (je->value_type != JSON_VALUE_NUMBER) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minProperties"); + return true; + } + + double val= je->s.cs->strntod((char *) je->value, + je->value_len, &end, &err); + if (val < 0) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minProperties"); + return true; + } + value= (int)val; + + return false; +} + +bool Json_schema_required::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + List<char> malloc_mem_list; + HASH required; + int res= true, curr_level= curr_je.stack_p; + List_iterator<String> it(required_properties); + String *curr_str; + + if (curr_je.value_type != JSON_VALUE_OBJECT) + return false; + + if(my_hash_init(PSI_INSTRUMENT_ME, &required, + curr_je.s.cs, 1024, 0, 0, (my_hash_get_key) get_key_name, + NULL, 0)) + return true; + while (json_scan_next(&curr_je)== 0 && curr_je.stack_p >= curr_level) + { + switch (curr_je.state) + { + case JST_KEY: + { + const uchar *key_end, *key_start; + int key_len; + char *str; + + key_start= curr_je.s.c_str; + do + { + key_end= curr_je.s.c_str; + } while (json_read_keyname_chr(&curr_je) == 0); + + key_len= (int)(key_end-key_start); + str= (char*)malloc((size_t)(key_len)+1); + strncpy(str, (const char*)key_start, key_len); + str[key_len]='\0'; + + if (my_hash_insert(&required, (const uchar*)str)) + goto error; + malloc_mem_list.push_back(str); + } + } + } + while ((curr_str= it++)) + { + if (!my_hash_search(&required, (const uchar*)curr_str->ptr(), + curr_str->length())) + goto error; + } + res= false; + error: + if (!malloc_mem_list.is_empty()) + { + List_iterator<char> it(malloc_mem_list); + char *curr_ptr; + while ((curr_ptr= it++)) + free(curr_ptr); + malloc_mem_list.empty(); + } + my_hash_free(&required); + return res; +} + +bool Json_schema_required::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + int level= je->stack_p; + + if (je->value_type != JSON_VALUE_ARRAY) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "required"); + return true; + } + while(json_scan_next(je)==0 && level <= je->stack_p) + { + if (json_read_value(je)) + return true; + if (je->value_type != JSON_VALUE_STRING) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "required"); + return true; + } + else + { + String *str= new (thd->mem_root)String((char*)je->value, + je->value_len, je->s.cs); + this->required_properties.push_back(str, thd->mem_root); + } + } + return je->s.error ? true : false; +} + +bool Json_schema_dependent_required::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + HASH properties; + bool res= true; + int curr_level= curr_je.stack_p; + List <char> malloc_mem_list; + List_iterator<st_dependent_keywords> it(dependent_required); + st_dependent_keywords *curr_keyword= NULL; + + if (curr_je.value_type != JSON_VALUE_OBJECT) + return false; + + if (my_hash_init(PSI_INSTRUMENT_ME, &properties, + curr_je.s.cs, 1024, 0, 0, (my_hash_get_key) get_key_name, + NULL, 0)) + return true; + + while (json_scan_next(&curr_je)== 0 && curr_je.stack_p >= curr_level) + { + switch (curr_je.state) + { + case JST_KEY: + { + const uchar *key_end, *key_start; + int key_len; + char *str; + + key_start= curr_je.s.c_str; + do + { + key_end= curr_je.s.c_str; + } while (json_read_keyname_chr(&curr_je) == 0); + + key_len= (int)(key_end-key_start); + str= (char*)malloc((size_t)(key_len)+1); + strncpy(str, (const char*)key_start, key_len); + str[(int)(key_end-key_start)]='\0'; + + if (my_hash_insert(&properties, (const uchar*)str)) + goto error; + malloc_mem_list.push_back(str); + } + } + } + while ((curr_keyword= it++)) + { + if (my_hash_search(&properties, + (const uchar*)curr_keyword->property->ptr(), + curr_keyword->property->length())) + { + List_iterator<String> it2(curr_keyword->dependents); + String *curr_depended_keyword; + while ((curr_depended_keyword= it2++)) + { + if (!my_hash_search(&properties, + (const uchar*)curr_depended_keyword->ptr(), + curr_depended_keyword->length())) + { + goto error; + } + } + } + } + res= false; + + error: + my_hash_free(&properties); + if (!malloc_mem_list.is_empty()) + { + List_iterator<char> it(malloc_mem_list); + char *curr_ptr; + while ((curr_ptr= it++)) + free(curr_ptr); + malloc_mem_list.empty(); + } + return res; +} + +bool Json_schema_dependent_required::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type == JSON_VALUE_OBJECT) + { + int level1= je->stack_p; + while (json_scan_next(je)==0 && level1 <= je->stack_p) + { + switch(je->state) + { + case JST_KEY: + { + const uchar *k_end, *k_start; + int k_len; + + k_start= je->s.c_str; + do + { + k_end= je->s.c_str; + } while (json_read_keyname_chr(je) == 0); + + k_len= (int)(k_end-k_start); + if (json_read_value(je)) + return true; + + if (je->value_type == JSON_VALUE_ARRAY) + { + st_dependent_keywords *curr_dependent_keywords= + (st_dependent_keywords *) alloc_root(thd->mem_root, + sizeof(st_dependent_keywords)); + + if (curr_dependent_keywords) + { + curr_dependent_keywords->property= + new (thd->mem_root)String((char*)k_start, + k_len, je->s.cs); + curr_dependent_keywords->dependents.empty(); + int level2= je->stack_p; + while (json_scan_next(je)==0 && level2 <= je->stack_p) + { + if (json_read_value(je) || je->value_type != JSON_VALUE_STRING) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), + "dependentRequired"); + return true; + } + else + { + String *str= + new (thd->mem_root)String((char*)je->value, + je->value_len, je->s.cs); + curr_dependent_keywords->dependents.push_back(str, thd->mem_root); + } + } + dependent_required.push_back(curr_dependent_keywords, thd->mem_root); + } + else + { + return true; + } + } + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), + "dependentRequired"); + return true; + } + } + } + } + } + else + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "dependentRequired"); + return true; + } + return je->s.error ? true : false; +} + +bool Json_schema_property_names::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + int level= curr_je.stack_p; + + if (je->value_type != JSON_VALUE_OBJECT) + return false; + + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + switch(curr_je.state) + { + case JST_KEY: + { + const uchar *k_end, *k_start; + k_start= curr_je.s.c_str; + do + { + k_end= curr_je.s.c_str; + } while (json_read_keyname_chr(&curr_je) == 0); + + if (json_read_value(&curr_je)) + return true; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + + List_iterator <Json_schema_keyword> it1 (property_names); + Json_schema_keyword *curr_schema= NULL; + while((curr_schema= it1++)) + { + if (curr_schema->validate(&curr_je, k_start, k_end)) + return true; + } + } + } + } + + return false; +} + +bool Json_schema_property_names::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "propertyNames"); + return true; + } + if (create_object_and_handle_keyword(thd, je, &property_names, + all_keywords)) + return true; + + return false; +} + +/* + additiona_items, additional_properties, unevaluated_items, + unevaluated_properties are all going to be schemas + (basically of object type). So they all can be handled + just like any other schema. +*/ +bool +Json_schema_additional_and_unevaluated::handle_keyword(THD *thd, + json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type == JSON_VALUE_FALSE) + { + set_allowed(false); + return false; + } + else if (je->value_type == JSON_VALUE_OBJECT) + { + return create_object_and_handle_keyword(thd, je, &schema_list, + all_keywords); + } + if (je->value_type != JSON_VALUE_TRUE) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), keyword_map->func_name.str); + return true; + } + return false; +} + +/* + Validating properties as an alternate shcema happens only when we have + logic keywords. Example we have allOf, and one key is not + validated against allOf but it is present in "properties" and validates + against it. Then the validation result should be true. So we would want that + key to be validated against "properties", with "properties" begin + alternate schema. +*/ +bool Json_schema_properties::validate_as_alternate(const json_engine_t *je, + const uchar* k_start, + const uchar* k_end) +{ + st_property *curr_property= NULL; + json_engine_t curr_je= *je; + if ((curr_property= + (st_property*)my_hash_search(&properties, + (const uchar*)k_start, + (size_t)(k_end-k_start)))) + { + if (validate_schema_items(&curr_je, curr_property->curr_schema)) + return true; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + else + { + if (alternate_schema && alternate_schema->validate_as_alternate(je, k_start, k_end)) + { + return true; + } + } + return false; +} + +bool +Json_schema_additional_and_unevaluated:: + validate_as_alternate(const json_engine_t *je, + const uchar* k_start, + const uchar* k_end) +{ + if (!allowed) + return true; + return validate_schema_items(je, &schema_list); +} + + +/* + Makes sense on its own, without existence of additionalProperties, + properties, patternProperties. +*/ +bool Json_schema_unevaluated_properties::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + int level= curr_je.stack_p, count= 0; + bool has_false= false; + + if (je->value_type != JSON_VALUE_OBJECT) + return false; + + if (!allowed) + has_false= true; + + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + if (json_read_value(&curr_je)) + return true; + count++; + if (validate_schema_items(&curr_je, &schema_list)) + return true; + } + return has_false ? (!count ? false: true) : false; +} + +/* + Unlike additionalItems, additionalProperties makes sense on its own + without existence of properties and patternProperties, +*/ +bool Json_schema_additional_properties::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + int level= curr_je.stack_p; + + if (je->value_type != JSON_VALUE_OBJECT) + return false; + + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + switch(curr_je.state) + { + case JST_KEY: + if (json_read_value(&curr_je)) + return true; + if (validate_schema_items(&curr_je, &schema_list)) + return true; + } + } + + return false; +} + +/* + When items/prefix Items is present and if a key is not validated + against it, then additionalItems is validated as an "alternate validation". + It will be present/linked as alternate keyword and will not be present + in the schema list for that level. This function called when + "items"/"prefixItems" is absent, i.e when additionalItems appears + in the schema list for that level. + So additional Properties on its own will not make sense. +*/ +bool Json_schema_additional_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + return false; +} +bool Json_schema_unevaluated_items::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + /* + Makes sense on its own without adjacent keywords. + */ + int level= je->stack_p, count=0; + bool is_false= false; + json_engine_t curr_je= *je; + + if (je->value_type != JSON_VALUE_ARRAY) + return false; + + if (!allowed) + is_false= true; + + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + if (json_read_value(&curr_je)) + return true; + count++; + if (validate_schema_items(&curr_je, &schema_list)) + return true; + } + + return is_false ? (!count ? false : true) : false; +} + +bool Json_schema_properties::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + + if (curr_je.value_type != JSON_VALUE_OBJECT) + return false; + + int level= curr_je.stack_p; + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + switch(curr_je.state) + { + case JST_KEY: + { + const uchar *k_end, *k_start= curr_je.s.c_str; + do + { + k_end= curr_je.s.c_str; + } while (json_read_keyname_chr(&curr_je) == 0); + + if (json_read_value(&curr_je)) + return true; + + st_property *curr_property= NULL; + if ((curr_property= + (st_property*)my_hash_search(&properties, + (const uchar*)k_start, + (size_t)(k_end-k_start)))) + { + if (validate_schema_items(&curr_je, curr_property->curr_schema)) + return true; + } + else + { + if (fall_back_on_alternate_schema(&curr_je, k_start, k_end)) + return true; + } + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + } + } + + return false; +} + +bool Json_schema_properties::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "properties"); + return true; + } + + if (my_hash_init(PSI_INSTRUMENT_ME, + &this->properties, + je->s.cs, 1024, 0, 0, + (my_hash_get_key) get_key_name_for_property, + NULL, 0)) + return true; + is_hash_inited= true; + + int level= je->stack_p; + while (json_scan_next(je)==0 && level <= je->stack_p) + { + switch(je->state) + { + case JST_KEY: + { + const uchar *k_end, *k_start= je->s.c_str; + do + { + k_end= je->s.c_str; + } while (json_read_keyname_chr(je) == 0); + + if (json_read_value(je)) + return true; + + st_property *curr_property= + (st_property*)alloc_root(thd->mem_root, + sizeof(st_property)); + if (curr_property) + { + curr_property->key_name= (char*)alloc_root(thd->mem_root, + (size_t)(k_end-k_start)+1); + curr_property->curr_schema= + new (thd->mem_root) List<Json_schema_keyword>; + if (curr_property->key_name) + { + curr_property->key_name[(int)(k_end-k_start)]= '\0'; + strncpy((char*)curr_property->key_name, (const char*)k_start, + (size_t)(k_end-k_start)); + if (create_object_and_handle_keyword(thd, je, + curr_property->curr_schema, + all_keywords)) + return true; + if (my_hash_insert(&properties, (const uchar*)curr_property)) + return true; + } + } + } + } + } + return je->s.error ? true : false; +} + +bool Json_schema_pattern_properties:: + validate_as_alternate(const json_engine_t *curr_je, + const uchar *k_start, + const uchar* k_end) +{ + bool match_found= false; + List_iterator <st_pattern_to_property> it1 (pattern_properties); + st_pattern_to_property *curr_pattern_property= NULL; + + str->str_value.set_or_copy_aligned((const char*)k_start, + (size_t)(k_end-k_start), curr_je->s.cs); + + while ((curr_pattern_property= it1++)) + { + if (curr_pattern_property->re.recompile(curr_pattern_property->pattern)) + return true; + if (curr_pattern_property->re.exec(str, 0, 0)) + return true; + if (curr_pattern_property->re.match()) + { + match_found= true; + if (validate_schema_items(curr_je, curr_pattern_property->curr_schema)) + return true; + break; + } + } + if (!match_found) + { + if (fall_back_on_alternate_schema(curr_je)) + return true; + } + return false; +} + + +bool Json_schema_pattern_properties::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + json_engine_t curr_je= *je; + int level= je->stack_p; + bool match_found= false; + + if (je->value_type != JSON_VALUE_OBJECT) + return false; + + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + switch(curr_je.state) + { + case JST_KEY: + { + const uchar *k_end, *k_start= curr_je.s.c_str; + do + { + k_end= curr_je.s.c_str; + } while (json_read_keyname_chr(&curr_je) == 0); + + str->str_value.set_or_copy_aligned((const char*)k_start, + (size_t)(k_end-k_start), curr_je.s.cs); + + if (json_read_value(&curr_je)) + return true; + + List_iterator <st_pattern_to_property> it1 (pattern_properties); + st_pattern_to_property *curr_pattern_property= NULL; + + while ((curr_pattern_property= it1++)) + { + if (curr_pattern_property->re.recompile(curr_pattern_property->pattern)) + return true; + if (curr_pattern_property->re.exec(str, 0, 0)) + return true; + if (curr_pattern_property->re.match()) + { + match_found= true; + if (validate_schema_items(&curr_je, curr_pattern_property->curr_schema)) + return true; + } + } + if (!match_found) + { + if (fall_back_on_alternate_schema(&curr_je, k_start, k_end)) + return true; + } + } + } + } + return false; +} + + + +bool Json_schema_pattern_properties::handle_keyword(THD *thd, + json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "patternProperties"); + return true; + } + + str= new (thd->mem_root) Item_string(thd, "", (uint) 0, je->s.cs); + + int level= je->stack_p; + while (json_scan_next(je)==0 && level <= je->stack_p) + { + switch(je->state) + { + case JST_KEY: + { + const uchar *k_end, *k_start= je->s.c_str; + do + { + k_end= je->s.c_str; + } while (json_read_keyname_chr(je) == 0); + + if (json_read_value(je)) + return true; + + st_pattern_to_property *curr_pattern_to_property= NULL; + + curr_pattern_to_property= new (thd->mem_root) pattern_to_property(); + if (curr_pattern_to_property) + { + my_repertoire_t repertoire= my_charset_repertoire(je->s.cs); + curr_pattern_to_property->pattern= + thd->make_string_literal((const char*)k_start, + (size_t)(k_end-k_start), + repertoire); + curr_pattern_to_property->re.init(je->s.cs, 0); + curr_pattern_to_property->re.unset_flag(PCRE2_CASELESS); + curr_pattern_to_property->curr_schema= + new (thd->mem_root) List<Json_schema_keyword>; + + if (curr_pattern_to_property->curr_schema) + { + if (create_object_and_handle_keyword(thd, je, + curr_pattern_to_property->curr_schema, + all_keywords)) + return true; + } + + pattern_properties.push_back(curr_pattern_to_property, thd->mem_root); + } + } + } + } + return false; +} + +bool Json_schema_logic::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_ARRAY) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), keyword_map->func_name.str); + return true; + } + + int level= je->stack_p; + while(json_scan_next(je)==0 && je->stack_p >= level) + { + json_engine_t temp_je; + char *begin, *end; + int len; + + if (json_read_value(je)) + return true; + begin= (char*)je->value; + + if (json_skip_level(je)) + return true; + + end= (char*)je->s.c_str; + len= (int)(end-begin); + + json_scan_start(&temp_je, je->s.cs, (const uchar *) begin, + (const uchar *)begin+len); + List<Json_schema_keyword> *keyword_list= + new (thd->mem_root) List<Json_schema_keyword>; + + if (!keyword_list) + return true; + if (create_object_and_handle_keyword(thd, &temp_je, keyword_list, + all_keywords)) + return true; + + schema_items.push_back(keyword_list, thd->mem_root); + } + + return false; +} + +bool Json_schema_logic::check_validation(const json_engine_t *je, + const uchar *k_start, + const uchar *k_end) +{ + List_iterator <List<Json_schema_keyword>> it1 (schema_items); + List<Json_schema_keyword> *curr_schema= NULL; + Json_schema_keyword *curr_alternate_schema= NULL; + uint count_validations= 0; + bool validated= true; + + if (je->value_type == JSON_VALUE_ARRAY) + curr_alternate_schema= alternate_choice1; + else if (je->value_type == JSON_VALUE_OBJECT) + curr_alternate_schema= alternate_choice2; + + while ((curr_schema= it1++)) + { + List_iterator<Json_schema_keyword> it2(*curr_schema); + Json_schema_keyword *curr_keyword= NULL; + validated= true; + + while ((curr_keyword=it2++)) + { + if (!curr_keyword->alternate_schema) + curr_keyword->alternate_schema= curr_alternate_schema; + if (curr_keyword->validate(je)) + { + validated= false; + break; + } + } + if (validated) + { + count_validations++; + if (logic_flag & HAS_NOT) + return true; + } + } + + if (validate_count(&count_validations, &schema_items.elements)) + return true; + + return false; +} +bool Json_schema_logic::validate(const json_engine_t *je, + const uchar *k_start, + const uchar* k_end) +{ + return check_validation(je, k_start, k_end); +} + +bool Json_schema_not::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + bool res= false; + + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), keyword_map->func_name.str); + return true; + } + + res= create_object_and_handle_keyword(thd, je, &schema_list, all_keywords); + + schema_items.push_back(&schema_list, thd->mem_root); + + return res; +} + + +bool Json_schema_keyword::validate_schema_items(const json_engine_t *je, + List<Json_schema_keyword> + *schema_items) +{ + json_engine_t curr_je= *je; + List_iterator<Json_schema_keyword> it1(*schema_items); + Json_schema_keyword *curr_schema= NULL; + + while((curr_schema= it1++)) + { + if (curr_schema->validate(&curr_je)) + return true; + } + + return false; +} + +bool Json_schema_conditional::validate(const json_engine_t *je, + const uchar *k_start, + const uchar *k_end) +{ + if (if_cond) + { + if (!if_cond->validate_schema_items(je, + if_cond->get_validation_keywords())) + { + if (then_cond) + { + if (then_cond->validate_schema_items(je, + then_cond->get_validation_keywords())) + return true; + } + } + else + { + if (else_cond) + { + if (else_cond->validate_schema_items(je, + else_cond->get_validation_keywords())) + return true; + } + } + } + return false; +} + + +bool Json_schema_conditional::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), keyword_map->func_name.str); + return true; + } + return create_object_and_handle_keyword(thd, je, &conditions_schema, + all_keywords); +} + +bool Json_schema_dependent_schemas::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_OBJECT) + { + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "properties"); + return true; + } + + if (my_hash_init(PSI_INSTRUMENT_ME, + &this->properties, + je->s.cs, 1024, 0, 0, + (my_hash_get_key) get_key_name_for_property, + NULL, 0)) + return true; + is_hash_inited= true; + + int level= je->stack_p; + while (json_scan_next(je)==0 && level <= je->stack_p) + { + switch(je->state) + { + case JST_KEY: + { + const uchar *k_end, *k_start= je->s.c_str; + do + { + k_end= je->s.c_str; + } while (json_read_keyname_chr(je) == 0); + + if (json_read_value(je)) + return true; + + st_property *curr_property= + (st_property*)alloc_root(thd->mem_root, + sizeof(st_property)); + if (curr_property) + { + curr_property->key_name= (char*)alloc_root(thd->mem_root, + (size_t)(k_end-k_start)+1); + curr_property->curr_schema= + new (thd->mem_root) List<Json_schema_keyword>; + if (curr_property->key_name) + { + curr_property->key_name[(int)(k_end-k_start)]= '\0'; + strncpy((char*)curr_property->key_name, (const char*)k_start, + (size_t)(k_end-k_start)); + if (create_object_and_handle_keyword(thd, je, + curr_property->curr_schema, + all_keywords)) + return true; + if (my_hash_insert(&properties, (const uchar*)curr_property)) + return true; + } + } + } + } + } + return false; +} + +bool Json_schema_dependent_schemas::validate(const json_engine_t *je, + const uchar *k_start, + const uchar *k_end) +{ + json_engine_t curr_je= *je; + + if (curr_je.value_type != JSON_VALUE_OBJECT) + return false; + + int level= curr_je.stack_p; + while (json_scan_next(&curr_je)==0 && level <= curr_je.stack_p) + { + switch(curr_je.state) + { + case JST_KEY: + { + const uchar *k_end, *k_start= curr_je.s.c_str; + do + { + k_end= curr_je.s.c_str; + } while (json_read_keyname_chr(&curr_je) == 0); + + if (json_read_value(&curr_je)) + return true; + + st_property *curr_property= NULL; + if ((curr_property= + (st_property*)my_hash_search(&properties, + (const uchar*)k_start, + (size_t)(k_end-k_start)))) + { + if (validate_schema_items(je, curr_property->curr_schema)) + return true; + if (!json_value_scalar(&curr_je)) + { + if (json_skip_level(&curr_je)) + return true; + } + } + } + } + } + + return false; +} + +bool Json_schema_media_string::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + if (je->value_type != JSON_VALUE_STRING) + { + String curr_keyword((char*)key_start, key_end-key_start, je->s.cs); + my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), curr_keyword.ptr()); + return true; + } + + return false; +} + +bool Json_schema_reference::handle_keyword(THD *thd, json_engine_t *je, + const char* key_start, + const char* key_end, + List<Json_schema_keyword> + *all_keywords) +{ + String keyword(0); + keyword.append((const char*)key_start, (int)(key_end-key_start)); + my_error(ER_JSON_SCHEMA_KEYWORD_UNSUPPORTED, MYF(0), keyword.ptr()); + return true; +} + +Json_schema_keyword* create_object(THD *thd, + Json_schema_keyword *curr_keyword, + const uchar* key_start, + const uchar* key_end) +{ + st_json_schema_keyword_map *curr_keyword_map= NULL; + curr_keyword_map= + (st_json_schema_keyword_map*) + my_hash_search(&all_keywords_hash, + key_start, + (size_t)(key_end-key_start)); + if (!curr_keyword_map) + { + curr_keyword_map= &empty_func_map; + } + curr_keyword= (curr_keyword_map->func)(thd); + curr_keyword->keyword_map= curr_keyword_map; + + return curr_keyword; +} + +static int sort_by_priority(Json_schema_keyword* el1, Json_schema_keyword* el2, + void *arg) +{ + return el1->priority > el2->priority; +} + +void fix_keyword_list(List <Json_schema_keyword> *keyword_list) +{ + bubble_sort<Json_schema_keyword>(keyword_list, sort_by_priority, NULL); + + if (keyword_list && !keyword_list->is_empty()) + { + int count= 1; + + List_iterator<Json_schema_keyword> it2(*keyword_list); + Json_schema_keyword *curr_schema= NULL; + + while((curr_schema=it2++)) + { + curr_schema->set_alternate_schema(keyword_list->elem(count)); + count++; + } + } + return; +} + +/* + Some schemas are interdependent: they are evaluated only if their + adjacent schemas fail to evaluate. So the need to be linked + in a way that if one fails to evaluate a value, we can try + an alternate schema. + Hence push such keywords in a temporary list, adjust the interdependence + and then add them to main schema list. +*/ +bool +add_schema_interdependence(THD *thd, List<Json_schema_keyword> *temporary, + List<Json_schema_keyword> *keyword_list) +{ + List_iterator<Json_schema_keyword> temp_it(*temporary); + List<Json_schema_keyword> array_prop, object_prop, logic_prop, conditional_prop; + Json_schema_keyword *temp_keyword= NULL, *contains= NULL, + *max_contains= NULL, *min_contains= NULL, + *if_cond= NULL, *then_cond= NULL, *else_cond= NULL; + + while((temp_keyword= temp_it++)) + { + size_t len= strlen(temp_keyword->keyword_map->func_name.str); + st_json_schema_keyword_map *curr_element= NULL; + if ((curr_element= (st_json_schema_keyword_map*) my_hash_search(&all_keywords_hash, + (uchar*)(temp_keyword->keyword_map->func_name.str), len))) + { + if (temp_keyword->priority > 0) + { + if (curr_element->flag == JSON_SCHEMA_ARRAY_KEYWORD) + array_prop.push_back(temp_keyword); + else if (curr_element->flag == JSON_SCHEMA_OBJECT_KEYWORD) + object_prop.push_back(temp_keyword); + else if (curr_element->flag == JSON_SCHEMA_LOGIC_KEYWORD) + logic_prop.push_back(temp_keyword); + } + else if (temp_keyword->keyword_map == &(json_schema_func_array[35])) + if_cond= temp_keyword; + else if (temp_keyword->keyword_map == &(json_schema_func_array[36])) + then_cond= temp_keyword; + else if (temp_keyword->keyword_map == &(json_schema_func_array[37])) + else_cond= temp_keyword; + else if (temp_keyword->keyword_map == &(json_schema_func_array[18])) + contains= temp_keyword; + else if (temp_keyword->keyword_map == &(json_schema_func_array[20])) + min_contains= temp_keyword; + else if (temp_keyword->keyword_map == &(json_schema_func_array[19])) + max_contains= temp_keyword; + else + keyword_list->push_back(temp_keyword, thd->mem_root); + } + } + + if (if_cond) + { + Json_schema_conditional *cond_schema= + new (current_thd->mem_root) Json_schema_conditional(); + if (cond_schema) + cond_schema->set_conditions(if_cond, then_cond, else_cond); + keyword_list->push_back(cond_schema, thd->mem_root); + } + if (contains) + { + contains->set_dependents(min_contains, max_contains); + keyword_list->push_back(contains, thd->mem_root); + } + + fix_keyword_list(&array_prop); + fix_keyword_list(&object_prop); + + /* + We want to check for alternate schema + When a key is not validated by logic keywords, we would want to also check + schema like properties, items etc to make sure the key is not validated by + any schema in order to return correct result. So "link" other schemas as + alternate when logic properties is present and only push logic keywords + to the schema list. + */ + if (!logic_prop.is_empty()) + { + List_iterator<Json_schema_keyword> it(logic_prop); + Json_schema_keyword *curr_schema= NULL; + while((curr_schema= it++)) + { + curr_schema->set_alternate_schema_choice(array_prop.elem(0), + object_prop.elem(0)); + keyword_list->push_back(curr_schema, thd->mem_root); + } + array_prop.empty(); + object_prop.empty(); + } + else + { + if (array_prop.elem(0)) + keyword_list->push_back(array_prop.elem(0), thd->mem_root); + if (object_prop.elem(0)) + keyword_list->push_back(object_prop.elem(0), thd->mem_root); + } + return false; +} + + +/* + Scan all keywords on the current level and put them in a temporary + list. Once scanning is done, adjust the dependency if needed, and + add the keywords in keyword_list +*/ +bool create_object_and_handle_keyword(THD *thd, json_engine_t *je, + List<Json_schema_keyword> *keyword_list, + List<Json_schema_keyword> *all_keywords) +{ + int level= je->stack_p; + List<Json_schema_keyword> temporary_list; + + DBUG_EXECUTE_IF("json_check_min_stack_requirement", + { + long arbitrary_var; + long stack_used_up= + (available_stack_size(thd->thread_stack, + &arbitrary_var)); + ALLOCATE_MEM_ON_STACK(my_thread_stack_size-stack_used_up-STACK_MIN_SIZE); + }); + if (check_stack_overrun(thd, STACK_MIN_SIZE , NULL)) + return 1; + + while (json_scan_next(je)== 0 && je->stack_p >= level) + { + switch(je->state) + { + case JST_KEY: + { + const uchar *key_end, *key_start; + + key_start= je->s.c_str; + do + { + key_end= je->s.c_str; + } while (json_read_keyname_chr(je) == 0); + + if (json_read_value(je)) + return true; + + Json_schema_keyword *curr_keyword= NULL; + curr_keyword= create_object(thd, curr_keyword, + key_start, key_end); + if (all_keywords) + all_keywords->push_back(curr_keyword, thd->mem_root); + if (curr_keyword->handle_keyword(thd, je, + (const char*)key_start, + (const char*)key_end, all_keywords)) + { + return true; + } + temporary_list.push_back(curr_keyword, thd->mem_root); + break; + } + } + } + + if (add_schema_interdependence(thd, &temporary_list, keyword_list)) + return true; + + return je->s.error ? true : false; +} + +uchar* get_key_name_for_property(const char *key_name, size_t *length, + my_bool /* unused */) +{ + st_property * curr_property= (st_property*)(key_name); + + *length= strlen(curr_property->key_name); + return (uchar*) curr_property->key_name; +} + +uchar* get_key_name_for_func(const char *key_name, size_t *length, + my_bool /* unused */) +{ + st_json_schema_keyword_map * curr_keyword= + (st_json_schema_keyword_map*)(key_name); + + *length= curr_keyword->func_name.length; + return (uchar*)curr_keyword->func_name.str; +} + +bool setup_json_schema_keyword_hash() +{ + if (my_hash_init(PSI_INSTRUMENT_ME, + &all_keywords_hash, + system_charset_info, 1024, 0, 0, + (my_hash_get_key) get_key_name_for_func, + NULL, 0)) + return true; + + int size= sizeof(json_schema_func_array)/sizeof(json_schema_func_array[0]); + for (int i= 0; i < size; i++) + { + if (my_hash_insert(&all_keywords_hash, (uchar*)(&json_schema_func_array[i]))) + return true; + } + return false; +} + +void cleanup_json_schema_keyword_hash() +{ + my_hash_free(&all_keywords_hash); + + return; +} |