Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
00492f5
fix: count binary literal char length in bytes
ck89119 Aug 6, 2026
90dabca
fix: preserve binary literal semantics across expressions
ck89119 Aug 6, 2026
0840537
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 6, 2026
de19c11
fix: preserve binary string semantics across materialization
ck89119 Aug 6, 2026
3e1430c
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 6, 2026
e7180c4
fix: preserve dynamic binary function semantics
ck89119 Aug 6, 2026
32591b3
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 7, 2026
257e058
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 7, 2026
717a347
fix: update blob substring bvt result
ck89119 Aug 7, 2026
e8ef4df
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 7, 2026
17fde9c
fix: close binary string semantic gaps
ck89119 Aug 8, 2026
fc469b0
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 8, 2026
1fb7517
test: avoid trailing empty binary output
ck89119 Aug 8, 2026
41e197f
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
3a2c160
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
a8cf864
fix: complete binary string semantics
ck89119 Aug 9, 2026
9d956ac
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
294e115
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
28d59b1
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
9ede4b7
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
ceff9e1
fix: close binary string provenance gaps
ck89119 Aug 9, 2026
6de9481
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 9, 2026
829ddb4
test: clean binary semantics bvt result
ck89119 Aug 9, 2026
ba1aa78
Merge remote-tracking branch 'mo/main' into issue-25295-main
ck89119 Aug 10, 2026
567bdb9
fix: repair binary semantics ci regressions
ck89119 Aug 10, 2026
2f55c8a
test: improve binary regexp coverage
ck89119 Aug 10, 2026
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
100 changes: 77 additions & 23 deletions pkg/container/batch/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ const (
prepareParamKindBatchModeNone = byte(0)
prepareParamKindBatchModeUniform = byte(1)
prepareParamKindBatchModeRows = byte(2)
prepareParamKindBatchBinaryFlag = byte(0x80)
prepareParamKindBatchMaxRows = int32(1 << 24)
)

type prepareParamKindBatchRecord struct {
mode byte
kind vector.PrepareParamKind
rows []vector.PrepareParamKind
mode byte
kind vector.PrepareParamKind
encodedRows []byte
binaryString bool
}

