|
84 | 84 | numberFloat32ValueType = reflect.TypeOf(float32(0)) |
85 | 85 | numberFloat64ValueType = reflect.TypeOf(float64(0)) |
86 | 86 |
|
| 87 | + // numre is a regex pattern for extracting MySQL-compatible numeric prefixes from strings. |
| 88 | + // It matches: |
| 89 | + // - Optional leading whitespace (space, tab, newline, carriage return) |
| 90 | + // - Optional sign (+ or -) |
| 91 | + // - Either: |
| 92 | + // - Digits followed by optional decimal point and more digits: "123.456" |
| 93 | + // - Just a decimal point followed by digits: ".456" |
| 94 | + // - Optional scientific notation (e/E followed by optional sign and digits) |
| 95 | + // Examples: "123.45abc" -> "123.45", " -3.14e2xyz" -> "-3.14e2", ".5test" -> ".5" |
87 | 96 | numre = regexp.MustCompile(`^[ \t\n\r]*[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?`) |
88 | 97 | ) |
89 | 98 |
|
@@ -936,6 +945,19 @@ func (t NumberTypeImpl_) DisplayWidth() int { |
936 | 945 | return t.displayWidth |
937 | 946 | } |
938 | 947 |
|
| 948 | +// convertStringToFloat64WithTruncation attempts to extract and parse a numeric prefix from a string |
| 949 | +// using MySQL-compatible truncation logic. Returns the parsed float64 value and |
| 950 | +// whether a valid numeric prefix was found. |
| 951 | +func convertStringToFloat64WithTruncation(originalV string) (float64, bool) { |
| 952 | + s := numre.FindString(originalV) |
| 953 | + if s != "" { |
| 954 | + if f, err := strconv.ParseFloat(strings.TrimSpace(s), 64); err == nil { |
| 955 | + return f, true |
| 956 | + } |
| 957 | + } |
| 958 | + return 0, false |
| 959 | +} |
| 960 | + |
939 | 961 | func convertToInt64(ctx context.Context, t NumberTypeImpl_, v interface{}) (int64, sql.ConvertInRange, error) { |
940 | 962 | switch v := v.(type) { |
941 | 963 | case time.Time: |
@@ -1016,19 +1038,13 @@ func convertToInt64(ctx context.Context, t NumberTypeImpl_, v interface{}) (int6 |
1016 | 1038 | f, err := strconv.ParseFloat(v, 64) |
1017 | 1039 | if err != nil { |
1018 | 1040 | // Always try MySQL-compatible truncation first for arithmetic expressions |
1019 | | - s := numre.FindString(originalV) |
1020 | | - if s != "" { |
1021 | | - f, parseErr := strconv.ParseFloat(s, 64) |
1022 | | - if parseErr == nil { |
1023 | | - f = math.Round(f) |
1024 | | - return int64(f), sql.InRange, nil |
1025 | | - } |
| 1041 | + if f, found := convertStringToFloat64WithTruncation(originalV); found { |
| 1042 | + f = math.Round(f) |
| 1043 | + return int64(f), sql.InRange, nil |
1026 | 1044 | } |
1027 | 1045 |
|
1028 | | - // For purely non-numeric strings like 'two', 'four', etc., return error |
1029 | | - // This allows Dolt's diff system to handle incompatible type conversions correctly |
1030 | | - // In strict mode, also return error for better schema validation |
1031 | | - if strictMode || s == "" { |
| 1046 | + // In strict mode, return error for better schema validation |
| 1047 | + if strictMode { |
1032 | 1048 | return 0, sql.OutOfRange, sql.ErrInvalidValue.New(originalV, "int") |
1033 | 1049 | } |
1034 | 1050 |
|
@@ -1220,12 +1236,9 @@ func convertToUint64(ctx context.Context, t NumberTypeImpl_, v interface{}) (uin |
1220 | 1236 | } |
1221 | 1237 | } |
1222 | 1238 | // Use same truncation logic as float conversion for MySQL compatibility |
1223 | | - s := numre.FindString(v) |
1224 | | - if s != "" { |
1225 | | - if f, err := strconv.ParseFloat(s, 64); err == nil { |
1226 | | - if val, inRange, err := convertToUint64(context.Background(), t, f); err == nil { |
1227 | | - return val, inRange, err |
1228 | | - } |
| 1239 | + if f, found := convertStringToFloat64WithTruncation(v); found { |
| 1240 | + if val, inRange, err := convertToUint64(context.Background(), t, f); err == nil { |
| 1241 | + return val, inRange, err |
1229 | 1242 | } |
1230 | 1243 | } |
1231 | 1244 | // If no valid number found, return 0 (MySQL behavior for pure non-numeric strings) |
@@ -1330,12 +1343,9 @@ func convertToUint32(t NumberTypeImpl_, v interface{}) (uint32, sql.ConvertInRan |
1330 | 1343 | } |
1331 | 1344 | } |
1332 | 1345 | // Use same truncation logic as float conversion for MySQL compatibility |
1333 | | - s := numre.FindString(v) |
1334 | | - if s != "" { |
1335 | | - if f, err := strconv.ParseFloat(s, 64); err == nil { |
1336 | | - if val, inRange, err := convertToUint32(t, f); err == nil { |
1337 | | - return val, inRange, err |
1338 | | - } |
| 1346 | + if f, found := convertStringToFloat64WithTruncation(v); found { |
| 1347 | + if val, inRange, err := convertToUint32(t, f); err == nil { |
| 1348 | + return val, inRange, err |
1339 | 1349 | } |
1340 | 1350 | } |
1341 | 1351 | // If no valid number found, return 0 (MySQL behavior for pure non-numeric strings) |
@@ -1436,12 +1446,9 @@ func convertToUint16(t NumberTypeImpl_, v interface{}) (uint16, sql.ConvertInRan |
1436 | 1446 | } |
1437 | 1447 | } |
1438 | 1448 | // Use same truncation logic as float conversion for MySQL compatibility |
1439 | | - s := numre.FindString(v) |
1440 | | - if s != "" { |
1441 | | - if f, err := strconv.ParseFloat(s, 64); err == nil { |
1442 | | - if val, inRange, err := convertToUint16(t, f); err == nil { |
1443 | | - return val, inRange, err |
1444 | | - } |
| 1449 | + if f, found := convertStringToFloat64WithTruncation(v); found { |
| 1450 | + if val, inRange, err := convertToUint16(t, f); err == nil { |
| 1451 | + return val, inRange, err |
1445 | 1452 | } |
1446 | 1453 | } |
1447 | 1454 | // If no valid number found, return 0 (MySQL behavior for pure non-numeric strings) |
@@ -1558,12 +1565,9 @@ func convertToUint8(ctx context.Context, t NumberTypeImpl_, v interface{}) (uint |
1558 | 1565 | } |
1559 | 1566 |
|
1560 | 1567 | // Use same truncation logic as float conversion for MySQL compatibility |
1561 | | - s := numre.FindString(originalV) |
1562 | | - if s != "" { |
1563 | | - if f, err := strconv.ParseFloat(s, 64); err == nil { |
1564 | | - if val, inRange, err := convertToUint8(ctx, t, f); err == nil { |
1565 | | - return val, inRange, err |
1566 | | - } |
| 1568 | + if f, found := convertStringToFloat64WithTruncation(originalV); found { |
| 1569 | + if val, inRange, err := convertToUint8(ctx, t, f); err == nil { |
| 1570 | + return val, inRange, err |
1567 | 1571 | } |
1568 | 1572 | } |
1569 | 1573 |
|
@@ -1639,12 +1643,8 @@ func convertToFloat64(ctx context.Context, t NumberTypeImpl_, v interface{}) (fl |
1639 | 1643 | i, err := strconv.ParseFloat(v, 64) |
1640 | 1644 | if err != nil { |
1641 | 1645 | // Always try MySQL-compatible truncation first for arithmetic expressions |
1642 | | - s := numre.FindString(originalV) |
1643 | | - if s != "" { |
1644 | | - f, parseErr := strconv.ParseFloat(s, 64) |
1645 | | - if parseErr == nil { |
1646 | | - return f, nil |
1647 | | - } |
| 1646 | + if f, found := convertStringToFloat64WithTruncation(originalV); found { |
| 1647 | + return f, nil |
1648 | 1648 | } |
1649 | 1649 |
|
1650 | 1650 | // In strict mode, return error instead of truncating for schema validation |
|
0 commit comments