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

MDEV-25727 Add FORMAT JSON support to JSON_TABLE

Allow explicit FORMAT JSON columns in JSON_TABLE for supported
string, blob, and JSON targets.

Preserve the explicit clause in view printing and add regression
coverage for formatted extraction, unsupported targets, and SHOW
CREATE VIEW.

Signed-off-by: Varun Deep Saini <varun.23bcs10048@ms.sst.scaler.com>
Signed-off-by: Varun Deep Saini <deepsainivarun@gmail.com>
This commit is contained in:
Varun Deep Saini
2026-03-06 15:26:00 +05:30
committed by Daniel Black
parent f40ea8f4e6
commit c3ec2dc368
5 changed files with 193 additions and 6 deletions

View File

@@ -106,6 +106,86 @@ id jpath json_path jexst
3 NULL NULL 1
select * from
json_table(
'[{"name":"Jeans","sizes":[32,34,36],"details":{"fit":"regular"},"flag":true,"jsnull":null},
{"name":"T-Shirt","sizes":["Medium","Large"],"details":{"fit":"loose"},"flag":false,"jsnull":null},
{"name":"Cellphone"}]',
'$[*]' columns (name varchar(10) path '$.name',
sizes varchar(100) format json path '$.sizes',
details text format json path '$.details',
flag varchar(10) format json path '$.flag',
jsnull varchar(10) format json path '$.jsnull')
) as jt;
name sizes details flag jsnull
Jeans [32,34,36] {"fit":"regular"} true null
T-Shirt ["Medium","Large"] {"fit":"loose"} false null
Cellphone NULL NULL NULL NULL
select * from
json_table(
'[{"doc":[1,2]},{"doc":{"k":"v"}},{"doc":null},{}]',
'$[*]' columns (doc json format json path '$.doc' null on empty)
) as jt;
doc
[1,2]
{"k":"v"}
null
NULL
SELECT HEX(doc) FROM
json_table(
'[{"doc":[1,2]},{}]',
'$[*]' columns (doc blob format json path '$.doc' null on empty)
) as jt;
HEX(doc)
5B312C325D
NULL
select * from
json_table(
'[{"name":"Jeans","sizes":[32,34,36]},{"name":"Cellphone"}]',
'$[*]' columns (name varchar(10) path '$.name',
sizes varchar(100) format json path '$.sizes'
default '[99]' on empty)
) as jt;
name sizes
Jeans [32,34,36]
Cellphone [99]
select * from
json_table(
'[{"a":[1]},{"a":[2]}]',
'$' columns (a varchar(20) format json path '$[*].a' error on error)
) as jt;
ERROR HY000: Can't store multiple matches of the path in the column 'a' of JSON_TABLE 'jt'.
select * from
json_table(
'[{"a":[1,2,3]}]',
'$[*]' columns (a int format json path '$.a')
) as jt;
ERROR HY000: Incorrect usage of FORMAT JSON and non-string JSON_TABLE column
create view v_fmt as
select * from
json_table(
'[{"sizes":[1,2]}]',
'$[*]' columns (sizes varchar(20) format json path '$.sizes')
) as jt;
select * from v_fmt;
sizes
[1,2]
show create view v_fmt;
View Create View character_set_client collation_connection
v_fmt CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_fmt` AS select `jt`.`sizes` AS `sizes` from JSON_TABLE('[{"sizes":[1,2]}]', '$[*]' COLUMNS (`sizes` varchar(20) FORMAT JSON PATH '$.sizes')) `jt` latin1 latin1_swedish_ci
drop view v_fmt;
create view v_fmt_cs as
select * from
json_table(
'[{"sizes":[1,2]}]',
'$[*]' columns (
sizes varchar(20) character set utf8mb4 collate utf8mb4_bin
format json path '$.sizes')
) as jt;
show create view v_fmt_cs;
View Create View character_set_client collation_connection
v_fmt_cs CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_fmt_cs` AS select `jt`.`sizes` AS `sizes` from JSON_TABLE('[{"sizes":[1,2]}]', '$[*]' COLUMNS (`sizes` varchar(20) CHARSET utf8mb4 COLLATE utf8mb4_bin FORMAT JSON PATH '$.sizes')) `jt` latin1 latin1_swedish_ci
drop view v_fmt_cs;
select * from
json_table(
'[{"a":"3"},{"a":2},{"b":1},{"a":0}]',
'$[*]' columns (id for ordinality,
jpath varchar(100) path '$.a' error on empty,

View File

@@ -84,6 +84,73 @@ select * from
#eval $query;
#eval explain $query;
select * from
json_table(
'[{"name":"Jeans","sizes":[32,34,36],"details":{"fit":"regular"},"flag":true,"jsnull":null},
{"name":"T-Shirt","sizes":["Medium","Large"],"details":{"fit":"loose"},"flag":false,"jsnull":null},
{"name":"Cellphone"}]',
'$[*]' columns (name varchar(10) path '$.name',
sizes varchar(100) format json path '$.sizes',
details text format json path '$.details',
flag varchar(10) format json path '$.flag',
jsnull varchar(10) format json path '$.jsnull')
) as jt;
select * from
json_table(
'[{"doc":[1,2]},{"doc":{"k":"v"}},{"doc":null},{}]',
'$[*]' columns (doc json format json path '$.doc' null on empty)
) as jt;
SELECT HEX(doc) FROM
json_table(
'[{"doc":[1,2]},{}]',
'$[*]' columns (doc blob format json path '$.doc' null on empty)
) as jt;
select * from
json_table(
'[{"name":"Jeans","sizes":[32,34,36]},{"name":"Cellphone"}]',
'$[*]' columns (name varchar(10) path '$.name',
sizes varchar(100) format json path '$.sizes'
default '[99]' on empty)
) as jt;
--error ER_JSON_TABLE_MULTIPLE_MATCHES
select * from
json_table(
'[{"a":[1]},{"a":[2]}]',
'$' columns (a varchar(20) format json path '$[*].a' error on error)
) as jt;
--error ER_WRONG_USAGE
select * from
json_table(
'[{"a":[1,2,3]}]',
'$[*]' columns (a int format json path '$.a')
) as jt;
create view v_fmt as
select * from
json_table(
'[{"sizes":[1,2]}]',
'$[*]' columns (sizes varchar(20) format json path '$.sizes')
) as jt;
select * from v_fmt;
show create view v_fmt;
drop view v_fmt;
create view v_fmt_cs as
select * from
json_table(
'[{"sizes":[1,2]}]',
'$[*]' columns (
sizes varchar(20) character set utf8mb4 collate utf8mb4_bin
format json path '$.sizes')
) as jt;
show create view v_fmt_cs;
drop view v_fmt_cs;
--error ER_JSON_TABLE_ERROR_ON_FIELD
select * from
json_table(

View File

@@ -418,7 +418,7 @@ static void store_json_in_field(Field *f, const json_engine_t *je)
}
static int store_json_in_json(Field *f, json_engine_t *je)
static int store_json_in_field_as_json(Field *f, json_engine_t *je)
{
const uchar *from= je->value_begin;
const uchar *to;
@@ -617,7 +617,7 @@ int ha_json_table::fill_column_values(THD *thd, uchar * buf, uchar *pos)
{
if (jc->m_format_json)
{
if (!(error= store_json_in_json(*f, &je)))
if (!(error= store_json_in_field_as_json(*f, &je)))
error= er_handler.errors;
}
else if (!(error= !json_value_scalar(&je)))
@@ -931,6 +931,21 @@ TABLE *create_table_for_function(THD *thd, TABLE_LIST *sql_table)
}
bool Json_table_column::enable_format_json()
{
if (!m_field->type_handler()->is_general_purpose_string_type())
{
my_error(ER_WRONG_USAGE, MYF(0), "FORMAT JSON",
"non-string JSON_TABLE column");
return true;
}
m_format_json= true;
m_explicit_format_json= true;
return false;
}
int Json_table_column::set(THD *thd, enum_type ctype, const LEX_CSTRING &path,
CHARSET_INFO *cs)
{
@@ -955,8 +970,11 @@ int Json_table_column::set(THD *thd, enum_type ctype, const LEX_CSTRING &path,
*/
m_path.s.c_str= (const uchar *) path.str;
if (ctype == PATH)
m_format_json= m_field->type_handler() == &type_handler_long_blob_json;
if (ctype == PATH &&
Type_handler_json_common::is_json_type_handler(m_field->type_handler()))
{
m_format_json= true;
}
return 0;
}
@@ -1018,10 +1036,13 @@ int Json_table_column::print(THD *thd, Field **f, String *str)
{
static const LEX_CSTRING path= { STRING_WITH_LEN(" PATH ") };
static const LEX_CSTRING exists_path= { STRING_WITH_LEN(" EXISTS PATH ") };
bool is_json_type=
Type_handler_json_common::is_json_type_handler(m_field->type_handler());
(*f)->sql_type(column_type);
if ((m_format_json ? str->append(STRING_WITH_LEN(" JSON ")) : str->append(column_type)))
if (is_json_type ? str->append(STRING_WITH_LEN("JSON")) :
str->append(column_type))
return 1;
if (((*f)->has_charset() && m_explicit_cs &&
(str->append(STRING_WITH_LEN(" CHARSET ")) ||
@@ -1029,6 +1050,8 @@ int Json_table_column::print(THD *thd, Field **f, String *str)
(Charset(m_explicit_cs).can_have_collate_clause() &&
(str->append(STRING_WITH_LEN(" COLLATE ")) ||
str->append(&m_explicit_cs->coll_name))))) ||
(m_explicit_format_json &&
str->append(STRING_WITH_LEN(" FORMAT JSON"))) ||
str->append(m_column_type == PATH ? &path : &exists_path) ||
print_path(str, &m_path))
return 1;

View File

@@ -151,6 +151,8 @@ public:
enum_type m_column_type;
bool m_format_json;
/* FORMAT JSON was explicitly specified and should be preserved. */
bool m_explicit_format_json;
json_path_t m_path;
On_response m_on_error;
On_response m_on_empty;
@@ -165,12 +167,14 @@ public:
int set(THD *thd, enum_type ctype, const LEX_CSTRING &path, CHARSET_INFO *cs);
int set(THD *thd, enum_type ctype, const LEX_CSTRING &path,
const Lex_column_charset_collation_attrs_st &cl);
bool enable_format_json();
Json_table_column(Create_field *f, Json_table_nested_path *nest) :
m_field(f), m_nest(nest), m_explicit_cs(NULL)
{
m_on_error.m_response= RESPONSE_NOT_SPECIFIED;
m_on_empty.m_response= RESPONSE_NOT_SPECIFIED;
m_format_json= false;
m_explicit_format_json= false;
}
int print(THD *tnd, Field **f, String *str);
};
@@ -291,4 +295,3 @@ table_map add_table_function_dependencies(List<TABLE_LIST> *join_list,
table_map nest_tables, bool *error);
#endif /* JSON_TABLE_INCLUDED */

View File

@@ -12323,6 +12323,20 @@ json_table_column_type:
MYSQL_YYABORT;
}
}
| json_table_field_type FORMAT_SYM JSON_SYM PATH_SYM json_text_literal
json_opt_on_empty_or_error
{
if (Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD))
MYSQL_YYABORT;
if (Lex->json_table->m_cur_json_table_column->enable_format_json() ||
Lex->json_table->m_cur_json_table_column->
set(thd, Json_table_column::PATH, $5,
$1.charset_collation_attrs()))
{
MYSQL_YYABORT;
}
}
| json_table_field_type EXISTS PATH_SYM json_text_literal
{
if (Lex->last_field->set_attributes(thd, $1,