Skip to content

Change underlying data structure of resultSet #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/iancoleman/orderedmap v0.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
Expand Down
2 changes: 2 additions & 0 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
Expand Down
2 changes: 1 addition & 1 deletion src/sched_tasks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func TestSchedTasksWithStatement(t *testing.T) {
t.Error("did not succeed, but should have")
}

if fmt.Sprint(res.Results[0].ResultSet[0]["num"]) != "17" {
if fmt.Sprint(getDefault[float64](res.Results[0].ResultSet[0], "num")) != "17" {
t.Error("scheduled statement probably didn't execute")
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"github.com/iancoleman/orderedmap"
"sync"
)

Expand Down Expand Up @@ -129,11 +130,11 @@ type request struct {
// These are for generating the response

type responseItem struct {
Success bool `json:"success"`
RowsUpdated *int64 `json:"rowsUpdated,omitempty"`
RowsUpdatedBatch []int64 `json:"rowsUpdatedBatch,omitempty"`
ResultSet []map[string]interface{} `json:"resultSet,omitnil"` // omitnil is used by jettison
Error string `json:"error,omitempty"`
Success bool `json:"success"`
RowsUpdated *int64 `json:"rowsUpdated,omitempty"`
RowsUpdatedBatch []int64 `json:"rowsUpdatedBatch,omitempty"`
ResultSet []orderedmap.OrderedMap `json:"resultSet,omitnil"` // omitnil is used by jettison
Error string `json:"error,omitempty"`
}

type response struct {
Expand Down
11 changes: 11 additions & 0 deletions src/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"database/sql"
"encoding/json"
"github.com/iancoleman/orderedmap"
"github.com/mitchellh/go-homedir"
mllog "github.com/proofrock/go-mylittlelogger"
"os"
Expand Down Expand Up @@ -121,3 +122,13 @@ func splitOnColon(toSplit string) (string, string) {
}
return toSplit, ""
}

func getDefault[T any](m orderedmap.OrderedMap, key string) T {
value, ok := m.Get(key)
if !ok {
var t T
return t
}

return value.(T)
}
18 changes: 10 additions & 8 deletions src/web_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"database/sql"
"errors"
"github.com/iancoleman/orderedmap"
"strings"
"time"

Expand Down Expand Up @@ -74,20 +75,21 @@ func encrypt(encoder requestItemCrypto, values map[string]interface{}) error {
}

// Scans the results from a db request and decrypts them as needed
func decrypt(decoder requestItemCrypto, results map[string]interface{}) error {
func decrypt(decoder requestItemCrypto, results *orderedmap.OrderedMap) error {
if decoder.CompressionLevel > 0 {
return errors.New("cannot specify compression level for decryption")
}
for i := range decoder.Fields {
sval, ok := results[decoder.Fields[i]].(string)
// sval, ok := results[decoder.Fields[i]].(string)
sval, ok := results.Get(decoder.Fields[i])
if !ok {
return errors.New("attempting to decrypt a non-string field")
}
dval, err := crypgo.Decrypt(decoder.Password, sval)
dval, err := crypgo.Decrypt(decoder.Password, sval.(string))
if err != nil {
return err
}
results[decoder.Fields[i]] = dval
results.Set(decoder.Fields[i], dval)
}
return nil
}
Expand All @@ -106,7 +108,7 @@ func reportError(err error, code int, reqIdx int, noFail bool, results []respons
//
// This method is needed to execute properly the defers.
func processWithResultSet(tx *sql.Tx, query string, decoder *requestItemCrypto, values map[string]interface{}) (*responseItem, error) {
resultSet := make([]map[string]interface{}, 0)
resultSet := make([]orderedmap.OrderedMap, 0)

rows, err := tx.Query(query, vals2nameds(values)...)
if err != nil {
Expand All @@ -125,17 +127,17 @@ func processWithResultSet(tx *sql.Tx, query string, decoder *requestItemCrypto,
return nil, err
}

toAdd := make(map[string]interface{})
toAdd := orderedmap.New()
for i := range values {
toAdd[fields[i]] = values[i]
toAdd.Set(fields[i], values[i])
}

if decoder != nil {
if err := decrypt(*decoder, toAdd); err != nil {
return nil, err
}
}
resultSet = append(resultSet, toAdd)
resultSet = append(resultSet, *toAdd)
}

if err = rows.Err(); err != nil {
Expand Down
81 changes: 68 additions & 13 deletions src/ws4sqlite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"github.com/gofiber/fiber/v2"
"os"
"slices"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -111,7 +112,7 @@ func TestSetup(t *testing.T) {
{
Id: "test",
Path: "../test/test.db",
//DisableWALMode: true,
// DisableWALMode: true,
StoredStatement: []storedStatement{
{
Id: "Q",
Expand Down Expand Up @@ -224,7 +225,7 @@ func TestTx(t *testing.T) {
t.Error("req 1 inconsistent")
}

if !res.Results[2].Success || res.Results[2].ResultSet[0]["VAL"] != "ONE" {
if !res.Results[2].Success || getDefault[string](res.Results[2].ResultSet[0], "VAL") != "ONE" {
t.Error("req 2 inconsistent")
}

Expand Down Expand Up @@ -367,7 +368,7 @@ func TestConcurrent(t *testing.T) {
t.Error("req 1 inconsistent")
}

if !res.Results[2].Success || res.Results[2].ResultSet[0]["VAL"] != "ONE" {
if !res.Results[2].Success || getDefault[string](res.Results[2].ResultSet[0], "VAL") != "ONE" {
t.Error("req 2 inconsistent")
}

Expand All @@ -387,6 +388,60 @@ func TestConcurrent(t *testing.T) {
wg.Wait()
}

func TestResultSetOrder(t *testing.T) {
// See this issue for more context: https://github.com/proofrock/sqliterg/issues/5
req := request{
Transaction: []requestItem{
{
Query: "CREATE TABLE table_with_many_columns (d INT, c INT, b INT, a INT)",
},
{
Query: "INSERT INTO table_with_many_columns VALUES (4, 3, 2, 1)",
},
{
Query: "SELECT * FROM table_with_many_columns",
},
},
}
code, _, res := call("test", req, t)

if code != 200 {
t.Error("did not succeed")
return
}

if !res.Results[0].Success ||
!res.Results[1].Success ||
!res.Results[2].Success {
t.Error("did not succeed")
return
}

queryResult := res.Results[2].ResultSet[0]
expectedKeys := []string{"d", "c", "b", "a"}
if !slices.Equal(
queryResult.Keys(),
expectedKeys,
) {
t.Error("should have the right order")
return
}

expectedValues := []float64{4, 3, 2, 1}
for i, key := range expectedKeys {
value, ok := queryResult.Get(key)
if !ok {
t.Error("unreachable code")
return
}
expectedValue := expectedValues[i]
if value != expectedValue {
t.Error("wrong value")
return
}
}
}

// don't remove the file, we'll use it for the next tests for read-only
func TestTeardown(t *testing.T) {
time.Sleep(time.Second)
Expand All @@ -403,7 +458,7 @@ func TestSetupRO(t *testing.T) {
{
Id: "test",
Path: "../test/test.db",
//DisableWALMode: true,
// DisableWALMode: true,
ReadOnly: true,
StoredStatement: []storedStatement{
{
Expand Down Expand Up @@ -451,7 +506,7 @@ func TestOkRO(t *testing.T) {
return
}

if !res.Results[0].Success || res.Results[0].ResultSet[3]["VAL"] != "FOUR" {
if !res.Results[0].Success || getDefault[string](res.Results[0].ResultSet[3], "VAL") != "FOUR" {
t.Error("req is inconsistent")
}
}
Expand All @@ -478,7 +533,7 @@ func TestConcurrentRO(t *testing.T) {
return
}

if !res.Results[0].Success || res.Results[0].ResultSet[3]["VAL"] != "FOUR" {
if !res.Results[0].Success || getDefault[string](res.Results[0].ResultSet[3], "VAL") != "FOUR" {
t.Error("req is inconsistent")
}
}(t)
Expand All @@ -501,7 +556,7 @@ func TestSetupSQO(t *testing.T) {
{
Id: "test",
Path: "../test/test.db",
//DisableWALMode: true,
// DisableWALMode: true,
ReadOnly: true,
UseOnlyStoredStatements: true,
StoredStatement: []storedStatement{
Expand Down Expand Up @@ -569,7 +624,7 @@ func TestSetupMEM(t *testing.T) {
{
Id: "test",
Path: ":memory:",
//DisableWALMode: true,
// DisableWALMode: true,
StoredStatement: []storedStatement{
{
Id: "Q",
Expand Down Expand Up @@ -640,7 +695,7 @@ func TestSetupMEM_RO(t *testing.T) {
Id: "test",
Path: ":memory:",
ReadOnly: true,
//DisableWALMode: true,
// DisableWALMode: true,
StoredStatement: []storedStatement{
{
Id: "Q",
Expand Down Expand Up @@ -689,7 +744,7 @@ func TestSetupWITH_ADD_PROPS(t *testing.T) {
{
Id: "test",
Path: "file::memory:",
//DisableWALMode: true,
// DisableWALMode: true,
StoredStatement: []storedStatement{
{
Id: "Q",
Expand Down Expand Up @@ -740,7 +795,7 @@ func TestRO_MEM_IS(t *testing.T) {
Id: "test",
Path: ":memory:",
ReadOnly: true,
//DisableWALMode: true,
// DisableWALMode: true,
InitStatements: []string{
"CREATE TABLE T1 (ID INT)",
},
Expand All @@ -767,7 +822,7 @@ func Test_IS_Err(t *testing.T) {
{
Id: "test",
Path: ":memory:",
//DisableWALMode: true,
// DisableWALMode: true,
InitStatements: []string{
"CREATE TABLE T1 (ID INT)",
"CREATE TABLE T1 (ID INT)",
Expand Down Expand Up @@ -1213,7 +1268,7 @@ func TestUnicode(t *testing.T) {
if code != 200 {
t.Error("SELECT failed", body)
}
if res.Results[0].ResultSet[0]["TXT"] != "世界" {
if getDefault[string](res.Results[0].ResultSet[0], "TXT") != "世界" {
t.Error("Unicode extraction failed", body)
}

Expand Down