2022-10-28 09:33:13 +02:00
|
|
|
/* 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"
|
2023-04-04 10:05:02 +02:00
|
|
|
#include "pcre2.h"
|
2022-10-28 09:33:13 +02:00
|
|
|
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);
|
|
|
|
}
|
2023-03-02 13:20:19 +01:00
|
|
|
return je->s.error ? true : false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
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);
|
2023-04-17 14:21:34 +02:00
|
|
|
int err= 0;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2023-03-02 14:39:45 +01:00
|
|
|
if (!(enum_scalar & (1 << temp_je.value_type)))
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
2023-03-02 14:39:45 +01:00
|
|
|
int count= 0;
|
2022-10-28 09:33:13 +02:00
|
|
|
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;
|
2023-03-02 14:39:45 +01:00
|
|
|
count++;
|
2022-10-28 09:33:13 +02:00
|
|
|
if (je->value_type > JSON_VALUE_NUMBER)
|
|
|
|
{
|
2023-03-02 14:39:45 +01:00
|
|
|
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;
|
|
|
|
}
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
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());
|
2023-03-02 14:39:45 +01:00
|
|
|
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;
|
|
|
|
}
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-02 14:39:45 +01:00
|
|
|
if (!count)
|
|
|
|
{
|
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "enum");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
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;
|
2023-03-03 09:20:46 +01:00
|
|
|
if (je->num_flags & JSON_NUM_FRAC_PART)
|
|
|
|
return true;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
2023-03-03 09:20:46 +01:00
|
|
|
longlong val= je->s.cs->strntoll((char *) je->value,
|
|
|
|
je->value_len, 10, &end, &err);
|
2022-10-28 09:33:13 +02:00
|
|
|
|
2023-03-03 09:20:46 +01:00
|
|
|
return val % multiple_of;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2023-03-03 09:20:46 +01:00
|
|
|
if (je->value_type != JSON_VALUE_NUMBER || (je->num_flags & JSON_NUM_FRAC_PART))
|
2022-10-28 09:33:13 +02:00
|
|
|
{
|
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "multipleOf");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-03 09:20:46 +01:00
|
|
|
longlong val= je->s.cs->strntoll((char *) je->value,
|
|
|
|
je->value_len, 10, &end, &err);
|
2023-03-02 12:49:36 +01:00
|
|
|
if (val <= 0)
|
2022-10-28 09:33:13 +02:00
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "multipleOf");
|
2023-03-02 12:49:36 +01:00
|
|
|
multiple_of= val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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)
|
2023-03-02 12:49:36 +01:00
|
|
|
{
|
2022-10-28 09:33:13 +02:00
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxLength");
|
2023-03-02 12:49:36 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
value= (int)val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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)
|
2023-03-06 13:30:04 +01:00
|
|
|
{
|
2022-10-28 09:33:13 +02:00
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minLength");
|
2023-03-02 12:49:36 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
value= (int)val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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);
|
2023-04-17 14:21:34 +02:00
|
|
|
str= new (thd->mem_root) Item_string(thd, "", (uint) 0, je->s.cs);
|
2022-10-28 09:33:13 +02:00
|
|
|
re.init(je->s.cs, 0);
|
2023-04-04 10:05:02 +02:00
|
|
|
re.unset_flag(PCRE2_CASELESS);
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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)
|
2023-03-02 12:49:36 +01:00
|
|
|
{
|
2022-10-28 09:33:13 +02:00
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxItems");
|
2023-03-02 12:49:36 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
value= (int)val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-03-02 12:49:36 +01:00
|
|
|
value= (int)val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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);
|
2023-03-06 13:30:04 +01:00
|
|
|
if (val < 0)
|
|
|
|
{
|
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "maxContains");
|
|
|
|
return true;
|
|
|
|
}
|
2022-10-28 09:33:13 +02:00
|
|
|
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;
|
2023-03-06 13:30:04 +01:00
|
|
|
if (val < 0)
|
|
|
|
{
|
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "minContains");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-28 09:33:13 +02:00
|
|
|
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;
|
|
|
|
|
2023-03-02 12:49:36 +01:00
|
|
|
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;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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;
|
2023-03-06 13:30:04 +01:00
|
|
|
int res= true, level= curr_je.stack_p, scalar_val= 0;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2023-03-06 13:30:04 +01:00
|
|
|
int err= 1;
|
2022-10-28 09:33:13 +02:00
|
|
|
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);
|
|
|
|
}
|
2023-03-06 13:30:04 +01:00
|
|
|
|
2022-10-28 09:33:13 +02:00
|
|
|
res= false;
|
2023-03-06 13:30:04 +01:00
|
|
|
|
2022-10-28 09:33:13 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-03-02 12:49:36 +01:00
|
|
|
value= (int)val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-03-02 12:49:36 +01:00
|
|
|
value= (int)val;
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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;
|
2023-03-06 13:30:04 +01:00
|
|
|
if (je->value_type != JSON_VALUE_STRING)
|
|
|
|
{
|
|
|
|
my_error(ER_JSON_INVALID_VALUE_FOR_KEYWORD, MYF(0), "required");
|
|
|
|
return true;
|
|
|
|
}
|
2022-10-28 09:33:13 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2023-03-02 13:20:19 +01:00
|
|
|
return je->s.error ? true : false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-03-02 13:20:19 +01:00
|
|
|
return je->s.error ? true : false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2023-04-04 11:09:41 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (alternate_schema && alternate_schema->validate_as_alternate(je, k_start, k_end))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2022-10-28 09:33:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
Json_schema_additional_and_unevaluated::
|
|
|
|
validate_as_alternate(const json_engine_t *je,
|
|
|
|
const uchar* k_start,
|
|
|
|
const uchar* k_end)
|
|
|
|
{
|
2023-04-04 11:09:41 +02:00
|
|
|
if (!allowed)
|
|
|
|
return true;
|
2022-10-28 09:33:13 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-02 13:20:19 +01:00
|
|
|
return je->s.error ? true : false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-04-17 14:21:34 +02:00
|
|
|
str= new (thd->mem_root) Item_string(thd, "", (uint) 0, je->s.cs);
|
2022-10-28 09:33:13 +02:00
|
|
|
|
|
|
|
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);
|
2023-04-04 10:05:02 +02:00
|
|
|
curr_pattern_to_property->re.unset_flag(PCRE2_CASELESS);
|
2022-10-28 09:33:13 +02:00
|
|
|
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;
|
|
|
|
|
2023-03-02 13:20:19 +01:00
|
|
|
return je->s.error ? true : false;
|
2022-10-28 09:33:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|