MySQL 9.7.0
Source Code Documentation
json_schema.cc File Reference
#include "sql-common/json_schema.h"
#include "my_rapidjson_size_t.h"
#include <rapidjson/document.h>
#include <rapidjson/error/error.h>
#include <rapidjson/memorystream.h>
#include <rapidjson/pointer.h>
#include <rapidjson/reader.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <string>
#include <unordered_set>
#include <utility>
#include "my_alloc.h"
#include "my_inttypes.h"
#include "my_sys.h"
#include "sql-common/json_syntax_check.h"

Classes

class  Json_schema_validator_impl
 Json_schema_validator_impl is an object that contains a JSON Schema that can be re-used multiple times. More...
 
class  Json_schema_validator_impl::My_remote_schema_document_provider
 This object acts as a handler/callback for the JSON schema validator and is called whenever a schema reference is encountered in the JSON document. More...
 

Functions

static bool is_local_json_pointer_ref (const rapidjson::Value &ref_value)
 Check whether a $ref value is a local JSON Pointer reference. More...
 
static const rapidjson::Value * resolve_local_json_pointer_ref (const rapidjson::Document &schema_document, const rapidjson::Value &ref_value)
 Resolve a local JSON Pointer $ref against the schema document. More...
 
static bool json_schema_exceeds_expansion_depth (const rapidjson::Document &schema_document, const rapidjson::Value *node, uint32_t depth_so_far, uint32_t limit, std::unordered_set< const rapidjson::Value * > *visiting)
 Check whether any path from a schema node exceeds the expansion depth limit. More...
 
static bool check_json_schema_expansion_depth (const rapidjson::Document &schema_document, const JsonErrorHandler &depth_handler)
 Validate that a JSON Schema does not require excessive recursion depth during rapidjson schema compilation. More...
 
static bool parse_json_schema (const char *json_schema_str, size_t json_schema_length, const JsonSchemaErrorHandler &error_handler, const JsonErrorHandler &depth_handler, rapidjson::Document *schema_document)
 parse_json_schema will parse a JSON input into a JSON Schema. More...
 
bool is_valid_json_schema (const char *document_str, size_t document_length, const char *json_schema_str, size_t json_schema_length, const JsonSchemaErrorHandler &error_handler, const JsonErrorHandler &depth_handler, bool *is_valid, Json_schema_validation_report *validation_report)
 This function will validate a JSON document against a JSON Schema using the validation provided by rapidjson. More...
 

Variables

static constexpr uint32_t JSON_SCHEMA_MAX_EXPANSION_DEPTH = 100
 Maximum "expansion depth" of a JSON Schema during rapidjson schema compilation. More...
 

Function Documentation

◆ check_json_schema_expansion_depth()

static bool check_json_schema_expansion_depth ( const rapidjson::Document &  schema_document,
const JsonErrorHandler depth_handler 
)
static

Validate that a JSON Schema does not require excessive recursion depth during rapidjson schema compilation.

rapidjson schema compilation expands local $ref by recursively compiling the referenced subschemas. The recursion depth is therefore related to the number of local reference "hops" along the deepest expansion path. This depth can be large even if no referred object has a $ref at its own top level, as long as each referred object contains a nested object with a $ref.

This function only checks whether any expansion path exceeds the limit. Each step into a nested object/array and each local $ref expansion counts as 1.

