Skip to content

Commit

Permalink
Merge pull request #83 from Mytherin/issue75
Browse files Browse the repository at this point in the history
Fix #75: no longer throw when interpreting TINYINT(1) columns with non-0 or 1 values as booleans
  • Loading branch information
Mytherin authored Sep 5, 2024
2 parents 64cb6ae + 9f2400a commit 248b976
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 13 deletions.
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

0 comments on commit 248b976

Please sign in to comment.