From 903a2656761509c63796db62730b2362ef5b967a Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Tue, 27 Nov 2018 16:51:51 -0600 Subject: [PATCH] Fix JSON marshal panic when dealing with NaN values --- pkg/client/client_test.go | 115 +++++++++++++++++++++----------------- pkg/client/dump_test.go | 2 +- pkg/client/result.go | 9 +++ 3 files changed, 73 insertions(+), 53 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 2f5014d..c8e4615 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/sosedoff/pgweb/pkg/command" + + "github.com/stretchr/testify/assert" ) var ( @@ -78,6 +78,9 @@ func onWindows() bool { } func setup() { + // No pretty JSON for testsm + command.Opts.DisablePrettyJson = true + out, err := exec.Command( testCommands["createdb"], "-U", serverUser, @@ -133,7 +136,7 @@ func teardown() { } } -func test_NewClientFromUrl(t *testing.T) { +func testNewClientFromUrl(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, err := NewFromUrl(url, nil) @@ -145,7 +148,7 @@ func test_NewClientFromUrl(t *testing.T) { assert.Equal(t, url, client.ConnectionString) } -func test_NewClientFromUrl2(t *testing.T) { +func testNewClientFromUrl2(t *testing.T) { url := fmt.Sprintf("postgresql://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, err := NewFromUrl(url, nil) @@ -157,7 +160,7 @@ func test_NewClientFromUrl2(t *testing.T) { assert.Equal(t, url, client.ConnectionString) } -func test_ClientIdleTime(t *testing.T) { +func testClientIdleTime(t *testing.T) { examples := map[time.Time]bool{ time.Now(): false, // Current time time.Now().Add(time.Minute * -30): false, // 30 minutes ago @@ -172,25 +175,25 @@ func test_ClientIdleTime(t *testing.T) { } } -func test_Test(t *testing.T) { +func testTest(t *testing.T) { assert.Equal(t, nil, testClient.Test()) } -func test_Info(t *testing.T) { +func testInfo(t *testing.T) { res, err := testClient.Info() assert.Equal(t, nil, err) assert.NotEqual(t, nil, res) } -func test_Activity(t *testing.T) { +func testActivity(t *testing.T) { res, err := testClient.Activity() assert.Equal(t, nil, err) assert.NotEqual(t, nil, res) } -func test_Databases(t *testing.T) { +func testDatabases(t *testing.T) { res, err := testClient.Databases() assert.Equal(t, nil, err) @@ -198,7 +201,7 @@ func test_Databases(t *testing.T) { assert.Contains(t, res, "postgres") } -func test_Objects(t *testing.T) { +func testObjects(t *testing.T) { res, err := testClient.Objects() objects := ObjectsFromResult(res) @@ -244,7 +247,7 @@ func test_Objects(t *testing.T) { } } -func test_Table(t *testing.T) { +func testTable(t *testing.T) { res, err := testClient.Table("books") columns := []string{ @@ -262,7 +265,7 @@ func test_Table(t *testing.T) { assert.Equal(t, 4, len(res.Rows)) } -func test_TableRows(t *testing.T) { +func testTableRows(t *testing.T) { res, err := testClient.TableRows("books", RowsOptions{}) assert.Equal(t, nil, err) @@ -270,7 +273,7 @@ func test_TableRows(t *testing.T) { assert.Equal(t, 15, len(res.Rows)) } -func test_TableInfo(t *testing.T) { +func testTableInfo(t *testing.T) { res, err := testClient.TableInfo("books") assert.Equal(t, nil, err) @@ -278,7 +281,7 @@ func test_TableInfo(t *testing.T) { assert.Equal(t, 1, len(res.Rows)) } -func test_EstimatedTableRowsCount(t *testing.T) { +func testEstimatedTableRowsCount(t *testing.T) { var count int64 = 15 res, err := testClient.EstimatedTableRowsCount("books", RowsOptions{}) @@ -287,7 +290,7 @@ func test_EstimatedTableRowsCount(t *testing.T) { assert.Equal(t, []Row{Row{count}}, res.Rows) } -func test_TableRowsCount(t *testing.T) { +func testTableRowsCount(t *testing.T) { var count int64 = 15 res, err := testClient.TableRowsCount("books", RowsOptions{}) @@ -296,7 +299,7 @@ func test_TableRowsCount(t *testing.T) { assert.Equal(t, []Row{Row{count}}, res.Rows) } -func test_TableRowsCountWithLargeTable(t *testing.T) { +func testTableRowsCountWithLargeTable(t *testing.T) { var count int64 = 100010 testClient.db.MustExec(`create table large_table as select s from generate_Series(1,100010) s;`) testClient.db.MustExec(`VACUUM large_table;`) @@ -307,7 +310,7 @@ func test_TableRowsCountWithLargeTable(t *testing.T) { assert.Equal(t, []Row{Row{count}}, res.Rows) } -func test_TableIndexes(t *testing.T) { +func testTableIndexes(t *testing.T) { res, err := testClient.TableIndexes("books") assert.Equal(t, nil, err) @@ -315,7 +318,7 @@ func test_TableIndexes(t *testing.T) { assert.Equal(t, 2, len(res.Rows)) } -func test_TableConstraints(t *testing.T) { +func testTableConstraints(t *testing.T) { res, err := testClient.TableConstraints("editions") assert.Equal(t, nil, err) @@ -324,7 +327,7 @@ func test_TableConstraints(t *testing.T) { assert.Equal(t, Row{"integrity", "CHECK (book_id IS NOT NULL AND edition IS NOT NULL)"}, res.Rows[1]) } -func test_Query(t *testing.T) { +func testQuery(t *testing.T) { res, err := testClient.Query("SELECT * FROM books") assert.Equal(t, nil, err) @@ -332,7 +335,7 @@ func test_Query(t *testing.T) { assert.Equal(t, 15, len(res.Rows)) } -func test_QueryError(t *testing.T) { +func testQueryError(t *testing.T) { res, err := testClient.Query("SELCT * FROM books") assert.NotEqual(t, nil, err) @@ -340,7 +343,7 @@ func test_QueryError(t *testing.T) { assert.Equal(t, true, res == nil) } -func test_QueryInvalidTable(t *testing.T) { +func testQueryInvalidTable(t *testing.T) { res, err := testClient.Query("SELECT * FROM books2") assert.NotEqual(t, nil, err) @@ -348,7 +351,7 @@ func test_QueryInvalidTable(t *testing.T) { assert.Equal(t, true, res == nil) } -func test_TableRowsOrderEscape(t *testing.T) { +func testTableRowsOrderEscape(t *testing.T) { rows, err := testClient.TableRows("dummies", RowsOptions{SortColumn: "isDummy"}) assert.Equal(t, nil, err) assert.Equal(t, 2, len(rows.Rows)) @@ -359,7 +362,14 @@ func test_TableRowsOrderEscape(t *testing.T) { assert.Equal(t, true, rows == nil) } -func test_ResultCsv(t *testing.T) { +func testResultJSON(t *testing.T) { + result, err := testClient.Query("SELECT 'NaN'::float AS value;") + + assert.NoError(t, err) + assert.Equal(t, `[{"value":null}]`, string(result.JSON())) +} + +func testResultCsv(t *testing.T) { res, _ := testClient.Query("SELECT * FROM books ORDER BY id ASC LIMIT 1") csv := res.CSV() @@ -368,7 +378,7 @@ func test_ResultCsv(t *testing.T) { assert.Equal(t, expected, string(csv)) } -func test_History(t *testing.T) { +func testHistory(t *testing.T) { _, err := testClient.Query("SELECT * FROM books WHERE id = 12345") query := testClient.History[len(testClient.History)-1].Query @@ -376,7 +386,7 @@ func test_History(t *testing.T) { assert.Equal(t, "SELECT * FROM books WHERE id = 12345", query) } -func test_HistoryError(t *testing.T) { +func testHistoryError(t *testing.T) { _, err := testClient.Query("SELECT * FROM books123") query := testClient.History[len(testClient.History)-1].Query @@ -384,7 +394,7 @@ func test_HistoryError(t *testing.T) { assert.NotEqual(t, "SELECT * FROM books123", query) } -func test_HistoryUniqueness(t *testing.T) { +func testHistoryUniqueness(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, _ := NewFromUrl(url, nil) @@ -395,7 +405,7 @@ func test_HistoryUniqueness(t *testing.T) { assert.Equal(t, "SELECT * FROM books WHERE id = 1", client.History[0].Query) } -func test_ReadOnlyMode(t *testing.T) { +func testReadOnlyMode(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) client, _ := NewFromUrl(url, nil) @@ -419,31 +429,32 @@ func TestAll(t *testing.T) { setup() setupClient() - test_NewClientFromUrl(t) - test_ClientIdleTime(t) - test_Test(t) - test_Info(t) - test_Activity(t) - test_Databases(t) - test_Objects(t) - test_Table(t) - test_TableRows(t) - test_TableInfo(t) - test_EstimatedTableRowsCount(t) - test_TableRowsCount(t) - test_TableRowsCountWithLargeTable(t) - test_TableIndexes(t) - test_TableConstraints(t) - test_Query(t) - test_QueryError(t) - test_QueryInvalidTable(t) - test_TableRowsOrderEscape(t) - test_ResultCsv(t) - test_History(t) - test_HistoryUniqueness(t) - test_HistoryError(t) - test_ReadOnlyMode(t) - test_DumpExport(t) + testNewClientFromUrl(t) + testClientIdleTime(t) + testTest(t) + testInfo(t) + testActivity(t) + testDatabases(t) + testObjects(t) + testTable(t) + testTableRows(t) + testTableInfo(t) + testEstimatedTableRowsCount(t) + testTableRowsCount(t) + testTableRowsCountWithLargeTable(t) + testTableIndexes(t) + testTableConstraints(t) + testQuery(t) + testQueryError(t) + testQueryInvalidTable(t) + testTableRowsOrderEscape(t) + testResultJSON(t) + testResultCsv(t) + testHistory(t) + testHistoryUniqueness(t) + testHistoryError(t) + testReadOnlyMode(t) + testDumpExport(t) teardownClient() teardown() diff --git a/pkg/client/dump_test.go b/pkg/client/dump_test.go index 67c2a13..884bc35 100644 --- a/pkg/client/dump_test.go +++ b/pkg/client/dump_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" ) -func test_DumpExport(t *testing.T) { +func testDumpExport(t *testing.T) { url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) savePath := "/tmp/dump.sql.gz" diff --git a/pkg/client/result.go b/pkg/client/result.go index 066187b..96c9255 100644 --- a/pkg/client/result.go +++ b/pkg/client/result.go @@ -5,6 +5,7 @@ import ( "encoding/csv" "encoding/json" "fmt" + "math" "reflect" "strconv" "time" @@ -51,6 +52,14 @@ func (res *Result) PrepareBigints() { } case reflect.Float64: val := col.(float64) + + // json.Marshal panics when dealing with NaN/Inf values + // issue: https://github.com/golang/go/issues/25721 + if math.IsNaN(val) { + res.Rows[i][j] = nil + break + } + if val < -999999999999999 || val > 999999999999999 { res.Rows[i][j] = strconv.FormatFloat(val, 'e', -1, 64) }