It does this by walking JSON-tree edges (object members / array elements) and local $ref edges (JSON pointer references starting with #), with early exit as soon as the limit is exceeded.

If the limit is exceeded (JSON_SCHEMA_MAX_EXPANSION_DEPTH), depth_handler() is called and the schema is rejected.

Parameters
schema_documentParsed JSON schema document.
depth_handlerError handler invoked when the limit is exceeded.
Return values
trueThe schema exceeded the limit and an error was reported.
falseNo expansion path exceeds the limit.

◆ is_local_json_pointer_ref()

static bool is_local_json_pointer_ref ( const rapidjson::Value &  ref_value)
static

Check whether a $ref value is a local JSON Pointer reference.

We only guard references of the form "#" or "#/...".

Parameters
ref_valueThe value of the $ref member.
Return values
trueThe $ref is a local JSON pointer reference.
falseOtherwise.

◆ is_valid_json_schema()

bool is_valid_json_schema ( const char *  document_str,
size_t  document_length,
const char *  json_schema_str,
size_t  json_schema_length,
const JsonSchemaErrorHandler error_handler,
const JsonErrorHandler depth_handler,
bool *  is_valid,
Json_schema_validation_report report 
)

This function will validate a JSON document against a JSON Schema using the validation provided by rapidjson.

Parameters
document_strA pointer to the JSON document to be validated.
document_lengthThe length of the JSON document to be validated.
json_schema_strA pointer to the JSON Schema.
json_schema_lengthThe length of the JSON Schema.
error_handlerError handlers to be called when parsing errors occur.
depth_handlerPointer to a function that should handle error occurred when depth is exceeded.
[out]is_validA variable containing the result of the validation. If true, the JSON document is valid according to the given JSON Schema.
[out]reportA structure containing a detailed report from the validation. Is only populated if is_valid is set to "false". Can be nullptr if a detailed report isn't needed.
Return values
trueif anything went wrong (like parsing the JSON inputs). my_error has been called with an appropriate error message.
falseif the validation succeeded. The result of the validation can be found in the output variable "is_valid".

◆ json_schema_exceeds_expansion_depth()

static bool json_schema_exceeds_expansion_depth ( const rapidjson::Document &  schema_document,
const rapidjson::Value *  node,
uint32_t  depth_so_far,
uint32_t  limit,
std::unordered_set< const rapidjson::Value * > *  visiting 
)
static

Check whether any path from a schema node exceeds the expansion depth limit.

The expansion depth is the number of edges along a path in the schema's expansion graph:

  • JSON-tree edges (object members / array elements), and
  • local $ref edges (local JSON Pointer references).

visiting is used to break $ref cycles (cycles are treated as non-contributing; rapidjson will report cyclic references separately).

Parameters
schema_documentParsed JSON schema document (used for resolving $ref).
nodeThe node to start the search from.
depth_so_farCurrent depth from the schema root.
limitMaximum allowed expansion depth.
[in,out]visitingVisitation set for cycle detection.
Return values
trueThe limit is exceeded along some path.
falseNo path exceeds the limit.

◆ parse_json_schema()

static bool parse_json_schema ( const char *  json_schema_str,
size_t  json_schema_length,
const JsonSchemaErrorHandler error_handler,
const JsonErrorHandler depth_handler,
rapidjson::Document *  schema_document 
)
static

parse_json_schema will parse a JSON input into a JSON Schema.

If the input isn't a valid JSON, or if the JSON is too deeply nested, an error will be returned to the user.

Parameters
json_schema_strA pointer to the JSON Schema input
json_schema_lengthThe length of the JSON Schema input
error_handlerError handlers to be called when parsing errors occur.
depth_handlerPointer to a function that should handle error occurred when depth is exceeded.
[out]schema_documentAn object where the JSON Schema will be put. This variable MUST be initialized.
Return values
trueon error (my_error has been called)
falseon success. The JSON Schema can be found in the output parameter schema_document.

◆ resolve_local_json_pointer_ref()

static const rapidjson::Value * resolve_local_json_pointer_ref ( const rapidjson::Document &  schema_document,
const rapidjson::Value &  ref_value 
)
static

Resolve a local JSON Pointer $ref against the schema document.

Parameters
schema_documentThe full JSON schema document.
ref_valueThe $ref member value (must be a local JSON pointer).
Returns
Pointer to the referenced schema node on success, otherwise nullptr.

Variable Documentation

◆ JSON_SCHEMA_MAX_EXPANSION_DEPTH

constexpr uint32_t JSON_SCHEMA_MAX_EXPANSION_DEPTH = 100
staticconstexpr

Maximum "expansion depth" of a JSON Schema during rapidjson schema compilation.

rapidjson schema compilation is recursive. The call stack depth is driven by a combination of:

  • JSON nesting within the schema (object members / array elements), and
  • local $ref expansion (compiling referenced subschemas recursively).

This cap bounds the maximum number of edges along any path in the schema's "expansion graph", where both JSON-tree edges and local $ref edges count as 1.

Since MySQL rejects JSON documents deeper than 100 levels (see sql-common/json_syntax_check.cc), schemas requiring more than 100 levels of expansion cannot describe JSON documents supported by MySQL. We therefore use 100 as the single guard limit here as well.