From 7ad6ecd2e9f6d3660643eb88947a8f7bbe5e56cf Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Fri, 27 Feb 2026 10:09:14 -0800 Subject: [PATCH] fix: strip style prefix for label/matrix primitive parameters (#99) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BindStyledParameterWithOptions passed the raw styled value directly to BindStringToObject for primitive types, without first removing the style prefix. This caused label (leading ".") and matrix (";param=") styles to fail with parse errors for non-struct, non-slice destinations. Route primitive values through splitStyledParameter — the same function already used by the struct and slice paths — so the prefix is stripped before binding. Co-Authored-By: Claude Opus 4.6 --- bindparam.go | 12 ++++++++++-- bindparam_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/bindparam.go b/bindparam.go index 7906f53..e7d29cc 100644 --- a/bindparam.go +++ b/bindparam.go @@ -152,8 +152,16 @@ func BindStyledParameterWithOptions(style string, paramName string, value string return bindSplitPartsToDestinationArray(parts, dest) } - // Try to bind the remaining types as a base type. - return BindStringToObject(value, dest) + // For primitive types, we still need to strip style prefixes (e.g. label's + // leading "." or matrix's ";paramName=") before binding. + parts, err := splitStyledParameter(style, opts.Explode, false, paramName, value) + if err != nil { + return fmt.Errorf("error splitting parameter '%s': %w", paramName, err) + } + if len(parts) != 1 { + return fmt.Errorf("parameter '%s': expected single value, got %d parts", paramName, len(parts)) + } + return BindStringToObject(parts[0], dest) } // This is a complex set of operations, but each given parameter style can be diff --git a/bindparam_test.go b/bindparam_test.go index a18fcbd..aa60348 100644 --- a/bindparam_test.go +++ b/bindparam_test.go @@ -1101,6 +1101,54 @@ func TestRoundTripQueryParameter(t *testing.T) { }) } +func TestBindStyledParameterWithOptions_LabelPrimitive(t *testing.T) { + tests := []struct { + name string + explode bool + value string + want int32 + }{ + {"non-exploded", false, ".5", 5}, + {"exploded", true, ".5", 5}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var dest int32 + err := BindStyledParameterWithOptions("label", "param", tt.value, &dest, BindStyledParameterOptions{ + ParamLocation: ParamLocationPath, + Explode: tt.explode, + Required: true, + }) + require.NoError(t, err) + assert.Equal(t, tt.want, dest) + }) + } +} + +func TestBindStyledParameterWithOptions_MatrixPrimitive(t *testing.T) { + tests := []struct { + name string + explode bool + value string + want int32 + }{ + {"non-exploded", false, ";param=5", 5}, + {"exploded", true, ";param=5", 5}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var dest int32 + err := BindStyledParameterWithOptions("matrix", "param", tt.value, &dest, BindStyledParameterOptions{ + ParamLocation: ParamLocationPath, + Explode: tt.explode, + Required: true, + }) + require.NoError(t, err) + assert.Equal(t, tt.want, dest) + }) + } +} + func TestBindStyledParameterWithLocation(t *testing.T) { t.Run("bigNumber", func(t *testing.T) { expectedBig := big.NewInt(12345678910)