Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
61801ff20d
|
|||
361dca5c85
|
|||
9f85a243e8
|
|||
dc6cb274ee
|
|||
f6b47792a4
|
|||
295b3ef793
|
|||
721c176337
|
|||
ebba6545a3
|
|||
19c7e22ced
|
|||
9f883b458f
|
|||
1f456c5134
|
|||
d7fbef37db
|
|||
a1668b6e5a
|
|||
3a17edfaf0
|
|||
3320a9c19d
|
|||
8dcd8a270a
|
|||
03a9b276d8
|
@@ -20,6 +20,7 @@ Potentially needs `export GOPRIVATE="gogs.mikescher.com"`
|
||||
| zipext | Mike | Utility for zip/gzip/tar etc |
|
||||
| reflectext | Mike | Utility for golang reflection |
|
||||
| fsext | Mike | Utility for filesytem access |
|
||||
| ctxext | Mike | Utility for context.Context |
|
||||
| | | |
|
||||
| mongoext | Mike | Utility/Helper functions for mongodb (kinda abandoned) |
|
||||
| cursortoken | Mike | MongoDB cursortoken implementation |
|
||||
|
@@ -46,7 +46,7 @@ var rexEnumPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]
|
||||
|
||||
var rexEnumDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*//\s*(@enum:type).*$`))
|
||||
|
||||
var rexEnumValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:\s\-.]+"|[0-9]+))\s*(//(?P<comm>.*))?.*$`))
|
||||
var rexEnumValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:\s\-.]*"|[0-9]+))\s*(//(?P<comm>.*))?.*$`))
|
||||
|
||||
var rexEnumChecksumConst = rext.W(regexp.MustCompile(`const ChecksumEnumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
||||
|
||||
|
27
ctxext/getter.go
Normal file
27
ctxext/getter.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package ctxext
|
||||
|
||||
import "context"
|
||||
|
||||
func Value[T any](ctx context.Context, key any) (T, bool) {
|
||||
v := ctx.Value(key)
|
||||
if v == nil {
|
||||
return *new(T), false
|
||||
}
|
||||
if tv, ok := v.(T); !ok {
|
||||
return *new(T), false
|
||||
} else {
|
||||
return tv, true
|
||||
}
|
||||
}
|
||||
|
||||
func ValueOrDefault[T any](ctx context.Context, key any, def T) T {
|
||||
v := ctx.Value(key)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
if tv, ok := v.(T); !ok {
|
||||
return def
|
||||
} else {
|
||||
return tv
|
||||
}
|
||||
}
|
@@ -57,3 +57,11 @@ func (m JsonOpt[T]) MustValue() T {
|
||||
}
|
||||
return m.value
|
||||
}
|
||||
|
||||
func (m JsonOpt[T]) IfSet(fn func(v T)) bool {
|
||||
if !m.isSet {
|
||||
return false
|
||||
}
|
||||
fn(m.value)
|
||||
return true
|
||||
}
|
||||
|
@@ -19,6 +19,14 @@ func (s Single[T1]) TupleValues() []any {
|
||||
return []any{s.V1}
|
||||
}
|
||||
|
||||
func NewSingle[T1 any](v1 T1) Single[T1] {
|
||||
return Single[T1]{V1: v1}
|
||||
}
|
||||
|
||||
func NewTuple1[T1 any](v1 T1) Single[T1] {
|
||||
return Single[T1]{V1: v1}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Tuple[T1 any, T2 any] struct {
|
||||
@@ -34,6 +42,14 @@ func (t Tuple[T1, T2]) TupleValues() []any {
|
||||
return []any{t.V1, t.V2}
|
||||
}
|
||||
|
||||
func NewTuple[T1 any, T2 any](v1 T1, v2 T2) Tuple[T1, T2] {
|
||||
return Tuple[T1, T2]{V1: v1, V2: v2}
|
||||
}
|
||||
|
||||
func NewTuple2[T1 any, T2 any](v1 T1, v2 T2) Tuple[T1, T2] {
|
||||
return Tuple[T1, T2]{V1: v1, V2: v2}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Triple[T1 any, T2 any, T3 any] struct {
|
||||
@@ -50,6 +66,14 @@ func (t Triple[T1, T2, T3]) TupleValues() []any {
|
||||
return []any{t.V1, t.V2, t.V3}
|
||||
}
|
||||
|
||||
func NewTriple[T1 any, T2 any, T3 any](v1 T1, v2 T2, v3 T3) Triple[T1, T2, T3] {
|
||||
return Triple[T1, T2, T3]{V1: v1, V2: v2, V3: v3}
|
||||
}
|
||||
|
||||
func NewTuple3[T1 any, T2 any, T3 any](v1 T1, v2 T2, v3 T3) Triple[T1, T2, T3] {
|
||||
return Triple[T1, T2, T3]{V1: v1, V2: v2, V3: v3}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Quadruple[T1 any, T2 any, T3 any, T4 any] struct {
|
||||
@@ -67,6 +91,14 @@ func (t Quadruple[T1, T2, T3, T4]) TupleValues() []any {
|
||||
return []any{t.V1, t.V2, t.V3, t.V4}
|
||||
}
|
||||
|
||||
func NewQuadruple[T1 any, T2 any, T3 any, T4 any](v1 T1, v2 T2, v3 T3, v4 T4) Quadruple[T1, T2, T3, T4] {
|
||||
return Quadruple[T1, T2, T3, T4]{V1: v1, V2: v2, V3: v3, V4: v4}
|
||||
}
|
||||
|
||||
func NewTuple4[T1 any, T2 any, T3 any, T4 any](v1 T1, v2 T2, v3 T3, v4 T4) Quadruple[T1, T2, T3, T4] {
|
||||
return Quadruple[T1, T2, T3, T4]{V1: v1, V2: v2, V3: v3, V4: v4}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Quintuple[T1 any, T2 any, T3 any, T4 any, T5 any] struct {
|
||||
@@ -86,6 +118,14 @@ func (t Quintuple[T1, T2, T3, T4, T5]) TupleValues() []any {
|
||||
|
||||
}
|
||||
|
||||
func NewQuintuple[T1 any, T2 any, T3 any, T4 any, T5 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5) Quintuple[T1, T2, T3, T4, T5] {
|
||||
return Quintuple[T1, T2, T3, T4, T5]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5}
|
||||
}
|
||||
|
||||
func NewTuple5[T1 any, T2 any, T3 any, T4 any, T5 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5) Quintuple[T1, T2, T3, T4, T5] {
|
||||
return Quintuple[T1, T2, T3, T4, T5]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Sextuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any] struct {
|
||||
@@ -106,6 +146,14 @@ func (t Sextuple[T1, T2, T3, T4, T5, T6]) TupleValues() []any {
|
||||
|
||||
}
|
||||
|
||||
func NewSextuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6) Sextuple[T1, T2, T3, T4, T5, T6] {
|
||||
return Sextuple[T1, T2, T3, T4, T5, T6]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6}
|
||||
}
|
||||
|
||||
func NewTuple6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6) Sextuple[T1, T2, T3, T4, T5, T6] {
|
||||
return Sextuple[T1, T2, T3, T4, T5, T6]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Septuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any] struct {
|
||||
@@ -126,6 +174,14 @@ func (t Septuple[T1, T2, T3, T4, T5, T6, T7]) TupleValues() []any {
|
||||
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7}
|
||||
}
|
||||
|
||||
func NewSeptuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7) Septuple[T1, T2, T3, T4, T5, T6, T7] {
|
||||
return Septuple[T1, T2, T3, T4, T5, T6, T7]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7}
|
||||
}
|
||||
|
||||
func NewTuple7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7) Septuple[T1, T2, T3, T4, T5, T6, T7] {
|
||||
return Septuple[T1, T2, T3, T4, T5, T6, T7]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Octuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any] struct {
|
||||
@@ -147,6 +203,14 @@ func (t Octuple[T1, T2, T3, T4, T5, T6, T7, T8]) TupleValues() []any {
|
||||
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7, t.V8}
|
||||
}
|
||||
|
||||
func NewOctuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8) Octuple[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||
return Octuple[T1, T2, T3, T4, T5, T6, T7, T8]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8}
|
||||
}
|
||||
|
||||
func NewTuple8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8) Octuple[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||
return Octuple[T1, T2, T3, T4, T5, T6, T7, T8]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Nonuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any] struct {
|
||||
@@ -168,3 +232,10 @@ func (t Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]) TupleLength() int {
|
||||
func (t Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]) TupleValues() []any {
|
||||
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7, t.V8, t.V9}
|
||||
}
|
||||
|
||||
func NewNonuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8, v9 T9) Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||
return Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8, V9: v9}
|
||||
}
|
||||
func NewTuple9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8, v9 T9) Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||
return Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8, V9: v9}
|
||||
}
|
||||
|
@@ -12,6 +12,8 @@ import (
|
||||
var reflectTypeStr = reflect.TypeOf("")
|
||||
|
||||
func FromError(err error) *ExErr {
|
||||
|
||||
//goland:noinspection GoTypeAssertionOnErrors
|
||||
if verr, ok := err.(*ExErr); ok {
|
||||
// A simple ExErr
|
||||
return verr
|
||||
|
@@ -38,6 +38,13 @@ func (ee *ExErr) Error() string {
|
||||
// Unwrap must be implemented so that some error.XXX methods work
|
||||
func (ee *ExErr) Unwrap() error {
|
||||
if ee.OriginalError == nil {
|
||||
|
||||
if ee.WrappedErr != nil {
|
||||
if werr, ok := ee.WrappedErr.(error); ok {
|
||||
return werr
|
||||
}
|
||||
}
|
||||
|
||||
return nil // this is neccessary - otherwise we return a wrapped nil and the `x == nil` comparison fails (= panic in errors.Is and other failures)
|
||||
}
|
||||
return ee.OriginalError
|
||||
|
@@ -86,3 +86,28 @@ func MessageMatch(e error, matcher func(string) bool) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// OriginalError returns the lowest level error, probably the original/external error that was originally wrapped
|
||||
func OriginalError(e error) error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//goland:noinspection GoTypeAssertionOnErrors
|
||||
bmerr, ok := e.(*ExErr)
|
||||
for !ok {
|
||||
return e
|
||||
}
|
||||
|
||||
for bmerr.OriginalError != nil {
|
||||
bmerr = bmerr.OriginalError
|
||||
}
|
||||
|
||||
if bmerr.WrappedErr != nil {
|
||||
if werr, ok := bmerr.WrappedErr.(error); ok {
|
||||
return werr
|
||||
}
|
||||
}
|
||||
|
||||
return bmerr
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ type jsonHTTPResponse struct {
|
||||
data any
|
||||
headers []headerval
|
||||
cookies []cookieval
|
||||
filterOverride *string
|
||||
}
|
||||
|
||||
func (j jsonHTTPResponse) jsonRenderer(g *gin.Context) json.GoJsonRender {
|
||||
@@ -18,6 +19,9 @@ func (j jsonHTTPResponse) jsonRenderer(g *gin.Context) json.GoJsonRender {
|
||||
if jsonfilter := g.GetString(jsonFilterKey); jsonfilter != "" {
|
||||
f = &jsonfilter
|
||||
}
|
||||
if j.filterOverride != nil {
|
||||
f = j.filterOverride
|
||||
}
|
||||
return json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true, Filter: f}
|
||||
}
|
||||
|
||||
@@ -68,3 +72,7 @@ func (j jsonHTTPResponse) Headers() []string {
|
||||
func JSON(sc int, data any) HTTPResponse {
|
||||
return &jsonHTTPResponse{statusCode: sc, data: data}
|
||||
}
|
||||
|
||||
func JSONWithFilter(sc int, data any, f string) HTTPResponse {
|
||||
return &jsonHTTPResponse{statusCode: sc, data: data, filterOverride: &f}
|
||||
}
|
||||
|
26
go.mod
26
go.mod
@@ -6,12 +6,12 @@ require (
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/glebarez/go-sqlite v1.22.0 // only needed for tests -.-
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/rs/xid v1.6.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
go.mongodb.org/mongo-driver v1.16.1
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/sys v0.23.0
|
||||
golang.org/x/term v0.23.0
|
||||
go.mongodb.org/mongo-driver v1.17.1
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/term v0.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -21,7 +21,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.12.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
@@ -32,12 +32,12 @@ require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/compress v1.17.10 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
@@ -45,7 +45,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
@@ -53,10 +53,10 @@ require (
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
golang.org/x/arch v0.9.0 // indirect
|
||||
golang.org/x/image v0.19.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/arch v0.11.0 // indirect
|
||||
golang.org/x/image v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.37.6 // indirect
|
||||
|
46
go.sum
46
go.sum
@@ -28,6 +28,10 @@ github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3l
|
||||
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
|
||||
github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
|
||||
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.0/go.mod h1:UmRT+IRTGKz/DAkzcEGzyVqQFJ7H9BqwBO3pm9H/+HY=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
@@ -93,6 +97,8 @@ github.com/go-playground/validator/v10 v10.21.0 h1:4fZA11ovvtkdgaeev9RGWPgc1uj3H
|
||||
github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
@@ -134,6 +140,8 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
||||
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
@@ -175,6 +183,8 @@ github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtos
|
||||
github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -184,6 +194,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||
@@ -236,6 +248,10 @@ go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4B
|
||||
go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8=
|
||||
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.mongodb.org/mongo-driver v1.17.0 h1:Hp4q2MCjvY19ViwimTs00wHi7G4yzxh4/2+nTx8r40k=
|
||||
go.mongodb.org/mongo-driver v1.17.0/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
|
||||
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
@@ -243,6 +259,10 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
|
||||
golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -267,6 +287,10 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@@ -278,6 +302,10 @@ golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
|
||||
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
|
||||
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
|
||||
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -302,6 +330,10 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
@@ -337,6 +369,12 @@ golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
@@ -355,6 +393,10 @@ golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -369,6 +411,10 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
@@ -1,5 +1,5 @@
|
||||
package goext
|
||||
|
||||
const GoextVersion = "0.0.505"
|
||||
const GoextVersion = "0.0.522"
|
||||
|
||||
const GoextVersionTimestamp = "2024-08-08T15:57:05+0200"
|
||||
const GoextVersionTimestamp = "2024-10-05T01:12:00+0200"
|
||||
|
@@ -788,7 +788,7 @@ FieldLoop:
|
||||
|
||||
if f.omitEmpty && isEmptyValue(fv) {
|
||||
continue
|
||||
} else if opts.filter != nil && !matchesJSONFilter(f.jsonfilter, *opts.filter) {
|
||||
} else if !matchesJSONFilter(f.jsonfilter, opts.filter) {
|
||||
continue
|
||||
}
|
||||
e.WriteByte(next)
|
||||
@@ -808,16 +808,20 @@ FieldLoop:
|
||||
}
|
||||
}
|
||||
|
||||
func matchesJSONFilter(filter jsonfilter, value string) bool {
|
||||
func matchesJSONFilter(filter jsonfilter, value *string) bool {
|
||||
if len(filter) == 0 {
|
||||
return true
|
||||
return true // no filter in struct
|
||||
}
|
||||
|
||||
if value == nil || *value == "" {
|
||||
return false // no filter set, but struct has filter, return false
|
||||
}
|
||||
|
||||
if len(filter) == 1 && filter[0] == "-" {
|
||||
return false
|
||||
}
|
||||
|
||||
if filter.Contains(value) {
|
||||
if filter.Contains(*value) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1353,7 +1357,7 @@ func typeFields(t reflect.Type, tagkey string) structFields {
|
||||
|
||||
var jsonfilter []string
|
||||
jsonfilterTag := sf.Tag.Get("jsonfilter")
|
||||
if jsonfilterTag != "" && jsonfilterTag != "-" {
|
||||
if jsonfilterTag != "" {
|
||||
jsonfilter = strings.Split(jsonfilterTag, ",")
|
||||
}
|
||||
|
||||
|
15
langext/io.go
Normal file
15
langext/io.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package langext
|
||||
|
||||
import "io"
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (n nopCloser) Close() error {
|
||||
return nil // no op
|
||||
}
|
||||
|
||||
func WriteNopCloser(w io.Writer) io.WriteCloser {
|
||||
return nopCloser{w}
|
||||
}
|
@@ -7,6 +7,7 @@ import (
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DB interface {
|
||||
@@ -57,89 +58,121 @@ func (db *database) AddListener(listener Listener) {
|
||||
|
||||
func (db *database) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
|
||||
origsql := sqlstr
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PreExecMeta{Context: ctx, TransactionConstructorContext: nil}
|
||||
for _, v := range db.lstr {
|
||||
err := v.PreExec(ctx, nil, &sqlstr, &prep)
|
||||
err := v.PreExec(ctx, nil, &sqlstr, &prep, preMeta)
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
res, err := db.db.NamedExecContext(ctx, sqlstr, prep)
|
||||
|
||||
postMeta := PostExecMeta{Context: ctx, TransactionConstructorContext: nil, Init: t0, Start: t1, End: time.Now()}
|
||||
for _, v := range db.lstr {
|
||||
v.PostExec(nil, origsql, sqlstr, prep)
|
||||
v.PostExec(nil, origsql, sqlstr, prep, err, postMeta)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "Failed to [exec] sql statement").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (db *database) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
|
||||
origsql := sqlstr
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PreQueryMeta{Context: ctx, TransactionConstructorContext: nil}
|
||||
for _, v := range db.lstr {
|
||||
err := v.PreQuery(ctx, nil, &sqlstr, &prep)
|
||||
err := v.PreQuery(ctx, nil, &sqlstr, &prep, preMeta)
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
rows, err := sqlx.NamedQueryContext(ctx, db.db, sqlstr, prep)
|
||||
|
||||
postMeta := PostQueryMeta{Context: ctx, TransactionConstructorContext: nil, Init: t0, Start: t1, End: time.Now()}
|
||||
for _, v := range db.lstr {
|
||||
v.PostQuery(nil, origsql, sqlstr, prep)
|
||||
v.PostQuery(nil, origsql, sqlstr, prep, err, postMeta)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "Failed to [query] sql statement").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (db *database) Ping(ctx context.Context) error {
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PrePingMeta{Context: ctx}
|
||||
for _, v := range db.lstr {
|
||||
err := v.PrePing(ctx)
|
||||
err := v.PrePing(ctx, preMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
err := db.db.PingContext(ctx)
|
||||
|
||||
postMeta := PostPingMeta{Context: ctx, Init: t0, Start: t1, End: time.Now()}
|
||||
for _, v := range db.lstr {
|
||||
v.PostPing(err)
|
||||
v.PostPing(err, postMeta)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return exerr.Wrap(err, "Failed to [ping] sql database").Build()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *database) BeginTransaction(ctx context.Context, iso sql.IsolationLevel) (Tx, error) {
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
db.lock.Lock()
|
||||
txid := db.txctr
|
||||
db.txctr += 1 // with overflow !
|
||||
db.lock.Unlock()
|
||||
|
||||
preMeta := PreTxBeginMeta{Context: ctx}
|
||||
for _, v := range db.lstr {
|
||||
err := v.PreTxBegin(ctx, txid)
|
||||
err := v.PreTxBegin(ctx, txid, preMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
xtx, err := db.db.BeginTxx(ctx, &sql.TxOptions{Isolation: iso})
|
||||
|
||||
postMeta := PostTxBeginMeta{Context: ctx, Init: t0, Start: t1, End: time.Now()}
|
||||
for _, v := range db.lstr {
|
||||
v.PostTxBegin(txid, err, postMeta)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "Failed to start sql transaction").Build()
|
||||
}
|
||||
|
||||
for _, v := range db.lstr {
|
||||
v.PostTxBegin(txid, err)
|
||||
}
|
||||
|
||||
return NewTransaction(xtx, txid, db), nil
|
||||
return newTransaction(ctx, xtx, txid, db), nil
|
||||
}
|
||||
|
||||
func (db *database) Exit() error {
|
||||
|
214
sq/listener.go
214
sq/listener.go
@@ -1,188 +1,266 @@
|
||||
package sq
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PrePingMeta struct {
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
type PreTxBeginMeta struct {
|
||||
Context context.Context
|
||||
ConstructorContext context.Context
|
||||
}
|
||||
|
||||
type PreTxCommitMeta struct {
|
||||
ConstructorContext context.Context
|
||||
}
|
||||
|
||||
type PreTxRollbackMeta struct {
|
||||
ConstructorContext context.Context
|
||||
}
|
||||
|
||||
type PreQueryMeta struct {
|
||||
Context context.Context
|
||||
TransactionConstructorContext context.Context
|
||||
}
|
||||
|
||||
type PreExecMeta struct {
|
||||
Context context.Context
|
||||
TransactionConstructorContext context.Context
|
||||
}
|
||||
|
||||
type PostPingMeta struct {
|
||||
Context context.Context
|
||||
Init time.Time
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
type PostTxBeginMeta struct {
|
||||
Context context.Context
|
||||
Init time.Time
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
type PostTxCommitMeta struct {
|
||||
ConstructorContext context.Context
|
||||
Init time.Time
|
||||
Start time.Time
|
||||
End time.Time
|
||||
ExecCounter int
|
||||
QueryCounter int
|
||||
}
|
||||
|
||||
type PostTxRollbackMeta struct {
|
||||
ConstructorContext context.Context
|
||||
Init time.Time
|
||||
Start time.Time
|
||||
End time.Time
|
||||
ExecCounter int
|
||||
QueryCounter int
|
||||
}
|
||||
|
||||
type PostQueryMeta struct {
|
||||
Context context.Context
|
||||
TransactionConstructorContext context.Context
|
||||
Init time.Time
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
type PostExecMeta struct {
|
||||
Context context.Context
|
||||
TransactionConstructorContext context.Context
|
||||
Init time.Time
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
type Listener interface {
|
||||
PrePing(ctx context.Context) error
|
||||
PreTxBegin(ctx context.Context, txid uint16) error
|
||||
PreTxCommit(txid uint16) error
|
||||
PreTxRollback(txid uint16) error
|
||||
PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
||||
PreExec(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
||||
PrePing(ctx context.Context, meta PrePingMeta) error
|
||||
PreTxBegin(ctx context.Context, txid uint16, meta PreTxBeginMeta) error
|
||||
PreTxCommit(txid uint16, meta PreTxCommitMeta) error
|
||||
PreTxRollback(txid uint16, meta PreTxRollbackMeta) error
|
||||
PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error
|
||||
PreExec(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error
|
||||
|
||||
PostPing(result error)
|
||||
PostTxBegin(txid uint16, result error)
|
||||
PostTxCommit(txid uint16, result error)
|
||||
PostTxRollback(txid uint16, result error)
|
||||
PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
||||
PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
||||
PostPing(result error, meta PostPingMeta)
|
||||
PostTxBegin(txid uint16, result error, meta PostTxBeginMeta)
|
||||
PostTxCommit(txid uint16, result error, meta PostTxCommitMeta)
|
||||
PostTxRollback(txid uint16, result error, meta PostTxRollbackMeta)
|
||||
PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta)
|
||||
PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta)
|
||||
}
|
||||
|
||||
type genListener struct {
|
||||
prePing func(ctx context.Context) error
|
||||
preTxBegin func(ctx context.Context, txid uint16) error
|
||||
preTxCommit func(txid uint16) error
|
||||
preTxRollback func(txid uint16) error
|
||||
preQuery func(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
||||
preExec func(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
||||
postPing func(result error)
|
||||
postTxBegin func(txid uint16, result error)
|
||||
postTxCommit func(txid uint16, result error)
|
||||
postTxRollback func(txid uint16, result error)
|
||||
postQuery func(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
||||
postExec func(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
||||
prePing func(ctx context.Context, meta PrePingMeta) error
|
||||
preTxBegin func(ctx context.Context, txid uint16, meta PreTxBeginMeta) error
|
||||
preTxCommit func(txid uint16, meta PreTxCommitMeta) error
|
||||
preTxRollback func(txid uint16, meta PreTxRollbackMeta) error
|
||||
preQuery func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error
|
||||
preExec func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error
|
||||
postPing func(result error, meta PostPingMeta)
|
||||
postTxBegin func(txid uint16, result error, meta PostTxBeginMeta)
|
||||
postTxCommit func(txid uint16, result error, meta PostTxCommitMeta)
|
||||
postTxRollback func(txid uint16, result error, meta PostTxRollbackMeta)
|
||||
postQuery func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta)
|
||||
postExec func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta)
|
||||
}
|
||||
|
||||
func (g genListener) PrePing(ctx context.Context) error {
|
||||
func (g genListener) PrePing(ctx context.Context, meta PrePingMeta) error {
|
||||
if g.prePing != nil {
|
||||
return g.prePing(ctx)
|
||||
return g.prePing(ctx, meta)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PreTxBegin(ctx context.Context, txid uint16) error {
|
||||
func (g genListener) PreTxBegin(ctx context.Context, txid uint16, meta PreTxBeginMeta) error {
|
||||
if g.preTxBegin != nil {
|
||||
return g.preTxBegin(ctx, txid)
|
||||
return g.preTxBegin(ctx, txid, meta)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PreTxCommit(txid uint16) error {
|
||||
func (g genListener) PreTxCommit(txid uint16, meta PreTxCommitMeta) error {
|
||||
if g.preTxCommit != nil {
|
||||
return g.preTxCommit(txid)
|
||||
return g.preTxCommit(txid, meta)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PreTxRollback(txid uint16) error {
|
||||
func (g genListener) PreTxRollback(txid uint16, meta PreTxRollbackMeta) error {
|
||||
if g.preTxRollback != nil {
|
||||
return g.preTxRollback(txid)
|
||||
return g.preTxRollback(txid, meta)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
||||
func (g genListener) PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error {
|
||||
if g.preQuery != nil {
|
||||
return g.preQuery(ctx, txID, sql, params)
|
||||
return g.preQuery(ctx, txID, sql, params, meta)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PreExec(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
||||
func (g genListener) PreExec(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error {
|
||||
if g.preExec != nil {
|
||||
return g.preExec(ctx, txID, sql, params)
|
||||
return g.preExec(ctx, txID, sql, params, meta)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PostPing(result error) {
|
||||
func (g genListener) PostPing(result error, meta PostPingMeta) {
|
||||
if g.postPing != nil {
|
||||
g.postPing(result)
|
||||
g.postPing(result, meta)
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PostTxBegin(txid uint16, result error) {
|
||||
func (g genListener) PostTxBegin(txid uint16, result error, meta PostTxBeginMeta) {
|
||||
if g.postTxBegin != nil {
|
||||
g.postTxBegin(txid, result)
|
||||
g.postTxBegin(txid, result, meta)
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PostTxCommit(txid uint16, result error) {
|
||||
func (g genListener) PostTxCommit(txid uint16, result error, meta PostTxCommitMeta) {
|
||||
if g.postTxCommit != nil {
|
||||
g.postTxCommit(txid, result)
|
||||
g.postTxCommit(txid, result, meta)
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PostTxRollback(txid uint16, result error) {
|
||||
func (g genListener) PostTxRollback(txid uint16, result error, meta PostTxRollbackMeta) {
|
||||
if g.postTxRollback != nil {
|
||||
g.postTxRollback(txid, result)
|
||||
g.postTxRollback(txid, result, meta)
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
||||
func (g genListener) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta) {
|
||||
if g.postQuery != nil {
|
||||
g.postQuery(txID, sqlOriginal, sqlReal, params)
|
||||
g.postQuery(txID, sqlOriginal, sqlReal, params, result, meta)
|
||||
}
|
||||
}
|
||||
|
||||
func (g genListener) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
||||
func (g genListener) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta) {
|
||||
if g.postExec != nil {
|
||||
g.postExec(txID, sqlOriginal, sqlReal, params)
|
||||
g.postExec(txID, sqlOriginal, sqlReal, params, result, meta)
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrePingListener(f func(ctx context.Context) error) Listener {
|
||||
func NewPrePingListener(f func(ctx context.Context, meta PrePingMeta) error) Listener {
|
||||
return genListener{prePing: f}
|
||||
}
|
||||
|
||||
func NewPreTxBeginListener(f func(ctx context.Context, txid uint16) error) Listener {
|
||||
func NewPreTxBeginListener(f func(ctx context.Context, txid uint16, meta PreTxBeginMeta) error) Listener {
|
||||
return genListener{preTxBegin: f}
|
||||
}
|
||||
|
||||
func NewPreTxCommitListener(f func(txid uint16) error) Listener {
|
||||
func NewPreTxCommitListener(f func(txid uint16, meta PreTxCommitMeta) error) Listener {
|
||||
return genListener{preTxCommit: f}
|
||||
}
|
||||
|
||||
func NewPreTxRollbackListener(f func(txid uint16) error) Listener {
|
||||
func NewPreTxRollbackListener(f func(txid uint16, meta PreTxRollbackMeta) error) Listener {
|
||||
return genListener{preTxRollback: f}
|
||||
}
|
||||
|
||||
func NewPreQueryListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP) error) Listener {
|
||||
func NewPreQueryListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error) Listener {
|
||||
return genListener{preQuery: f}
|
||||
}
|
||||
|
||||
func NewPreExecListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP) error) Listener {
|
||||
func NewPreExecListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error) Listener {
|
||||
return genListener{preExec: f}
|
||||
}
|
||||
|
||||
func NewPreListener(f func(ctx context.Context, cmdtype string, txID *uint16, sql *string, params *PP) error) Listener {
|
||||
return genListener{
|
||||
preExec: func(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
||||
preExec: func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error {
|
||||
return f(ctx, "EXEC", txID, sql, params)
|
||||
},
|
||||
preQuery: func(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
||||
preQuery: func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error {
|
||||
return f(ctx, "QUERY", txID, sql, params)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewPostPingListener(f func(result error)) Listener {
|
||||
func NewPostPingListener(f func(result error, meta PostPingMeta)) Listener {
|
||||
return genListener{postPing: f}
|
||||
}
|
||||
|
||||
func NewPostTxBeginListener(f func(txid uint16, result error)) Listener {
|
||||
func NewPostTxBeginListener(f func(txid uint16, result error, meta PostTxBeginMeta)) Listener {
|
||||
return genListener{postTxBegin: f}
|
||||
}
|
||||
|
||||
func NewPostTxCommitListener(f func(txid uint16, result error)) Listener {
|
||||
func NewPostTxCommitListener(f func(txid uint16, result error, meta PostTxCommitMeta)) Listener {
|
||||
return genListener{postTxCommit: f}
|
||||
}
|
||||
|
||||
func NewPostTxRollbackListener(f func(txid uint16, result error)) Listener {
|
||||
func NewPostTxRollbackListener(f func(txid uint16, result error, meta PostTxRollbackMeta)) Listener {
|
||||
return genListener{postTxRollback: f}
|
||||
}
|
||||
|
||||
func NewPostQueryListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP)) Listener {
|
||||
func NewPostQueryListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta)) Listener {
|
||||
return genListener{postQuery: f}
|
||||
}
|
||||
|
||||
func NewPostExecListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP)) Listener {
|
||||
func NewPostExecListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta)) Listener {
|
||||
return genListener{postExec: f}
|
||||
}
|
||||
|
||||
func NewPostListener(f func(cmdtype string, txID *uint16, sqlOriginal string, sqlReal string, params PP)) Listener {
|
||||
func NewPostListener(f func(cmdtype string, txID *uint16, sqlOriginal string, sqlReal string, result error, params PP)) Listener {
|
||||
return genListener{
|
||||
postExec: func(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
||||
f("EXEC", txID, sqlOriginal, sqlReal, params)
|
||||
postExec: func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta) {
|
||||
f("EXEC", txID, sqlOriginal, sqlReal, result, params)
|
||||
},
|
||||
postQuery: func(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
||||
f("QUERY", txID, sqlOriginal, sqlReal, params)
|
||||
postQuery: func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta) {
|
||||
f("QUERY", txID, sqlOriginal, sqlReal, result, params)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TxStatus string
|
||||
@@ -26,6 +27,7 @@ type Tx interface {
|
||||
}
|
||||
|
||||
type transaction struct {
|
||||
constructorContext context.Context
|
||||
tx *sqlx.Tx
|
||||
id uint16
|
||||
status TxStatus
|
||||
@@ -34,8 +36,9 @@ type transaction struct {
|
||||
db *database
|
||||
}
|
||||
|
||||
func NewTransaction(xtx *sqlx.Tx, txid uint16, db *database) Tx {
|
||||
func newTransaction(ctx context.Context, xtx *sqlx.Tx, txid uint16, db *database) Tx {
|
||||
return &transaction{
|
||||
constructorContext: ctx,
|
||||
tx: xtx,
|
||||
id: txid,
|
||||
status: TxStatusInitial,
|
||||
@@ -46,42 +49,56 @@ func NewTransaction(xtx *sqlx.Tx, txid uint16, db *database) Tx {
|
||||
}
|
||||
|
||||
func (tx *transaction) Rollback() error {
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PreTxRollbackMeta{ConstructorContext: tx.constructorContext}
|
||||
for _, v := range tx.db.lstr {
|
||||
err := v.PreTxRollback(tx.id)
|
||||
err := v.PreTxRollback(tx.id, preMeta)
|
||||
if err != nil {
|
||||
return exerr.Wrap(err, "failed to call SQL pre-rollback listener").Int("tx.id", int(tx.id)).Build()
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
result := tx.tx.Rollback()
|
||||
|
||||
if result == nil {
|
||||
tx.status = TxStatusRollback
|
||||
}
|
||||
|
||||
postMeta := PostTxRollbackMeta{ConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now(), ExecCounter: tx.execCtr, QueryCounter: tx.queryCtr}
|
||||
for _, v := range tx.db.lstr {
|
||||
v.PostTxRollback(tx.id, result)
|
||||
v.PostTxRollback(tx.id, result, postMeta)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (tx *transaction) Commit() error {
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PreTxCommitMeta{ConstructorContext: tx.constructorContext}
|
||||
for _, v := range tx.db.lstr {
|
||||
err := v.PreTxCommit(tx.id)
|
||||
err := v.PreTxCommit(tx.id, preMeta)
|
||||
if err != nil {
|
||||
return exerr.Wrap(err, "failed to call SQL pre-commit listener").Int("tx.id", int(tx.id)).Build()
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
result := tx.tx.Commit()
|
||||
|
||||
if result == nil {
|
||||
tx.status = TxStatusComitted
|
||||
}
|
||||
|
||||
postMeta := PostTxCommitMeta{ConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now(), ExecCounter: tx.execCtr, QueryCounter: tx.queryCtr}
|
||||
for _, v := range tx.db.lstr {
|
||||
v.PostTxRollback(tx.id, result)
|
||||
v.PostTxCommit(tx.id, result, postMeta)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -89,21 +106,29 @@ func (tx *transaction) Commit() error {
|
||||
|
||||
func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
|
||||
origsql := sqlstr
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PreExecMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext}
|
||||
for _, v := range tx.db.lstr {
|
||||
err := v.PreExec(ctx, langext.Ptr(tx.id), &sqlstr, &prep)
|
||||
err := v.PreExec(ctx, langext.Ptr(tx.id), &sqlstr, &prep, preMeta)
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
res, err := tx.tx.NamedExecContext(ctx, sqlstr, prep)
|
||||
tx.execCtr++
|
||||
|
||||
if tx.status == TxStatusInitial && err == nil {
|
||||
tx.status = TxStatusActive
|
||||
}
|
||||
|
||||
postMeta := PostExecMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now()}
|
||||
for _, v := range tx.db.lstr {
|
||||
v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep)
|
||||
v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep, err, postMeta)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -114,21 +139,29 @@ func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Re
|
||||
|
||||
func (tx *transaction) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
|
||||
origsql := sqlstr
|
||||
|
||||
t0 := time.Now()
|
||||
|
||||
preMeta := PreQueryMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext}
|
||||
for _, v := range tx.db.lstr {
|
||||
err := v.PreQuery(ctx, langext.Ptr(tx.id), &sqlstr, &prep)
|
||||
err := v.PreQuery(ctx, langext.Ptr(tx.id), &sqlstr, &prep, preMeta)
|
||||
if err != nil {
|
||||
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||
}
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sqlstr, prep)
|
||||
tx.queryCtr++
|
||||
|
||||
if tx.status == TxStatusInitial && err == nil {
|
||||
tx.status = TxStatusActive
|
||||
}
|
||||
|
||||
postMeta := PostQueryMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now()}
|
||||
for _, v := range tx.db.lstr {
|
||||
v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep)
|
||||
v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep, err, postMeta)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@@ -2,6 +2,9 @@ package timeext
|
||||
|
||||
import "time"
|
||||
|
||||
// YearDifference calculates the difference between two timestamps in years.
|
||||
// = t1 - t2
|
||||
// returns a float value
|
||||
func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 {
|
||||
|
||||
yDelta := float64(t1.Year() - t2.Year())
|
||||
@@ -11,3 +14,31 @@ func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 {
|
||||
|
||||
return yDelta + (processT1 - processT2)
|
||||
}
|
||||
|
||||
// MonthDifference calculates the difference between two timestamps in months.
|
||||
// = t1 - t2
|
||||
// returns a float value
|
||||
func MonthDifference(t1 time.Time, t2 time.Time) float64 {
|
||||
|
||||
yDelta := float64(t1.Year() - t2.Year())
|
||||
mDelta := float64(t1.Month() - t2.Month())
|
||||
|
||||
dDelta := float64(0)
|
||||
|
||||
t1MonthDays := DaysInMonth(t1)
|
||||
t2MonthDays := DaysInMonth(t2)
|
||||
|
||||
if t2.Year() > t1.Year() || (t2.Year() == t1.Year() && t2.Month() > t1.Month()) {
|
||||
dDelta -= 1
|
||||
dDelta += float64(t1MonthDays-t1.Day()) / float64(t1MonthDays)
|
||||
dDelta += float64(t2.Day()) / float64(t2MonthDays)
|
||||
} else if t2.Year() < t1.Year() || (t2.Year() == t1.Year() && t2.Month() < t1.Month()) {
|
||||
dDelta -= 1
|
||||
dDelta += float64(t1.Day()) / float64(t1MonthDays)
|
||||
dDelta += float64(t2MonthDays-t2.Day()) / float64(t2MonthDays)
|
||||
} else {
|
||||
dDelta += float64(t1.Day()-t2.Day()) / float64(t1MonthDays)
|
||||
}
|
||||
|
||||
return yDelta*12 + mDelta + dDelta
|
||||
}
|
||||
|
@@ -81,3 +81,63 @@ func epsilonEquals(a, b float64) bool {
|
||||
epsilon := 0.01
|
||||
return math.Abs(a-b) < epsilon
|
||||
}
|
||||
|
||||
func TestMonthDifferenceSameDate(t *testing.T) {
|
||||
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
expected := 0.0
|
||||
result := MonthDifference(t2, t1)
|
||||
if !epsilonEquals(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthDifferenceSameMonth(t *testing.T) {
|
||||
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2022, 1, 31, 0, 0, 0, 0, time.UTC)
|
||||
expected := 0.967741935483871 // Approximation of 30/31 days
|
||||
result := MonthDifference(t2, t1)
|
||||
if !epsilonEquals(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthDifferenceDifferentMonthsSameYear(t *testing.T) {
|
||||
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2022, 3, 1, 0, 0, 0, 0, time.UTC)
|
||||
expected := 2.0
|
||||
result := MonthDifference(t2, t1)
|
||||
if !epsilonEquals(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthDifferenceDifferentYears(t *testing.T) {
|
||||
t1 := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2022, 2, 1, 0, 0, 0, 0, time.UTC)
|
||||
expected := 2.0
|
||||
result := MonthDifference(t2, t1)
|
||||
if !epsilonEquals(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthDifferenceT1BeforeT2(t *testing.T) {
|
||||
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
expected := 5.0
|
||||
result := MonthDifference(t2, t1)
|
||||
if !epsilonEquals(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthDifferenceT1AfterT2(t *testing.T) {
|
||||
t1 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
expected := -5.0
|
||||
result := MonthDifference(t2, t1)
|
||||
if !epsilonEquals(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
@@ -184,3 +184,10 @@ func AddYears(t time.Time, yearCount float64, tz *time.Location) time.Time {
|
||||
|
||||
return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount))
|
||||
}
|
||||
|
||||
func DaysInMonth(t time.Time) int {
|
||||
// https://stackoverflow.com/a/73882035/1761622
|
||||
|
||||
y, m, _ := t.Date()
|
||||
return time.Date(y, m+1, 0, 0, 0, 0, 0, time.UTC).Day()
|
||||
}
|
||||
|
@@ -191,3 +191,39 @@ func TestCombineDateAndTime_CombineDifferentParts(t *testing.T) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaysInMonth_31Days(t *testing.T) {
|
||||
date := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) // January
|
||||
expected := 31
|
||||
result := DaysInMonth(date)
|
||||
if result != expected {
|
||||
t.Errorf("Expected %d but got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaysInMonth_30Days(t *testing.T) {
|
||||
date := time.Date(2022, 4, 1, 0, 0, 0, 0, time.UTC) // April
|
||||
expected := 30
|
||||
result := DaysInMonth(date)
|
||||
if result != expected {
|
||||
t.Errorf("Expected %d but got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaysInMonth_FebruaryLeapYear(t *testing.T) {
|
||||
date := time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC) // February in a leap year
|
||||
expected := 29
|
||||
result := DaysInMonth(date)
|
||||
if result != expected {
|
||||
t.Errorf("Expected %d but got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaysInMonth_FebruaryNonLeapYear(t *testing.T) {
|
||||
date := time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC) // February in a non-leap year
|
||||
expected := 28
|
||||
result := DaysInMonth(date)
|
||||
if result != expected {
|
||||
t.Errorf("Expected %d but got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
@@ -215,7 +215,7 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken
|
||||
|
||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a greater $secondary (= newer)
|
||||
cond = append(cond, bson.M{"$and": bson.A{
|
||||
bson.M{fieldPrimary: valuePrimary},
|
||||
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||
bson.M{*fieldSecondary: bson.M{"$gt": valueSecondary}},
|
||||
}})
|
||||
|
||||
@@ -225,7 +225,7 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken
|
||||
|
||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a smaller $secondary (= older)
|
||||
cond = append(cond, bson.M{"$and": bson.A{
|
||||
bson.M{fieldPrimary: valuePrimary},
|
||||
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||
bson.M{*fieldSecondary: bson.M{"$lt": valueSecondary}},
|
||||
}})
|
||||
|
||||
|
Reference in New Issue
Block a user