1
0
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:
DerZc
2026-09-21 12:46:46 +08:00
committed by Raghunandan Bhat
parent 94f80cbfa2
commit e331450f1b
5 changed files with 224 additions and 2 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}