mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-39969: Incorrect result of JSON_TABLE with BIT
Problem: JSON_TABLE maps JSON numbers [0,1] to BIT(1) values [1,1]. The scalar helper passes decimal text to BIT's binary-string store, so the byte for '0' is 48 and is clipped to 1. Fix: Add Field::store_numeric() for number literals serialized as text, including fractions and exponents, and use it for JSON_TABLE numbers. BIT parses these literals as rounded unsigned integers and applies its existing range checks. Other fields retain their existing conversion; JSON strings and booleans retain their existing storage paths. Preserve the existing distinction between conversion warnings and JSON structural errors, so numeric overflow does not invoke ON ERROR. Extend json.json_table to cover BIT widths, unsigned boundaries, fractions, exponents, boolean/string/null inputs, other field types, conversion warnings and ON ERROR responses. Assisted-by: OpenAI Codex:gpt-6
This commit is contained in:
@@ -1298,4 +1298,122 @@ COLLATION(virtual_table_from_json.property)
|
||||
utf8mb4_uca1400_ai_ci
|
||||
DROP TABLE table_with_longtext_column;
|
||||
DROP DATABASE collation_testing;
|
||||
#
|
||||
# MDEV-39969: JSON_TABLE conversion of numeric values to BIT
|
||||
#
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[0,1]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value
|
||||
1 0
|
||||
2 1
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[0,2,255,256,0.4,0.5,1.5,1e1,1e-1]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(9) PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value
|
||||
1 0
|
||||
2 2
|
||||
3 255
|
||||
4 256
|
||||
5 0
|
||||
6 1
|
||||
7 2
|
||||
8 10
|
||||
9 0
|
||||
SELECT ord, b+0 AS bit_value, b+0 = u AS matches_unsigned
|
||||
FROM JSON_TABLE('[9223372036854775808,18446744073709551615]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(64) PATH '$',
|
||||
u BIGINT UNSIGNED PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value matches_unsigned
|
||||
1 9223372036854775808 1
|
||||
2 18446744073709551615 1
|
||||
SELECT ord, b+0 AS bit_value, i, s
|
||||
FROM JSON_TABLE('[false,true]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$',
|
||||
i INT PATH '$', s VARCHAR(5) PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value i s
|
||||
1 0 0 false
|
||||
2 1 1 true
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('["0","1",null]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(8) PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value
|
||||
1 48
|
||||
2 49
|
||||
3 NULL
|
||||
SELECT i, d, f = 1e100 AS real_value_matches, s, dt
|
||||
FROM JSON_TABLE('[{"n":1e2,"f":1e100,"d":20260924}]', '$[*]'
|
||||
COLUMNS (i INT PATH '$.n', d DECIMAL(6,2) PATH '$.n',
|
||||
f DOUBLE PATH '$.f', s VARCHAR(10) PATH '$.n',
|
||||
dt DATE PATH '$.d')) AS jt;
|
||||
i d real_value_matches s dt
|
||||
100 100.00 1 1e2 2026-09-24
|
||||
SELECT ord, b+0 AS bit_value, s
|
||||
FROM JSON_TABLE(CONVERT('[false,true,0,1]' USING utf8mb4), '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$',
|
||||
s VARCHAR(5) PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value s
|
||||
1 0 false
|
||||
2 1 true
|
||||
3 0 0
|
||||
4 1 1
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[2]', '$[*]' COLUMNS (b BIT(1) PATH '$')) AS jt;
|
||||
bit_value
|
||||
1
|
||||
Warnings:
|
||||
Warning 1264 Out of range value for column 'b' at row 1
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[2]', '$[*]'
|
||||
COLUMNS (b BIT(1) PATH '$' NULL ON ERROR)) AS jt;
|
||||
bit_value
|
||||
1
|
||||
Warnings:
|
||||
Warning 1264 Out of range value for column 'b' at row 1
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[256]', '$[*]'
|
||||
COLUMNS (b BIT(8) PATH '$' DEFAULT 'A' ON ERROR)) AS jt;
|
||||
bit_value
|
||||
255
|
||||
Warnings:
|
||||
Warning 1264 Out of range value for column 'b' at row 1
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[2]', '$[*]'
|
||||
COLUMNS (b BIT(1) PATH '$' ERROR ON ERROR)) AS jt;
|
||||
bit_value
|
||||
1
|
||||
Warnings:
|
||||
Warning 1264 Out of range value for column 'b' at row 1
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[-1,18446744073709551616,1e1000]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(64) PATH '$')) AS jt ORDER BY ord;
|
||||
ord bit_value
|
||||
1 0
|
||||
2 18446744073709551615
|
||||
3 18446744073709551615
|
||||
Warnings:
|
||||
Warning 1264 Out of range value for column 'b' at row 1
|
||||
Warning 1264 Out of range value for column 'b' at row 2
|
||||
Warning 1264 Out of range value for column 'b' at row 3
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{},[]]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$' NULL ON ERROR)) AS jt
|
||||
ORDER BY ord;
|
||||
ord bit_value
|
||||
1 NULL
|
||||
2 NULL
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{}]', '$[*]'
|
||||
COLUMNS (b BIT(8) PATH '$' DEFAULT 'A' ON ERROR)) AS jt;
|
||||
bit_value
|
||||
65
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{}]', '$[*]'
|
||||
COLUMNS (b BIT(1) PATH '$' ERROR ON ERROR)) AS jt;
|
||||
ERROR HY000: Can't store an array or an object in the scalar column 'b' of JSON_TABLE 'jt'.
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{}]', '$[*]'
|
||||
COLUMNS (b BIT(8) PATH '$.missing' DEFAULT 'A' ON EMPTY)) AS jt;
|
||||
bit_value
|
||||
65
|
||||
# End of 10.11 test
|
||||
|
||||
@@ -1121,4 +1121,65 @@ SELECT COLLATION(virtual_table_from_json.property) FROM JSON_TABLE((SELECT colum
|
||||
DROP TABLE table_with_longtext_column;
|
||||
DROP DATABASE collation_testing;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39969: JSON_TABLE conversion of numeric values to BIT
|
||||
--echo #
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[0,1]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$')) AS jt ORDER BY ord;
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[0,2,255,256,0.4,0.5,1.5,1e1,1e-1]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(9) PATH '$')) AS jt ORDER BY ord;
|
||||
SELECT ord, b+0 AS bit_value, b+0 = u AS matches_unsigned
|
||||
FROM JSON_TABLE('[9223372036854775808,18446744073709551615]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(64) PATH '$',
|
||||
u BIGINT UNSIGNED PATH '$')) AS jt ORDER BY ord;
|
||||
SELECT ord, b+0 AS bit_value, i, s
|
||||
FROM JSON_TABLE('[false,true]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$',
|
||||
i INT PATH '$', s VARCHAR(5) PATH '$')) AS jt ORDER BY ord;
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('["0","1",null]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(8) PATH '$')) AS jt ORDER BY ord;
|
||||
SELECT i, d, f = 1e100 AS real_value_matches, s, dt
|
||||
FROM JSON_TABLE('[{"n":1e2,"f":1e100,"d":20260924}]', '$[*]'
|
||||
COLUMNS (i INT PATH '$.n', d DECIMAL(6,2) PATH '$.n',
|
||||
f DOUBLE PATH '$.f', s VARCHAR(10) PATH '$.n',
|
||||
dt DATE PATH '$.d')) AS jt;
|
||||
SELECT ord, b+0 AS bit_value, s
|
||||
FROM JSON_TABLE(CONVERT('[false,true,0,1]' USING utf8mb4), '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$',
|
||||
s VARCHAR(5) PATH '$')) AS jt ORDER BY ord;
|
||||
|
||||
# Type conversion warnings must not invoke ON ERROR.
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[2]', '$[*]' COLUMNS (b BIT(1) PATH '$')) AS jt;
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[2]', '$[*]'
|
||||
COLUMNS (b BIT(1) PATH '$' NULL ON ERROR)) AS jt;
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[256]', '$[*]'
|
||||
COLUMNS (b BIT(8) PATH '$' DEFAULT 'A' ON ERROR)) AS jt;
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[2]', '$[*]'
|
||||
COLUMNS (b BIT(1) PATH '$' ERROR ON ERROR)) AS jt;
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[-1,18446744073709551616,1e1000]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(64) PATH '$')) AS jt ORDER BY ord;
|
||||
|
||||
# JSON structure errors still use the configured ON ERROR response.
|
||||
SELECT ord, b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{},[]]', '$[*]'
|
||||
COLUMNS (ord FOR ORDINALITY, b BIT(1) PATH '$' NULL ON ERROR)) AS jt
|
||||
ORDER BY ord;
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{}]', '$[*]'
|
||||
COLUMNS (b BIT(8) PATH '$' DEFAULT 'A' ON ERROR)) AS jt;
|
||||
--error ER_JSON_TABLE_SCALAR_EXPECTED
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{}]', '$[*]'
|
||||
COLUMNS (b BIT(1) PATH '$' ERROR ON ERROR)) AS jt;
|
||||
SELECT b+0 AS bit_value
|
||||
FROM JSON_TABLE('[{}]', '$[*]'
|
||||
COLUMNS (b BIT(8) PATH '$.missing' DEFAULT 'A' ON EMPTY)) AS jt;
|
||||
--echo # End of 10.11 test
|
||||
|
||||
28
sql/field.cc
28
sql/field.cc
@@ -10125,6 +10125,34 @@ int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs)
|
||||
}
|
||||
|
||||
|
||||
int Field_bit::store_numeric(const char *from, size_t length, CHARSET_INFO *cs)
|
||||
{
|
||||
char *end;
|
||||
int error;
|
||||
ulonglong nr= cs->strntoull10rnd(from, length, true, &end, &error);
|
||||
int store_error= store((longlong) nr, true);
|
||||
|
||||
if (unlikely(error == MY_ERRNO_ERANGE))
|
||||
{
|
||||
if (!store_error)
|
||||
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
|
||||
return 1;
|
||||
}
|
||||
if (error == MY_ERRNO_EDOM || from == end)
|
||||
{
|
||||
ErrConvString str(from, length, cs);
|
||||
set_warning_truncated_wrong_value("integer", str.ptr());
|
||||
return 1;
|
||||
}
|
||||
if (test_if_important_data(cs, end, from + length))
|
||||
{
|
||||
set_warning(WARN_DATA_TRUNCATED, 1);
|
||||
return 1;
|
||||
}
|
||||
return store_error;
|
||||
}
|
||||
|
||||
|
||||
int Field_bit::store(double nr)
|
||||
{
|
||||
return Field_bit::store((longlong) nr, FALSE);
|
||||
|
||||
12
sql/field.h
12
sql/field.h
@@ -967,6 +967,17 @@ public:
|
||||
{
|
||||
return store(to, length, cs);
|
||||
}
|
||||
/*
|
||||
Store a number literal as text, possibly with a fraction or exponent,
|
||||
rather than a binary string. Called by store_json_in_field() in JSON_TABLE
|
||||
for JSON numeric values. Types whose string store uses a binary
|
||||
representation can override this without changing string input.
|
||||
As with store(), a nonzero result can indicate a conversion warning.
|
||||
*/
|
||||
virtual int store_numeric(const char *from, size_t length, CHARSET_INFO *cs)
|
||||
{
|
||||
return store(from, length, cs);
|
||||
}
|
||||
virtual int store_binary(const char *to, size_t length)
|
||||
{
|
||||
return store(to, length, &my_charset_bin);
|
||||
@@ -5003,6 +5014,7 @@ public:
|
||||
int save_in_field(Field *to) override { return to->store(val_int(), true); }
|
||||
bool memcpy_field_possible(const Field *from) const override{ return false; }
|
||||
int store(const char *to, size_t length, CHARSET_INFO *charset) override;
|
||||
int store_numeric(const char *from, size_t length, CHARSET_INFO *cs) override;
|
||||
int store(double nr) override;
|
||||
int store(longlong nr, bool unsigned_val) override;
|
||||
int store_decimal(const my_decimal *) override;
|
||||
|
||||
@@ -360,7 +360,7 @@ int ha_json_table::rnd_init(bool scan)
|
||||
/*
|
||||
@brief
|
||||
Store JSON value in an SQL field, doing necessary special conversions
|
||||
for JSON's null, true, and false.
|
||||
for JSON's null, true, false, and numeric values.
|
||||
*/
|
||||
|
||||
static void store_json_in_field(Field *f, const json_engine_t *je)
|
||||
@@ -388,7 +388,10 @@ static void store_json_in_field(Field *f, const json_engine_t *je)
|
||||
break;
|
||||
};
|
||||
st_append_json(&res_tmp, je->s.cs, je->value, je->value_len);
|
||||
f->store((const char *) res_tmp.ptr(), (uint32) res_tmp.length(), je->s.cs);
|
||||
if (je->value_type == JSON_VALUE_NUMBER)
|
||||
f->store_numeric(res_tmp.ptr(), res_tmp.length(), je->s.cs);
|
||||
else
|
||||
f->store(res_tmp.ptr(), res_tmp.length(), je->s.cs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user