Skip to content
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

feat: add sqlfields package #621

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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 .github/conventional_commits.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"sjsonx",
"snapshotx",
"sqlcon",
"sqlfields",
"sqlxx",
"stringslice",
"stringsx",
Expand Down
11 changes: 0 additions & 11 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3k
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
Expand Down Expand Up @@ -364,7 +363,6 @@ github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k=
github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
Expand Down Expand Up @@ -697,7 +695,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY=
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
Expand Down Expand Up @@ -757,7 +754,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
Expand Down Expand Up @@ -840,7 +836,6 @@ github.com/instana/go-sensor v1.41.1/go.mod h1:E42MelHWFz11qqaLwvgt0j98v2s2O/bq2
github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65 h1:T25FL3WEzgmKB0m6XCJNZ65nw09/QIp3T1yXr487D+A=
github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65/go.mod h1:nYhEREG/B7HUY7P+LKOrqy53TpIqmJ9JyUShcaEKtGw=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
Expand All @@ -867,7 +862,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
Expand Down Expand Up @@ -1003,7 +997,6 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
Expand Down Expand Up @@ -1404,7 +1397,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
Expand Down Expand Up @@ -1880,8 +1872,6 @@ golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
Expand Down Expand Up @@ -2173,7 +2163,6 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
30 changes: 29 additions & 1 deletion pointerx/pointerx.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
package pointerx

// Ptr returns the input value's pointer.
func Ptr[T any](v T) *T {
return &v
}

// Deref returns the input values de-referenced value, or zero value if nil.
func Deref[T any](p *T) T {
if p == nil {
var zero T
return zero
}
return *p
}

// String returns the input value's pointer.
// Deprecated: use Ptr instead.
func String(s string) *string {
return &s
}

