Skip to content

GODRIVER-3924 Fix bug of unmarshaling null.#2401

Merged
qingyang-hu merged 1 commit into
mongodb:release/2.6from
qingyang-hu:godriver3924
Jun 3, 2026
Merged

GODRIVER-3924 Fix bug of unmarshaling null.#2401
qingyang-hu merged 1 commit into
mongodb:release/2.6from
qingyang-hu:godriver3924

Conversation

@qingyang-hu
Copy link
Copy Markdown
Contributor

@qingyang-hu qingyang-hu commented May 26, 2026

GODRIVER-3924

Summary

This PR improves type guards and test coverage for TypeNull or TypeUndefined.

Background & Motivation

The type guards added to rawDecodeValue, coreDocumentDecodeValue, and arrayCodec.DecodeValue didn't handle TypeNull or TypeUndefined.

This PR also improves test coverage:

-> bson.Raw -> bsoncore.Document ->bsoncore.Array
bson.A N/A
bson.D ✓ (Added in this PR) N/A ✓ error
bsoncore.Document -- -- ✓ error
nil ✓ (Added in this PR) ✓ (Added in this PR) ✓ (Added in this PR)

@mongodb-drivers-pr-bot
Copy link
Copy Markdown
Contributor

🧪 Performance Results

Commit SHA: 7dd7aac

The following benchmark tests for version 6a16211460604d0007574a2c had statistically significant changes (i.e., |z-score| > 1.96):

Benchmark Measurement % Change Patch Value Stable Region H-Score Z-Score
BenchmarkMultiInsertSmallDocument total_mem_allocs 16.8467 2726557.0000 Avg: 2333447.3969
Med: 2315057.0000
Stdev: 118340.9243
0.8485 3.3218
BenchmarkMultiInsertSmallDocument total_bytes_allocated 11.7659 510345448.0000 Avg: 456620031.9389
Med: 454743480.0000
Stdev: 14640035.2426
0.8571 3.6698
BenchmarkMultiInsertSmallDocument total_time_seconds 11.0338 1.1753 Avg: 1.0585
Med: 1.0441
Stdev: 0.0457
0.8117 2.5534
BenchmarkSingleRunCommand total_time_seconds -6.9515 1.0622 Avg: 1.1415
Med: 1.1370
Stdev: 0.0315
0.7906 -2.5183
BenchmarkMultiFindMany ops_per_second_max -2.7046 4115226.3374 Avg: 4229621.3731
Med: 4237288.1356
Stdev: 53244.2066
0.7578 -2.1485
BenchmarkBSONDeepDocumentEncoding total_time_seconds 1.8806 1.2165 Avg: 1.1940
Med: 1.1944
Stdev: 0.0093
0.7897 2.4192
BenchmarkMultiFindMany ops_per_second_med -1.6374 3610108.3032 Avg: 3670202.8560
Med: 3676470.5882
Stdev: 17861.0247
0.8404 -3.3646
BenchmarkBSONFlatDocumentDecoding total_time_seconds 1.3153 1.1996 Avg: 1.1840
Med: 1.1840
Stdev: 0.0048
0.8904 3.2255
BenchmarkSingleFindOneByID allocated_bytes_per_op 0.1901 22505.0000 Avg: 22462.2920
Med: 22464.0000
Stdev: 18.6011
0.7519 2.2960
BenchmarkSingleRunCommand allocated_bytes_per_op -0.1335 12216.0000 Avg: 12232.3333
Med: 12234.0000
Stdev: 6.8581
0.8195 -2.3816

For a comprehensive view of all microbenchmark results for this PR's commit, please check out the Evergreen perf task for this patch.

@mongodb-drivers-pr-bot
Copy link
Copy Markdown
Contributor

API Change Report

No changes found!

