|
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