Skip to content

Commit 8e5eb75

Browse files
takaidohigasiclaude
andcommitted
dumpling/export: fail loudly on boundary-sampling errors and unknown column types
- Boundary-sampling failure used to log a warning and `break`, which silently dropped into the post-loop branches. On a first-iteration failure the table was dumped as a single un-chunked task; on a later failure the dump emitted an oversized "tail" chunk from the last good boundary to +∞. Either way the caller never saw the failure. Return the error so the job-level retry/backoff applies. - Replace the silent `fmt.Sprintf("%v", v)` fallback in the row-scan type switch with an explicit error referencing the column name and Go type. The MySQL driver returns string/[]byte/int*/float* for the supported boundary types (parseTime is not enabled in GetDriverConfig), so any other type means a future caller widened the chunking surface beyond what the WHERE-clause builders support and we should fail loudly rather than emit a malformed predicate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3cf73d2 commit 8e5eb75

1 file changed

Lines changed: 36 additions & 22 deletions

File tree

dumpling/export/string_chunking.go

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -175,26 +175,33 @@ func (d *Dumper) streamStringChunks(tctx *tcontext.Context, conn *BaseConn, meta
175175
for j, val := range values {
176176
if val == nil {
177177
currentBoundary[j] = ""
178-
} else {
179-
// Convert SQL driver value to string properly
180-
switch v := val.(type) {
181-
case string:
182-
currentBoundary[j] = v
183-
case []byte:
184-
currentBoundary[j] = string(v)
185-
case int64:
186-
currentBoundary[j] = strconv.FormatInt(v, 10)
187-
case int32:
188-
currentBoundary[j] = strconv.FormatInt(int64(v), 10)
189-
case int:
190-
currentBoundary[j] = strconv.Itoa(v)
191-
case float64:
192-
currentBoundary[j] = strconv.FormatFloat(v, 'f', -1, 64)
193-
case float32:
194-
currentBoundary[j] = strconv.FormatFloat(float64(v), 'f', -1, 32)
195-
default:
196-
currentBoundary[j] = fmt.Sprintf("%v", v)
197-
}
178+
continue
179+
}
180+
// Supported driver-returned types for boundary columns. The
181+
// MySQL driver returns []byte for string/binary/DECIMAL/DATE*
182+
// (parseTime is not enabled, see GetDriverConfig), int64 for
183+
// integers, and float64 for floating-point — all of which
184+
// round-trip safely through the WHERE-clause builders. Any
185+
// other type would silently produce a malformed predicate, so
186+
// fail loudly instead.
187+
switch v := val.(type) {
188+
case string:
189+
currentBoundary[j] = v
190+
case []byte:
191+
currentBoundary[j] = string(v)
192+
case int64:
193+
currentBoundary[j] = strconv.FormatInt(v, 10)
194+
case int32:
195+
currentBoundary[j] = strconv.FormatInt(int64(v), 10)
196+
case int:
197+
currentBoundary[j] = strconv.Itoa(v)
198+
case float64:
199+
currentBoundary[j] = strconv.FormatFloat(v, 'f', -1, 64)
200+
case float32:
201+
currentBoundary[j] = strconv.FormatFloat(float64(v), 'f', -1, 32)
202+
default:
203+
return fmt.Errorf("unsupported boundary column type %T for %s in table `%s`.`%s`",
204+
v, orderByColumns[j], db, tbl)
198205
}
199206
}
200207
return nil
@@ -207,12 +214,19 @@ func (d *Dumper) streamStringChunks(tctx *tcontext.Context, conn *BaseConn, meta
207214
}, sampleQuery)
208215

209216
if err != nil {
210-
tctx.L().Warn("failed to sample boundary, stopping boundary collection",
217+
// Don't swallow sampling failures: with `break` the loop would fall
218+
// through to the post-loop branches and either dump the entire table
219+
// as one un-chunked task (when the very first sample fails) or emit
220+
// an oversized tail chunk from the last good boundary to +∞ (when a
221+
// later sample fails). Both silently degrade the dump. Propagate so
222+
// the job-level retry/backoff applies and the caller sees the
223+
// failure.
224+
tctx.L().Warn("failed to sample boundary, aborting string-key chunking",
211225
zap.String("database", db),
212226
zap.String("table", tbl),
213227
zap.Int64("chunkIndex", i),
214228
zap.Error(err))
215-
break
229+
return err
216230
}
217231

218232
if len(currentBoundary) == 0 {

0 commit comments

Comments
 (0)