@qingyang-hu qingyang-hu changed the base branch from master to release/2.6 June 2, 2026 15:40
@qingyang-hu qingyang-hu marked this pull request as ready for review June 2, 2026 15:53
@qingyang-hu qingyang-hu requested a review from a team as a code owner June 2, 2026 15:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes BSON decoding behavior so bson.Raw, bsoncore.Document, and bsoncore.Array correctly handle TypeNull and TypeUndefined during unmarshaling, aligning these decoders with other value decoders in the package and preventing spurious type-compatibility errors.

Changes:

  • Add TypeNull/TypeUndefined handling to rawDecodeValue, coreDocumentDecodeValue, and arrayCodec.DecodeValue.
  • Expand unit test coverage for the above decoders (including explicit TypeUndefined cases).
  • Add unmarshaling compatibility tests for decoding bson.D and nil into bson.Raw / bsoncore raw byte types.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
bson/unmarshal_test.go Adds integration-style unmarshaling compatibility tests for bson.D/nil into raw byte container types.
bson/primitive_codecs.go Updates bson.Raw decoding to accept and properly consume null/undefined.
bson/primitive_codecs_test.go Adds unit tests for bson.Raw decoding of null/undefined.
bson/default_value_decoders.go Updates bson.D and bsoncore.Document decoding to handle undefined (and null for core document).
bson/default_value_decoders_test.go Adds unit tests for bsoncore.Document and bsoncore.Array decoding of null/undefined.
bson/array_codec.go Updates bsoncore.Array decoding to handle null/undefined.
Comments suppressed due to low confidence (2)

bson/primitive_codecs.go:101

  • rawDecodeValue allows TypeArray but always copies via appendDocumentBytes, which falls back to copyDocument (and therefore ValueReader.ReadDocument) when the reader does not implement the internal bytesReader fast-path. For TypeArray, ReadDocument is not a valid transition for a ValueReader implementation, so decoding arrays into bson.Raw can fail for non-bytes readers (even though TypeArray is explicitly accepted).
	switch vrType := vr.Type(); vrType {
	case Type(0), TypeEmbeddedDocument, TypeArray:
	case TypeNull:
		val.Set(reflect.Zero(val.Type()))
		return vr.ReadNull()
	case TypeUndefined:
		val.Set(reflect.Zero(val.Type()))
		return vr.ReadUndefined()
	default:
		return fmt.Errorf("cannot decode %v into a %s", vrType, val.Type())
	}

	if val.IsNil() {
		val.Set(reflect.MakeSlice(val.Type(), 0, 0))
	}

	val.SetLen(0)

	rdr, err := appendDocumentBytes(val.Interface().(Raw), vr)
	val.Set(reflect.ValueOf(rdr))
	return err

bson/default_value_decoders.go:1336

  • coreDocumentDecodeValue accepts TypeArray but always copies bytes via appendDocumentBytes, whose slow path uses ValueReader.ReadDocument(). For TypeArray, ReadDocument is not a valid operation per the ValueReader contract, so decoding arrays into bsoncore.Document can fail for ValueReader implementations that don't support the internal bytesReader shortcut.
	switch vrType := vr.Type(); vrType {
	case Type(0), TypeEmbeddedDocument, TypeArray:
	case TypeNull:
		val.Set(reflect.Zero(val.Type()))
		return vr.ReadNull()
	case TypeUndefined:
		val.Set(reflect.Zero(val.Type()))
		return vr.ReadUndefined()
	default:
		return fmt.Errorf("cannot decode %v into a %s", vrType, val.Type())
	}

	if val.IsNil() {
		val.Set(reflect.MakeSlice(val.Type(), 0, 0))
	}

	val.SetLen(0)

	cdoc, err := appendDocumentBytes(val.Interface().(bsoncore.Document), vr)
	val.Set(reflect.ValueOf(cdoc))
	return err

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@matthewdale matthewdale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 👍

@qingyang-hu qingyang-hu merged commit 6019145 into mongodb:release/2.6 Jun 3, 2026
31 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants