From 5fe3296055c3f7736bb0f66f8c628165b48a2d2b Mon Sep 17 00:00:00 2001 From: Luke H-W Date: Fri, 5 Sep 2025 02:24:58 +0930 Subject: [PATCH] Disable Field count validation of CSV viewer (#35228) Default behaviour rejected all rows (Records) with more or fewer columns (Fields) than the first row, preventing them from parsing at all and silently hiding them. While RFC4180 section 2.4 says each line _should_ contain the same number of fields, enforcing this on the viewer is unhelpful. This pull request disables that validation, allowing the viewer to render lines with fewer columns than the maximum number within the file. As it's a simple HTML table, this works without additional changes (i.e. no need to manually determine the maximum number of columns), but the default appearance of rows with fewer columns may be undesirable to some people, especially when using CSS that has `td {border-right: none}`. Screenshot without cell right
borders Screenshot with cell right borders Fixes #16559, #30358. Unfortunately, retaining empty lines is less trivial, so the line numbers on the leftmost column will still not match the source file whenever those are present, though a future PR could address that. --- modules/csv/csv.go | 2 ++ modules/csv/csv_test.go | 33 ++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/modules/csv/csv.go b/modules/csv/csv.go index f1ca3b0923..ad61b81d69 100644 --- a/modules/csv/csv.go +++ b/modules/csv/csv.go @@ -30,6 +30,8 @@ func CreateReader(input io.Reader, delimiter rune) *stdcsv.Reader { // thus would change `\t\t` to just `\t` or ` ` (two spaces) to just ` ` (single space) rd.TrimLeadingSpace = true } + // Don't force validation of every row to have the same number of entries as the first row. + rd.FieldsPerRecord = -1 return rd } diff --git a/modules/csv/csv_test.go b/modules/csv/csv_test.go index be9fc5f823..5ea9718466 100644 --- a/modules/csv/csv_test.go +++ b/modules/csv/csv_test.go @@ -94,6 +94,24 @@ j, ,\x20 }, expectedDelimiter: ',', }, + // case 3 - every delimiter used, default to comma and handle differing number of fields per record + { + csv: `col1,col2 +a;b +c@e +f g +h|i +jkl`, + expectedRows: [][]string{ + {"col1", "col2"}, + {"a;b"}, + {"c@e"}, + {"f g"}, + {"h|i"}, + {"jkl"}, + }, + expectedDelimiter: ',', + }, } for n, c := range cases { @@ -119,21 +137,6 @@ func TestDetermineDelimiterShortBufferError(t *testing.T) { assert.Nil(t, rd, "CSV reader should be mnil") } -func TestDetermineDelimiterReadAllError(t *testing.T) { - rd, err := CreateReaderAndDetermineDelimiter(nil, strings.NewReader(`col1,col2 - a;b - c@e - f g - h|i - jkl`)) - assert.NoError(t, err, "CreateReaderAndDetermineDelimiter() shouldn't throw error") - assert.NotNil(t, rd, "CSV reader should not be mnil") - rows, err := rd.ReadAll() - assert.Error(t, err, "RaadAll() should throw error") - assert.ErrorIs(t, err, csv.ErrFieldCount) - assert.Empty(t, rows, "rows should be empty") -} - func TestDetermineDelimiter(t *testing.T) { cases := []struct { csv string