Fix JSON marshal panic when dealing with NaN values
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user