// StringR is the reverse to String.
// Deprecated: use Deref instead.
func StringR(s *string) string {
if s == nil {
return ""
Expand All @@ -14,11 +30,13 @@ func StringR(s *string) string {
}

// Int returns the input value's pointer.
// Deprecated: use Ptr instead.
func Int(s int) *int {
return &s
}

// IntR is the reverse to Int.
// Deprecated: use Deref instead.
func IntR(s *int) int {
if s == nil {
return int(0)
Expand All @@ -27,11 +45,13 @@ func IntR(s *int) int {
}

// Int32 returns the input value's pointer.
func Int32(s int32) *int32 {
// Deprecated: use Ptr instead.
func Int32[T any](s int32) *int32 {
return &s
}

// Int32R is the reverse to Int32.
// Deprecated: use Deref instead.
func Int32R(s *int32) int32 {
if s == nil {
return int32(0)
Expand All @@ -40,11 +60,13 @@ func Int32R(s *int32) int32 {
}

// Int64 returns the input value's pointer.
// Deprecated: use Ptr instead.
func Int64(s int64) *int64 {
return &s
}

// Int64R is the reverse to Int64.
// Deprecated: use Deref instead.
func Int64R(s *int64) int64 {
if s == nil {
return int64(0)
Expand All @@ -53,11 +75,13 @@ func Int64R(s *int64) int64 {
}

// Float32 returns the input value's pointer.
// Deprecated: use Ptr instead.
func Float32(s float32) *float32 {
return &s
}

// Float32R is the reverse to Float32.
// Deprecated: use Deref instead.
func Float32R(s *float32) float32 {
if s == nil {
return float32(0)
Expand All @@ -66,11 +90,13 @@ func Float32R(s *float32) float32 {
}

// Float64 returns the input value's pointer.
// Deprecated: use Ptr instead.
func Float64(s float64) *float64 {
return &s
}

// Float64R is the reverse to Float64.
// Deprecated: use Deref instead.
func Float64R(s *float64) float64 {
if s == nil {
return float64(0)
Expand All @@ -79,11 +105,13 @@ func Float64R(s *float64) float64 {
}

// Bool returns the input value's pointer.
// Deprecated: use Ptr instead.
func Bool(s bool) *bool {
return &s
}

// BoolR is the reverse to Bool.
// Deprecated: use Deref instead.
func BoolR(s *bool) bool {
if s == nil {
return false
Expand Down
109 changes: 109 additions & 0 deletions sqlfields/base_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package sqlfields

import (
"encoding/json"
"fmt"
"testing"
"time"

"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func testJSONEncoding[T any](t *testing.T, value T, expectedJSON string, isNullable bool) {
t.Run(fmt.Sprintf("type=%T", value), func(t *testing.T) {
t.Run("case=marshal", func(t *testing.T) {
actual, err := json.Marshal(value)
require.NoError(t, err)
assert.JSONEq(t, expectedJSON, string(actual))
})
t.Run("case=unmarshal", func(t *testing.T) {
var other T
require.NoError(t, json.Unmarshal([]byte(expectedJSON), &other))
assert.EqualValues(t, value, other)
})
t.Run("case=null", func(t *testing.T) {
var actual, expected T
assert.NoError(t, json.Unmarshal([]byte("null"), &actual))
if _, ok := any(value).(JSONRawMessage); ok {
assert.Equal(t, JSONRawMessage("null"), actual)
} else {
assert.Equal(t, expected, actual)
}

raw, err := json.Marshal(expected)
require.NoError(t, err)
if !isNullable {
assert.NotEqual(t, "null", string(raw))
} else {
assert.Equal(t, "null", string(raw))
}
})
})
}

func TestJSONCompat(t *testing.T) {
testJSONEncoding(t, String("foo"), `"foo"`, false)
testJSONEncoding(t, Int(123), `123`, false)
testJSONEncoding(t, Int32(456), `456`, false)
testJSONEncoding(t, Int64(789), `789`, false)
testJSONEncoding(t, Float64(1.23), `1.23`, false)
testJSONEncoding(t, Bool(true), `true`, false)
testJSONEncoding(t, Duration(10*time.Second), `"10s"`, false)
testJSONEncoding(t, Time(time.Unix(123, 0).UTC()), `"1970-01-01T00:02:03Z"`, false)
testJSONEncoding(t, JSONRawMessage(`{"foo":"bar"}`), `{"foo":"bar"}`, true)
testJSONEncoding(t, StringSliceJSONFormat{"foo", "bar"}, `["foo","bar"]`, true)
testJSONEncoding(t, StringSlicePipeDelimiter{"foo", "bar"}, `["foo","bar"]`, true)
}

func testSQLCompatibility[T any](t *testing.T, db *sqlx.DB, value T) {
insertValue := func(t *testing.T, value T) int64 {
res, err := db.Exec(`INSERT INTO "testing" ("value") VALUES (?)`, value)
require.NoError(t, err)
id, err := res.LastInsertId()
require.NoError(t, err)
return id
}

t.Run(fmt.Sprintf("type=%T", value), func(t *testing.T) {
t.Run("case=insert and select", func(t *testing.T) {
var actual T
require.NoError(t, db.Get(&actual, `SELECT "value" FROM "testing" WHERE "id" = ?`, insertValue(t, value)))
assert.EqualValues(t, value, actual)
})
})
}

func TestSQLCompat(t *testing.T) {
db, err := sqlx.Connect("sqlite3", "file::memory:")
require.NoError(t, err)
defer db.Close()

// You have to hate the inconsistencies of SQLite. But for this test, it's great to have column that takes any data type.
_, err = db.Exec(`CREATE TABLE "testing" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"value" BLOB
)`)
require.NoError(t, err)

testSQLCompatibility(t, db, String("foo"))
testSQLCompatibility(t, db, Int(123))
testSQLCompatibility(t, db, Int32(456))
testSQLCompatibility(t, db, Int64(789))
testSQLCompatibility(t, db, Float64(1.23))
testSQLCompatibility(t, db, Bool(true))
testSQLCompatibility(t, db, Duration(10*time.Second))
testSQLCompatibility(t, db, Time(time.Unix(12345, 0).UTC()))
testSQLCompatibility(t, db, JSONRawMessage(`{"foo":"bar"}`))
testSQLCompatibility(t, db, StringSliceJSONFormat{"foo", "bar"})
testSQLCompatibility(t, db, StringSlicePipeDelimiter{"foo", "bar"})
}

func TestFactoryFuncs(t *testing.T) {
t.Run("case=JSONRawMessage", func(t *testing.T) {
assert.True(t, NewNullJSONRawMessage([]byte(`"foo"`)).Valid)
assert.False(t, NewNullJSONRawMessage(nil).Valid)
})
}
Loading