Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #75: no longer throw when interpreting TINYINT(1) columns with non-0 or 1 values as booleans #83

Merged
merged 4 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion duckdb
Submodule duckdb updated 749 files
23 changes: 17 additions & 6 deletions src/mysql_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,24 @@ void CastBoolFromMySQL(ClientContext &context, Vector &input, Vector &result, id
}
auto str_data = input_data[r].GetData();
auto str_size = input_data[r].GetSize();
if (str_size != 1) {
throw BinderException("Failed to cast MySQL boolean - expected 1 byte "
"element but got element of size %s",
str_size);
if (str_size == 0) {
throw BinderException(
"Failed to cast MySQL boolean - expected 1 byte element but got element of size %d\n* SET "
"mysql_tinyint1_as_boolean=false to disable loading TINYINT(1) columns as booleans\n* SET "
"mysql_bit1_as_boolean=false to disable loading BIT(1) columns as booleans",
str_size);
}
// booleans are EITHER binary "1" or "0" (BIT(1))
// OR a number
// in both cases we can figure out what value it is from the first character:
// \0 -> zero byte, false
// - -> negative number, false
// 0 -> zero number, false
if (*str_data == '\0' || *str_data == '0' || *str_data == '-') {
result_data[r] = false;
} else {
result_data[r] = true;
}
auto bool_char = *str_data;
result_data[r] = bool_char == '\1' || bool_char == '1';
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/storage/mysql_table_entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ unique_ptr<BaseStatistics> MySQLTableEntry::GetStatistics(ClientContext &context
return nullptr;
}

void MySQLTableEntry::BindUpdateConstraints(Binder &binder, LogicalGet &, LogicalProjection &, LogicalUpdate &, ClientContext &) {
void MySQLTableEntry::BindUpdateConstraints(Binder &binder, LogicalGet &, LogicalProjection &, LogicalUpdate &,
ClientContext &) {
}

TableFunction MySQLTableEntry::GetScanFunction(ClientContext &context, unique_ptr<FunctionData> &bind_data) {
Expand Down
11 changes: 11 additions & 0 deletions test/sql/attach_fake_booleans.test
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ SELECT * FROM s1.fake_booleans
true true
false false
NULL NULL
false true
true false

query II
SELECT typeof(tinyint_bool), typeof(bit_bool) FROM s1.fake_booleans LIMIT 1
Expand All @@ -40,6 +42,15 @@ SELECT typeof(tinyint_bool), typeof(bit_bool) FROM s1.fake_booleans LIMIT 1
----
TINYINT BLOB

query II
SELECT * FROM s1.fake_booleans
----
1 \x01
0 \x00
NULL NULL
-128 \x01
127 \x00

statement ok
SET mysql_tinyint1_as_boolean=true

Expand Down
2 changes: 1 addition & 1 deletion test/sql/attach_types.test
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ NULL NULL NULL NULL
# test all types
statement ok
CREATE TABLE all_types_tbl AS SELECT *
EXCLUDE (float, double, ubigint, hugeint, uhugeint, int_array, double_array, date_array, timestamp_array, timestamptz_array, varchar_array, nested_int_array, struct, struct_of_arrays, array_of_structs, map, "union",fixed_int_array,fixed_varchar_array,fixed_nested_varchar_array,list_of_fixed_int_array,fixed_array_of_int_list,fixed_nested_int_array,struct_of_fixed_array,fixed_struct_array
EXCLUDE (float, double, ubigint, hugeint, uhugeint, int_array, double_array, date_array, timestamp_array, timestamptz_array, varchar_array, nested_int_array, struct, struct_of_arrays, array_of_structs, map, "union",fixed_int_array,fixed_varchar_array,fixed_nested_varchar_array,list_of_fixed_int_array,fixed_array_of_int_list,fixed_nested_int_array,struct_of_fixed_array,fixed_struct_array, varint
)
REPLACE(
CASE WHEN int IS NOT NULL THEN '2000-01-01' ELSE NULL END AS date,
Expand Down
8 changes: 5 additions & 3 deletions test/sql/mysql_query.test
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,16 @@ DECIMAL(2,1) DECIMAL(5,1) DECIMAL(10,2) DECIMAL(20,3) DOUBLE
query II
SELECT * FROM mysql_query('simple', 'SELECT * FROM fake_booleans')
----
true true
false false
1 true
0 false
NULL NULL
-128 true
127 false

query II
SELECT * FROM mysql_query('simple', 'SELECT * FROM bits')
----
false \x00\x00\x00\x00\x00\x01UU
true \x00\x00\x00\x00\x00\x01UU
NULL NULL

query III
Expand Down
2 changes: 1 addition & 1 deletion test/test_data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ INSERT INTO decimals VALUES (

-- MySQL doesn't support a "proper" boolean so people often use TINYINT(1) or BIT(1) as boolean
CREATE TABLE fake_booleans(tinyint_bool TINYINT(1), bit_bool BIT(1));
INSERT INTO fake_booleans VALUES (true, true), (false, false), (NULL, NULL);
INSERT INTO fake_booleans VALUES (true, true), (false, false), (NULL, NULL), (-128, b'1'), (127, b'0');

CREATE TABLE bits(b BIT(6), bl BIT(64));
INSERT INTO bits VALUES (b'000101', b'010101010101010101');
Expand Down
Loading