func New(attrs []string) *Batch {
Expand Down Expand Up @@ -152,6 +154,9 @@ func (bat *Batch) HasPrepareParamKindMetadata() bool {
if vec == nil {
continue
}
if vec.GetIsBinaryString() {
return true
}
if len(vec.GetPrepareParamKinds()) != 0 {
return true
}
Expand All @@ -162,6 +167,26 @@ func (bat *Batch) HasPrepareParamKindMetadata() bool {
return false
}

func (bat *Batch) HasBinaryStringMetadata() bool {
if bat == nil {
return false
}
for _, vec := range bat.Vecs {
if vec == nil {
continue
}
switch vec.GetType().Oid {
case types.T_binary, types.T_varbinary, types.T_blob:
// Static binary types need no dynamic trailer.
continue
}
if vec.GetIsBinaryString() {
return true
}
}
return false
}

// AppendPrepareParamKindMetadata appends a self-identifying transient trailer
// after the stable Batch bytes. It is intentionally not part of
// MarshalBinaryTo: persisted/stable Vector and Batch bytes remain unchanged.
Expand All @@ -183,25 +208,39 @@ func (bat *Batch) AppendPrepareParamKindMetadata(w *bytes.Buffer) error {
return moerr.NewInvalidInputNoCtx("cannot encode prepared parameter metadata for nil vector")
}
kinds := vec.GetPrepareParamKinds()
mixedBinaryString := vec.HasBinaryStringRows()
binaryFlag := byte(0)
if vec.GetIsBinaryString() && !mixedBinaryString {
binaryFlag = prepareParamKindBatchBinaryFlag
}
switch {
case len(kinds) != 0:
if len(kinds) != vec.Length() || int64(len(kinds)) > int64(prepareParamKindBatchMaxRows) {
case len(kinds) != 0 || mixedBinaryString:
if (len(kinds) != 0 && len(kinds) != vec.Length()) ||
int64(vec.Length()) > int64(prepareParamKindBatchMaxRows) {
return moerr.NewInvalidInputNoCtx("invalid prepared parameter metadata row count")
}
ext.WriteByte(prepareParamKindBatchModeRows)
rowLen := int32(len(kinds))
rowLen := int32(vec.Length())
ext.Write(types.EncodeInt32(&rowLen))
for _, kind := range kinds {
for row := 0; row < vec.Length(); row++ {
kind := vector.PrepareParamNone
if len(kinds) != 0 {
kind = kinds[row]
}
if kind > vector.PrepareParamBoolean {
return moerr.NewInvalidInputNoCtx("invalid prepared parameter metadata kind")
}
ext.WriteByte(byte(kind))
encoded := byte(kind)
if vec.GetIsBinaryStringAt(row) {
encoded |= prepareParamKindBatchBinaryFlag
}
ext.WriteByte(encoded)
}
case vec.HasPrepareParamKind() && vec.GetPrepareParamKind() != vector.PrepareParamNone:
ext.WriteByte(prepareParamKindBatchModeUniform)
ext.WriteByte(prepareParamKindBatchModeUniform | binaryFlag)
ext.WriteByte(byte(vec.GetPrepareParamKind()))
default:
ext.WriteByte(prepareParamKindBatchModeNone)
ext.WriteByte(prepareParamKindBatchModeNone | binaryFlag)
}
}
if uint64(ext.Len()) > uint64(^uint32(0))-4 {
Expand Down Expand Up @@ -438,7 +477,6 @@ func (bat *Batch) UnmarshalBinaryWithPrepareParamKinds(data []byte, mp *mpool.MP
if rowCount != int64(bat.RowCount()) {
return moerr.NewInvalidInputNoCtx("prepared parameter metadata batch row count mismatch")
}
cleared := 0
for i, record := range records {
if i >= len(bat.Vecs) {
return moerr.NewInvalidInputNoCtx("prepared parameter metadata vector count mismatch")
Expand All @@ -447,29 +485,35 @@ func (bat *Batch) UnmarshalBinaryWithPrepareParamKinds(data []byte, mp *mpool.MP
var applyErr error
switch record.mode {
case prepareParamKindBatchModeNone:
// The stable decoder reset any metadata from an earlier reuse.
vec.SetIsBinaryString(record.binaryString)
case prepareParamKindBatchModeUniform:
if record.kind == vector.PrepareParamNone {
applyErr = moerr.NewInvalidInputNoCtx("uniform prepared parameter metadata cannot be None")
} else {
vec.SetPrepareParamKind(record.kind)
}
vec.SetIsBinaryString(record.binaryString)
case prepareParamKindBatchModeRows:
if len(record.rows) != vec.Length() {
if len(record.encodedRows) != vec.Length() {
applyErr = moerr.NewInvalidInputNoCtx("prepared parameter metadata row count mismatch")
} else {
applyErr = vec.SetPrepareParamKindsWithMP(record.rows, mp)
applyErr = vec.SetPrepareParamKindsAndBinaryStringFromReader(
bytes.NewReader(record.encodedRows), len(record.encodedRows), mp,
prepareParamKindBatchBinaryFlag,
)
}
default:
applyErr = moerr.NewInvalidInputNoCtx("invalid prepared parameter metadata mode")
}
if applyErr != nil {
for j := 0; j < cleared; j++ {
_ = bat.Vecs[j].SetPrepareParamKindsWithMP(nil, mp)
for _, resetVec := range bat.Vecs {
if resetVec != nil {
_ = resetVec.SetPrepareParamKindsWithMP(nil, mp)
resetVec.SetIsBinaryString(false)
}
}
return applyErr
}
cleared = i + 1
}
return nil
}
Expand Down Expand Up @@ -506,6 +550,7 @@ func (bat *Batch) UnmarshalFromReaderWithPrepareParamKinds(
for _, vec := range bat.Vecs {
if vec != nil {
_ = vec.SetPrepareParamKindsWithMP(nil, mp)
vec.SetIsBinaryString(false)
}
}
}
Expand Down Expand Up @@ -563,9 +608,11 @@ func (bat *Batch) UnmarshalFromReaderWithPrepareParamKinds(
if err != nil {
return fail(err)
}
binaryString := mode&prepareParamKindBatchBinaryFlag != 0
mode &^= prepareParamKindBatchBinaryFlag
switch mode {
case prepareParamKindBatchModeNone:
// The stable decoder reset any metadata from an earlier reuse.
bat.Vecs[i].SetIsBinaryString(binaryString)
case prepareParamKindBatchModeUniform:
kind, err := readByte()
if err != nil {
Expand All @@ -576,6 +623,7 @@ func (bat *Batch) UnmarshalFromReaderWithPrepareParamKinds(
return fail(moerr.NewInvalidInputNoCtx("invalid uniform prepared parameter metadata kind"))
}
bat.Vecs[i].SetPrepareParamKind(vector.PrepareParamKind(kind))
bat.Vecs[i].SetIsBinaryString(binaryString)
case prepareParamKindBatchModeRows:
count, err := types.ReadInt32(limited)
if err != nil {
Expand All @@ -592,7 +640,9 @@ func (bat *Batch) UnmarshalFromReaderWithPrepareParamKinds(
if limited.N < minimumRemaining || int64(count) > limited.N-minimumRemaining {
return fail(io.ErrUnexpectedEOF)
}
if err := bat.Vecs[i].SetPrepareParamKindsFromReader(limited, int(count), mp); err != nil {
if err := bat.Vecs[i].SetPrepareParamKindsAndBinaryStringFromReader(
limited, int(count), mp, prepareParamKindBatchBinaryFlag,
); err != nil {
return fail(err)
}
default:
Expand Down Expand Up @@ -719,6 +769,8 @@ func parsePrepareParamKindBatchTrailer(
if err != nil {
return nil, 0, err
}
records[i].binaryString = mode&prepareParamKindBatchBinaryFlag != 0
mode &^= prepareParamKindBatchBinaryFlag
records[i].mode = mode
switch mode {
case prepareParamKindBatchModeNone:
Expand All @@ -743,13 +795,14 @@ func parsePrepareParamKindBatchTrailer(
if reader.Len() < 4 || int64(count) > int64(reader.Len()-4) {
return nil, 0, io.ErrUnexpectedEOF
}
records[i].rows = make([]vector.PrepareParamKind, int(count))
for row := range records[i].rows {
kind, err := types.ReadByte(reader)
records[i].encodedRows = make([]byte, int(count))
for row := range records[i].encodedRows {
encoded, err := types.ReadByte(reader)
kind := encoded &^ prepareParamKindBatchBinaryFlag
if err != nil || vector.PrepareParamKind(kind) > vector.PrepareParamBoolean {
return nil, 0, moerr.NewInvalidInputNoCtx("invalid prepared parameter metadata kind")
}
records[i].rows[row] = vector.PrepareParamKind(kind)
records[i].encodedRows[row] = encoded
}
default:
return nil, 0, moerr.NewInvalidInputNoCtx("invalid prepared parameter metadata mode")
Expand Down Expand Up @@ -1615,6 +1668,7 @@ func (bat *Batch) CloneTo(toBat *Batch, mp *mpool.MPool) (err error) {
toBat.Clean(mp)
return
}
toVec.SetIsBinaryString(srcVec.GetIsBinaryString())
}
}
toBat.rowCount = bat.rowCount
Expand Down
24 changes: 24 additions & 0 deletions pkg/container/batch/batch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,26 @@ func TestClonePreservesPrepareParamKind(t *testing.T) {
require.Equal(t, vector.PrepareParamDecimal, cloned.Vecs[0].GetPrepareParamKind())
}

func TestClonePreservesConstantBinaryStringMetadata(t *testing.T) {
mp := mpool.MustNewZero()
source := NewWithSize(1)
var err error
source.Vecs[0], err = vector.NewConstBytes(
types.T_varchar.ToType(), []byte{0xe4, 0xbd, 0xa0}, 3, mp)
require.NoError(t, err)
source.Vecs[0].SetIsBinaryString(true)
source.SetRowCount(3)
defer source.Clean(mp)

cloned, err := source.Dup(mp)
require.NoError(t, err)
defer cloned.Clean(mp)
require.True(t, cloned.Vecs[0].GetIsBinaryString())
for row := 0; row < 3; row++ {
require.True(t, cloned.Vecs[0].GetIsBinaryStringAt(row))
}
}

func TestPrepareParamKindTransportRoundTripAndReuse(t *testing.T) {
mp := mpool.MustNewZero()
source := NewWithSize(1)
Expand All @@ -646,6 +666,7 @@ func TestPrepareParamKindTransportRoundTripAndReuse(t *testing.T) {
vector.PrepareParamFloat,
vector.PrepareParamNone,
})
require.NoError(t, source.Vecs[0].SetBinaryStringRows([]bool{true, false}))
source.SetRowCount(2)
defer source.Clean(mp)

Expand All @@ -662,11 +683,14 @@ func TestPrepareParamKindTransportRoundTripAndReuse(t *testing.T) {
require.NoError(t, decoded.UnmarshalBinaryWithPrepareParamKinds(encoded, mp))
require.Equal(t, vector.PrepareParamFloat, decoded.Vecs[0].GetPrepareParamKindAt(0))
require.Equal(t, vector.PrepareParamNone, decoded.Vecs[0].GetPrepareParamKindAt(1))
require.True(t, decoded.Vecs[0].GetIsBinaryStringAt(0))
require.False(t, decoded.Vecs[0].GetIsBinaryStringAt(1))

// Reusing the receiver with a legacy payload must clear the previous
// sidecar rather than leaking the first generation's provenance.
require.NoError(t, decoded.UnmarshalBinaryWithPrepareParamKinds(legacy, mp))
require.Equal(t, vector.PrepareParamNone, decoded.Vecs[0].GetPrepareParamKindAt(0))
require.False(t, decoded.Vecs[0].GetIsBinaryString())
decoded.Clean(mp)
}

Expand Down
1 change: 1 addition & 0 deletions pkg/container/pSpool/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ func (cb *cachedBatch) GetCopiedBatch(
dst.Vecs[i].SetGrouping(vec.GetGrouping())
}
dst.Vecs[i].SetIsBin(vec.GetIsBin())
dst.Vecs[i].SetIsBinaryString(vec.GetIsBinaryString())
if vec.IsConst() {
// GetUnionAllFunction already propagates row provenance for the
// non-constant path. Constants still need their scalar metadata
Expand Down
Loading
Loading