Skip to content

Commit c1530af

Browse files
committed
#43: Option to return results as a list, instead of a map
1 parent b965ec6 commit c1530af

5 files changed

Lines changed: 45 additions & 55 deletions

File tree

src/sched_tasks_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ package main
1818

1919
import (
2020
"fmt"
21+
"github.com/robfig/cron/v3"
2122
"os"
2223
"path/filepath"
2324
"testing"
2425
"time"
25-
26-
"github.com/robfig/cron/v3"
2726
)
2827

2928
// The post-0.14 "scheduledTasks" structure is only actually tested in TestAtStartupMultiple, but the contents of the
@@ -237,7 +236,7 @@ func TestSchedTasksWithStatement(t *testing.T) {
237236
t.Error("did not succeed, but should have")
238237
}
239238

240-
if fmt.Sprint(res.Results[0].ResultSet[0].(map[string]interface{})["num"]) != "17" {
239+
if fmt.Sprint(getDefault[float64](res.Results[0].ResultSet[0], "num")) != "17" {
241240
t.Error("scheduled statement probably didn't execute")
242241
}
243242
}

src/structs.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"encoding/json"
2222
"fmt"
2323
"sync"
24+
25+
"github.com/iancoleman/orderedmap"
2426
)
2527

2628
// This is the ws4sqlite error type
@@ -127,12 +129,13 @@ type requestParams struct {
127129
// These are for generating the response
128130

129131
type responseItem struct {
130-
Success bool `json:"success"`
131-
RowsUpdated *int64 `json:"rowsUpdated,omitempty"`
132-
RowsUpdatedBatch []int64 `json:"rowsUpdatedBatch,omitempty"`
133-
ResultHeaders []string `json:"resultHeaders,omitempty"`
134-
ResultSet []interface{} `json:"resultSet,omitnil"` // omitnil is used by jettison
135-
Error string `json:"error,omitempty"`
132+
Success bool `json:"success"`
133+
RowsUpdated *int64 `json:"rowsUpdated,omitempty"`
134+
RowsUpdatedBatch []int64 `json:"rowsUpdatedBatch,omitempty"`
135+
ResultHeaders []string `json:"resultHeaders,omitempty"`
136+
ResultSet []orderedmap.OrderedMap `json:"resultSet,omitnil"` // omitnil is used by jettison
137+
ResultSetList [][]interface{} `json:"resultSetList,omitnil"` // omitnil is used by jettison
138+
Error string `json:"error,omitempty"`
136139
}
137140

138141
type response struct {

src/utils.go

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ import (
2121
"database/sql"
2222
"encoding/json"
2323
"errors"
24-
"github.com/iancoleman/orderedmap"
25-
"github.com/mitchellh/go-homedir"
26-
mllog "github.com/proofrock/go-mylittlelogger"
2724
"os"
2825
"path/filepath"
2926
"slices"
3027
"strings"
28+
29+
"github.com/iancoleman/orderedmap"
30+
"github.com/mitchellh/go-homedir"
31+
mllog "github.com/proofrock/go-mylittlelogger"
3132
)
3233

3334
// Uppercases (?) the first letter of a string
@@ -57,20 +58,6 @@ func dirExists(dirname string) bool {
5758
return info.IsDir()
5859
}
5960

60-
// Maps the raw JSON messages to a proper map, to manage unstructured JSON parsing;
61-
// see https://noamt.medium.com/using-gos-json-rawmessage-a2371a1c11b7
62-
func raw2vals(raw map[string]json.RawMessage) (map[string]interface{}, error) {
63-
ret := make(map[string]interface{})
64-
for key, rawVal := range raw {
65-
var val interface{}
66-
if err := json.Unmarshal(rawVal, &val); err != nil {
67-
return nil, err
68-
}
69-
ret[key] = val
70-
}
71-
return ret, nil
72-
}
73-
7461
// Maps named parameters to the proper sql type, needed to use named params
7562
func vals2nameds(vals map[string]interface{}) []interface{} {
7663
var nameds []interface{}
@@ -82,7 +69,7 @@ func vals2nameds(vals map[string]interface{}) []interface{} {
8269

8370
func isEmptyRaw(raw json.RawMessage) bool {
8471
// the last check is for `null`
85-
return raw == nil || len(raw) == 0 || slices.Equal(raw, []byte{110, 117, 108, 108})
72+
return len(raw) == 0 || slices.Equal(raw, []byte{110, 117, 108, 108})
8673
}
8774

8875
func raw2params(raw json.RawMessage) (*requestParams, error) {

src/web_service.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,15 @@ func reportError(err error, code int, reqIdx int, noFail bool, results []respons
5757
if !noFail {
5858
panic(newWSError(reqIdx, code, err.Error()))
5959
}
60-
results[reqIdx] = responseItem{false, nil, nil, nil, nil, capitalize(err.Error())}
60+
results[reqIdx] = responseItem{false, nil, nil, nil, nil, nil, capitalize(err.Error())}
6161
}
6262

6363
// Processes a query, and returns a suitable responseItem
6464
//
6565
// This method is needed to execute properly the defers.
6666
func processWithResultSet(tx *sql.Tx, query string, isListResultSet bool, params requestParams) (*responseItem, error) {
67-
resultSet := make([]interface{}, 0)
67+
resultSet := make([]orderedmap.OrderedMap, 0)
68+
resultSetList := make([][]interface{}, 0)
6869

6970
rows := (*sql.Rows)(nil)
7071
err := (error)(nil)
@@ -80,10 +81,10 @@ func processWithResultSet(tx *sql.Tx, query string, isListResultSet bool, params
8081
}
8182
defer rows.Close()
8283

83-
fields, _ := rows.Columns() // I can ignore the error, rows aren't closed
84+
headers, _ := rows.Columns() // I can ignore the error, rows aren't closed
8485
for rows.Next() {
85-
values := make([]interface{}, len(fields)) // values of the various fields
86-
scans := make([]interface{}, len(fields)) // pointers to the values, to pass to Scan()
86+
values := make([]interface{}, len(headers)) // values of the various fields
87+
scans := make([]interface{}, len(headers)) // pointers to the values, to pass to Scan()
8788
for i := range values {
8889
scans[i] = &values[i]
8990
}
@@ -93,16 +94,14 @@ func processWithResultSet(tx *sql.Tx, query string, isListResultSet bool, params
9394

9495
if isListResultSet {
9596
// List-style result set
96-
toAdd := make([]interface{}, 0)
97-
for i := range values {
98-
toAdd = append(toAdd, values[i])
99-
}
100-
resultSet = append(resultSet, toAdd)
97+
98+
resultSetList = append(resultSetList, values)
10199
} else {
102100
// Map-style result set
101+
103102
toAdd := orderedmap.New()
104103
for i := range values {
105-
toAdd.Set(fields[i], values[i])
104+
toAdd.Set(headers[i], values[i])
106105
}
107106

108107
resultSet = append(resultSet, *toAdd)
@@ -113,7 +112,10 @@ func processWithResultSet(tx *sql.Tx, query string, isListResultSet bool, params
113112
return nil, err
114113
}
115114

116-
return &responseItem{true, nil, nil, fields, resultSet, ""}, nil
115+
if isListResultSet {
116+
return &responseItem{true, nil, nil, headers, nil, resultSetList, ""}, nil
117+
}
118+
return &responseItem{true, nil, nil, headers, resultSet, nil, ""}, nil
117119
}
118120

119121
// Process a single statement, and returns a suitable responseItem
@@ -136,7 +138,7 @@ func processForExec(tx *sql.Tx, statement string, params requestParams) (*respon
136138
return nil, err
137139
}
138140

139-
return &responseItem{true, &rowsUpdated, nil, nil, nil, ""}, nil
141+
return &responseItem{true, &rowsUpdated, nil, nil, nil, nil, ""}, nil
140142
}
141143

142144
// Process a batch statement, and returns a suitable responseItem.
@@ -171,7 +173,7 @@ func processForExecBatch(tx *sql.Tx, q string, paramsBatch []requestParams) (*re
171173
rowsUpdatedBatch = append(rowsUpdatedBatch, rowsUpdated)
172174
}
173175

174-
return &responseItem{true, nil, rowsUpdatedBatch, nil, nil, ""}, nil
176+
return &responseItem{true, nil, rowsUpdatedBatch, nil, nil, nil, ""}, nil
175177
}
176178

177179
func ckSQL(sql string) string {

src/ws4sqlite_test.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func callBA(databaseId string, req request, user, password string, t *testing.T)
7878

7979
code, bodyBytes, errs := post.Bytes()
8080

81-
if errs != nil && len(errs) > 0 {
81+
if len(errs) > 0 {
8282
t.Error(errs[0])
8383
}
8484

@@ -222,7 +222,7 @@ func TestTx(t *testing.T) {
222222
t.Error("req 1 inconsistent")
223223
}
224224

225-
if !res.Results[2].Success || res.Results[2].ResultSet[0].(map[string]interface{})["VAL"] != "ONE" {
225+
if !res.Results[2].Success || getDefault[string](res.Results[2].ResultSet[0], "VAL") != "ONE" {
226226
t.Error("req 2 inconsistent")
227227
}
228228

@@ -365,7 +365,7 @@ func TestConcurrent(t *testing.T) {
365365
t.Error("req 1 inconsistent")
366366
}
367367

368-
if !res.Results[2].Success || res.Results[2].ResultSet[0].(map[string]interface{})["VAL"] != "ONE" {
368+
if !res.Results[2].Success || getDefault[string](res.Results[2].ResultSet[0], "VAL") != "ONE" {
369369
t.Error("req 2 inconsistent")
370370
}
371371

@@ -418,11 +418,10 @@ func TestResultSetOrder(t *testing.T) {
418418
return
419419
}
420420

421-
queryResult := res.Results[2].ResultSet[0].(map[string]interface{})
422-
queryResultHeaders := res.Results[2].ResultHeaders
421+
queryResult := res.Results[2].ResultSet[0]
423422
expectedKeys := []string{"d", "c", "b", "a"}
424423
if !slices.Equal(
425-
queryResultHeaders,
424+
queryResult.Keys(),
426425
expectedKeys,
427426
) {
428427
t.Error("should have the right order")
@@ -431,7 +430,7 @@ func TestResultSetOrder(t *testing.T) {
431430

432431
expectedValues := []float64{4, 3, 2, 1}
433432
for i, key := range expectedKeys {
434-
value, ok := queryResult[key]
433+
value, ok := queryResult.Get(key)
435434
if !ok {
436435
t.Error("unreachable code")
437436
return
@@ -480,7 +479,7 @@ func TestListResultSet(t *testing.T) {
480479
return
481480
}
482481

483-
queryResult := res.Results[2].ResultSet[0].([]interface{})
482+
queryResult := res.Results[2].ResultSetList[0]
484483
queryResultHeaders := res.Results[2].ResultHeaders
485484
expectedKeys := []string{"d", "c", "b", "a"}
486485
if !slices.Equal(
@@ -610,7 +609,7 @@ func TestOkRO(t *testing.T) {
610609
return
611610
}
612611

613-
if !res.Results[0].Success || res.Results[0].ResultSet[3].(map[string]interface{})["VAL"] != "FOUR" {
612+
if !res.Results[0].Success || getDefault[string](res.Results[0].ResultSet[3], "VAL") != "FOUR" {
614613
t.Error("req is inconsistent")
615614
}
616615
}
@@ -637,7 +636,7 @@ func TestConcurrentRO(t *testing.T) {
637636
return
638637
}
639638

640-
if !res.Results[0].Success || res.Results[0].ResultSet[3].(map[string]interface{})["VAL"] != "FOUR" {
639+
if !res.Results[0].Success || getDefault[string](res.Results[0].ResultSet[3], "VAL") != "FOUR" {
641640
t.Error("req is inconsistent")
642641
}
643642
}(t)
@@ -1372,7 +1371,7 @@ func TestUnicode(t *testing.T) {
13721371
if code != 200 {
13731372
t.Error("SELECT failed", body)
13741373
}
1375-
if res.Results[0].ResultSet[0].(map[string]interface{})["TXT"] != "世界" {
1374+
if getDefault[string](res.Results[0].ResultSet[0], "TXT") != "世界" {
13761375
t.Error("Unicode extraction failed", body)
13771376
}
13781377

@@ -1499,7 +1498,7 @@ func TestFileServer(t *testing.T) {
14991498

15001499
code, _, errs := get.String()
15011500

1502-
if errs != nil && len(errs) > 0 {
1501+
if len(errs) > 0 {
15031502
t.Error(errs[0])
15041503
}
15051504

@@ -1526,7 +1525,7 @@ func TestFileServerKO(t *testing.T) {
15261525

15271526
code, _, errs := get.String()
15281527

1529-
if errs != nil && len(errs) > 0 {
1528+
if len(errs) > 0 {
15301529
t.Error(errs[0])
15311530
}
15321531

@@ -1553,7 +1552,7 @@ func TestFileServerWithOverlap(t *testing.T) {
15531552

15541553
code, _, errs := get.String()
15551554

1556-
if errs != nil && len(errs) > 0 {
1555+
if len(errs) > 0 {
15571556
t.Error(errs[0])
15581557
}
15591558

0 commit comments

Comments
 (0)