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:
committed by
Daniel Black
parent
f40ea8f4e6
commit
c3ec2dc368
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user