Fix JSON marshal panic when dealing with NaN values

This commit is contained in:
Dan Sosedoff
2018-11-27 16:51:51 -06:00
parent 3850eedf07
commit 903a265676
3 changed files with 73 additions and 53 deletions

View File

@@ -9,9 +9,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/command"
"github.com/stretchr/testify/assert"
) )
var ( var (
@@ -78,6 +78,9 @@ func onWindows() bool {
} }
func setup() { func setup() {
// No pretty JSON for testsm
command.Opts.DisablePrettyJson = true
out, err := exec.Command( out, err := exec.Command(
testCommands["createdb"], testCommands["createdb"],
"-U", serverUser, "-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) url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase)
client, err := NewFromUrl(url, nil) client, err := NewFromUrl(url, nil)
@@ -145,7 +148,7 @@ func test_NewClientFromUrl(t *testing.T) {
assert.Equal(t, url, client.ConnectionString) 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) url := fmt.Sprintf("postgresql://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase)
client, err := NewFromUrl(url, nil) client, err := NewFromUrl(url, nil)
@@ -157,7 +160,7 @@ func test_NewClientFromUrl2(t *testing.T) {
assert.Equal(t, url, client.ConnectionString) assert.Equal(t, url, client.ConnectionString)
} }
func test_ClientIdleTime(t *testing.T) { func testClientIdleTime(t *testing.T) {
examples := map[time.Time]bool{ examples := map[time.Time]bool{
time.Now(): false, // Current time time.Now(): false, // Current time
time.Now().Add(time.Minute * -30): false, // 30 minutes ago 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()) assert.Equal(t, nil, testClient.Test())
} }
func test_Info(t *testing.T) { func testInfo(t *testing.T) {
res, err := testClient.Info() res, err := testClient.Info()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.NotEqual(t, nil, res) assert.NotEqual(t, nil, res)
} }
func test_Activity(t *testing.T) { func testActivity(t *testing.T) {
res, err := testClient.Activity() res, err := testClient.Activity()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.NotEqual(t, nil, res) assert.NotEqual(t, nil, res)
} }
func test_Databases(t *testing.T) { func testDatabases(t *testing.T) {
res, err := testClient.Databases() res, err := testClient.Databases()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@@ -198,7 +201,7 @@ func test_Databases(t *testing.T) {
assert.Contains(t, res, "postgres") assert.Contains(t, res, "postgres")
} }
func test_Objects(t *testing.T) { func testObjects(t *testing.T) {
res, err := testClient.Objects() res, err := testClient.Objects()
objects := ObjectsFromResult(res) 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") res, err := testClient.Table("books")
columns := []string{ columns := []string{
@@ -262,7 +265,7 @@ func test_Table(t *testing.T) {
assert.Equal(t, 4, len(res.Rows)) assert.Equal(t, 4, len(res.Rows))
} }
func test_TableRows(t *testing.T) { func testTableRows(t *testing.T) {
res, err := testClient.TableRows("books", RowsOptions{}) res, err := testClient.TableRows("books", RowsOptions{})
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@@ -270,7 +273,7 @@ func test_TableRows(t *testing.T) {
assert.Equal(t, 15, len(res.Rows)) assert.Equal(t, 15, len(res.Rows))
} }
func test_TableInfo(t *testing.T) { func testTableInfo(t *testing.T) {
res, err := testClient.TableInfo("books") res, err := testClient.TableInfo("books")
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@@ -278,7 +281,7 @@ func test_TableInfo(t *testing.T) {
assert.Equal(t, 1, len(res.Rows)) assert.Equal(t, 1, len(res.Rows))
} }
func test_EstimatedTableRowsCount(t *testing.T) { func testEstimatedTableRowsCount(t *testing.T) {
var count int64 = 15 var count int64 = 15
res, err := testClient.EstimatedTableRowsCount("books", RowsOptions{}) res, err := testClient.EstimatedTableRowsCount("books", RowsOptions{})
@@ -287,7 +290,7 @@ func test_EstimatedTableRowsCount(t *testing.T) {
assert.Equal(t, []Row{Row{count}}, res.Rows) assert.Equal(t, []Row{Row{count}}, res.Rows)
} }
func test_TableRowsCount(t *testing.T) { func testTableRowsCount(t *testing.T) {
var count int64 = 15 var count int64 = 15
res, err := testClient.TableRowsCount("books", RowsOptions{}) res, err := testClient.TableRowsCount("books", RowsOptions{})
@@ -296,7 +299,7 @@ func test_TableRowsCount(t *testing.T) {
assert.Equal(t, []Row{Row{count}}, res.Rows) assert.Equal(t, []Row{Row{count}}, res.Rows)
} }
func test_TableRowsCountWithLargeTable(t *testing.T) { func testTableRowsCountWithLargeTable(t *testing.T) {
var count int64 = 100010 var count int64 = 100010
testClient.db.MustExec(`create table large_table as select s from generate_Series(1,100010) s;`) testClient.db.MustExec(`create table large_table as select s from generate_Series(1,100010) s;`)
testClient.db.MustExec(`VACUUM large_table;`) testClient.db.MustExec(`VACUUM large_table;`)
@@ -307,7 +310,7 @@ func test_TableRowsCountWithLargeTable(t *testing.T) {
assert.Equal(t, []Row{Row{count}}, res.Rows) assert.Equal(t, []Row{Row{count}}, res.Rows)
} }
func test_TableIndexes(t *testing.T) { func testTableIndexes(t *testing.T) {
res, err := testClient.TableIndexes("books") res, err := testClient.TableIndexes("books")
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@@ -315,7 +318,7 @@ func test_TableIndexes(t *testing.T) {
assert.Equal(t, 2, len(res.Rows)) assert.Equal(t, 2, len(res.Rows))
} }
func test_TableConstraints(t *testing.T) { func testTableConstraints(t *testing.T) {
res, err := testClient.TableConstraints("editions") res, err := testClient.TableConstraints("editions")
assert.Equal(t, nil, err) 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]) 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") res, err := testClient.Query("SELECT * FROM books")
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@@ -332,7 +335,7 @@ func test_Query(t *testing.T) {
assert.Equal(t, 15, len(res.Rows)) 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") res, err := testClient.Query("SELCT * FROM books")
assert.NotEqual(t, nil, err) assert.NotEqual(t, nil, err)
@@ -340,7 +343,7 @@ func test_QueryError(t *testing.T) {
assert.Equal(t, true, res == nil) assert.Equal(t, true, res == nil)
} }
func test_QueryInvalidTable(t *testing.T) { func testQueryInvalidTable(t *testing.T) {
res, err := testClient.Query("SELECT * FROM books2") res, err := testClient.Query("SELECT * FROM books2")
assert.NotEqual(t, nil, err) assert.NotEqual(t, nil, err)
@@ -348,7 +351,7 @@ func test_QueryInvalidTable(t *testing.T) {
assert.Equal(t, true, res == nil) 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"}) rows, err := testClient.TableRows("dummies", RowsOptions{SortColumn: "isDummy"})
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 2, len(rows.Rows)) assert.Equal(t, 2, len(rows.Rows))
@@ -359,7 +362,14 @@ func test_TableRowsOrderEscape(t *testing.T) {
assert.Equal(t, true, rows == nil) 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") res, _ := testClient.Query("SELECT * FROM books ORDER BY id ASC LIMIT 1")
csv := res.CSV() csv := res.CSV()
@@ -368,7 +378,7 @@ func test_ResultCsv(t *testing.T) {
assert.Equal(t, expected, string(csv)) 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") _, err := testClient.Query("SELECT * FROM books WHERE id = 12345")
query := testClient.History[len(testClient.History)-1].Query 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) 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") _, err := testClient.Query("SELECT * FROM books123")
query := testClient.History[len(testClient.History)-1].Query 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) 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) url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase)
client, _ := NewFromUrl(url, nil) 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) 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) url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase)
client, _ := NewFromUrl(url, nil) client, _ := NewFromUrl(url, nil)
@@ -419,31 +429,32 @@ func TestAll(t *testing.T) {
setup() setup()
setupClient() setupClient()
test_NewClientFromUrl(t) testNewClientFromUrl(t)
test_ClientIdleTime(t) testClientIdleTime(t)
test_Test(t) testTest(t)
test_Info(t) testInfo(t)
test_Activity(t) testActivity(t)
test_Databases(t) testDatabases(t)
test_Objects(t) testObjects(t)
test_Table(t) testTable(t)
test_TableRows(t) testTableRows(t)
test_TableInfo(t) testTableInfo(t)
test_EstimatedTableRowsCount(t) testEstimatedTableRowsCount(t)
test_TableRowsCount(t) testTableRowsCount(t)
test_TableRowsCountWithLargeTable(t) testTableRowsCountWithLargeTable(t)
test_TableIndexes(t) testTableIndexes(t)
test_TableConstraints(t) testTableConstraints(t)
test_Query(t) testQuery(t)
test_QueryError(t) testQueryError(t)
test_QueryInvalidTable(t) testQueryInvalidTable(t)
test_TableRowsOrderEscape(t) testTableRowsOrderEscape(t)
test_ResultCsv(t) testResultJSON(t)
test_History(t) testResultCsv(t)
test_HistoryUniqueness(t) testHistory(t)
test_HistoryError(t) testHistoryUniqueness(t)
test_ReadOnlyMode(t) testHistoryError(t)
test_DumpExport(t) testReadOnlyMode(t)
testDumpExport(t)
teardownClient() teardownClient()
teardown() teardown()

View File

@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/assert" "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) url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase)
savePath := "/tmp/dump.sql.gz" savePath := "/tmp/dump.sql.gz"

View File

@@ -5,6 +5,7 @@ import (
"encoding/csv" "encoding/csv"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math"
"reflect" "reflect"
"strconv" "strconv"
"time" "time"
@@ -51,6 +52,14 @@ func (res *Result) PrepareBigints() {
} }
case reflect.Float64: case reflect.Float64:
val := col.(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 { if val < -999999999999999 || val > 999999999999999 {
res.Rows[i][j] = strconv.FormatFloat(val, 'e', -1, 64) res.Rows[i][j] = strconv.FormatFloat(val, 'e', -1, 64)
} }