diff --git a/docs/reference/sparql-anything.md b/docs/reference/sparql-anything.md index ef915e4e..65510270 100644 --- a/docs/reference/sparql-anything.md +++ b/docs/reference/sparql-anything.md @@ -144,7 +144,7 @@ Set `extension` for a tool that reads the format from the file name – SPARQL A **Splitting is by line**, so every record must be one line. A delimited format that wraps a field in quotes to carry a newline inside it would be cut in two; tab-separated exports, N-Triples and NDJSON are one record per line by definition. Line endings are normalised to `\n`. -Chunks of the same input left by an earlier call are removed first, so a re-run cannot leave a longer run's tail behind for something to pick up. Only those: everything else in the directory is the caller's. +Chunks of the same input left by an earlier call are removed first, so a re-run cannot leave a longer run's tail behind for something to pick up. Only those: everything else in the directory is the caller's. Take the paths `chunk()` returns as the list of chunks to convert, rather than rediscovering them by name in the directory – a listing would also pick up whatever else is there. An input with no rows is an error rather than an empty set of chunks: a step that produced an empty file has already failed. diff --git a/packages/sparql-anything/src/chunk.ts b/packages/sparql-anything/src/chunk.ts index cc10435f..85715535 100644 --- a/packages/sparql-anything/src/chunk.ts +++ b/packages/sparql-anything/src/chunk.ts @@ -146,8 +146,10 @@ async function removeChunksOf( extension: string, into: string, ): Promise { + // Four digits or more: the index is padded to four, and grows past them + // from the 10,000th chunk on. const chunkFile = new RegExp( - `^${escapeForRegExp(name)}-\\d{4}${escapeForRegExp(extension)}$`, + `^${escapeForRegExp(name)}-\\d{4,}${escapeForRegExp(extension)}$`, ); const entries = await readdir(into, { withFileTypes: true }); await Promise.all( diff --git a/packages/sparql-anything/test/chunk.test.ts b/packages/sparql-anything/test/chunk.test.ts index ddce13c9..dccff599 100644 --- a/packages/sparql-anything/test/chunk.test.ts +++ b/packages/sparql-anything/test/chunk.test.ts @@ -183,6 +183,29 @@ describe('chunk', () => { ]); }); + // Writing 10,001 files takes a few seconds, so leave it room on a slow runner. + it( + 'removes chunks past the 10,000th, whose index has five digits', + { timeout: 30_000 }, + async () => { + const into = join(workDir, 'chunks'); + const input = join(workDir, 'places.txt'); + // One character per row keeps 10,001 rows small. + await writeFile(input, 'x\n'.repeat(10_001)); + const firstPaths = await chunk(input, { rows: 1, into }); + expect(firstPaths[10_000].endsWith('places-10000.txt')).toBe(true); + + const secondPaths = await chunk(input, { rows: 5_000, into }); + + expect(secondPaths).toHaveLength(3); + expect((await readdir(into)).sort()).toEqual([ + 'places-0000.txt', + 'places-0001.txt', + 'places-0002.txt', + ]); + }, + ); + it('refuses an extension without its leading dot', async () => { const input = await writeInput(2);