Skip to content
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
18 changes: 18 additions & 0 deletions neo4j/dbtype/spatial.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package dbtype

import (
"fmt"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/packstream"
)

// Point2D represents a two dimensional point in a particular coordinate reference system.
Expand All @@ -36,6 +37,23 @@ type Point3D struct {
SpatialRefId uint32 // Id of coordinate reference system.
}

// SerializeNeo4j serializes this point into the Packer.
func (p Point2D) SerializeNeo4j(packer packstream.Packer) {
packer.StructHeader('X', 3)
packer.Uint32(p.SpatialRefId)
packer.Float64(p.X)
packer.Float64(p.Y)
}

// SerializeNeo4j serializes this point into the Packer.
func (p Point3D) SerializeNeo4j(packer packstream.Packer) {
packer.StructHeader('Y', 4)
packer.Uint32(p.SpatialRefId)
packer.Float64(p.X)
packer.Float64(p.Y)
packer.Float64(p.Z)
}

// String returns string representation of this point.
func (p Point2D) String() string {
return fmt.Sprintf("Point{SpatialRefId=%d, X=%f, Y=%f}", p.SpatialRefId, p.X, p.Y)
Expand Down
53 changes: 53 additions & 0 deletions neo4j/dbtype/temporal.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package dbtype

import (
"fmt"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/packstream"
"time"
)

Expand All @@ -31,6 +32,16 @@ type (
LocalDateTime time.Time // Date and time in local timezone
)

// SerializeNeo4j serializes this LocalDateTime into the Packer.
func (t LocalDateTime) SerializeNeo4j(p packstream.Packer) {
tt := time.Time(t)
_, offset := tt.Zone()
secs := tt.Unix() + int64(offset)
p.StructHeader('d', 2)
p.Int64(secs)
p.Int(tt.Nanosecond())
}

// Time casts LocalDateTime to time.Time
//
// Note that the resulting time.Time will have its location set to time.Local.
Expand All @@ -45,6 +56,17 @@ func (t LocalDateTime) String() string {
return t.Time().Format("2006-01-02T15:04:05.999999999")
}

// SerializeNeo4j serializes this LocalTime into the Packer.
func (t LocalTime) SerializeNeo4j(p packstream.Packer) {
tt := time.Time(t)
nanos := int64(time.Hour)*int64(tt.Hour()) +
int64(time.Minute)*int64(tt.Minute()) +
int64(time.Second)*int64(tt.Second()) +
int64(tt.Nanosecond())
p.StructHeader('t', 1)
p.Int64(nanos)
}

// Time casts LocalTime to time.Time
//
// Note that the resulting time.Time will have its location set to time.Local.
Expand All @@ -59,6 +81,17 @@ func (t LocalTime) String() string {
return t.Time().Format("15:04:05.999999999")
}

// SerializeNeo4j serializes this Date into the Packer.
func (t Date) SerializeNeo4j(p packstream.Packer) {
tt := time.Time(t)
secs := tt.Unix()
_, offset := tt.Zone()
secs += int64(offset)
days := secs / (60 * 60 * 24)
p.StructHeader('D', 1)
p.Int64(days)
}

// Time casts Date to time.Time
func (t Date) Time() time.Time {
return time.Time(t)
Expand All @@ -70,6 +103,17 @@ func (t Date) String() string {
return t.Time().Format("2006-01-02")
}

// SerializeNeo4j serializes this Time into the Packer.
func (t Time) SerializeNeo4j(p packstream.Packer) {
tt := time.Time(t)
_, tzOffsetSecs := tt.Zone()
d := tt.Sub(
time.Date(tt.Year(), tt.Month(), tt.Day(), 0, 0, 0, 0, tt.Location()))
p.StructHeader('T', 2)
p.Int64(d.Nanoseconds())
p.Int(tzOffsetSecs)
}

// Time casts Time to time.Time
func (t Time) Time() time.Time {
return time.Time(t)
Expand All @@ -90,6 +134,15 @@ type Duration struct {
Nanos int
}

// SerializeNeo4j serializes this Duration into the Packer.
func (d Duration) SerializeNeo4j(p packstream.Packer) {
p.StructHeader('E', 4)
p.Int64(d.Months)
p.Int64(d.Days)
p.Int64(d.Seconds)
p.Int(d.Nanos)
}

// String returns the string representation of this Duration in ISO-8601 compliant form.
func (d Duration) String() string {
sign := ""
Expand Down
5 changes: 3 additions & 2 deletions neo4j/internal/bolt/bolt3.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ func NewBolt3(
}
b.out = &outgoing{
chunker: newChunker(),
packer: packstream.Packer{},
packer: packstream.Packer{
UseUtc: false,
},
onPackErr: func(err error) {
if b.err == nil {
b.err = err
Expand All @@ -142,7 +144,6 @@ func NewBolt3(
b.state = bolt3_dead
},
boltLogger: boltLog,
useUtc: false,
}
return b
}
Expand Down
2 changes: 1 addition & 1 deletion neo4j/internal/bolt/bolt3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func TestBolt3(outer *testing.T) {

bolt := c.(*bolt3)
assertBoltState(t, bolt3_ready, bolt)
if bolt.out.useUtc {
if bolt.out.packer.UseUtc {
t.Fatalf("Bolt 3 connections must never send and receive UTC datetimes")
}
return bolt, cleanup
Expand Down
2 changes: 1 addition & 1 deletion neo4j/internal/bolt/bolt4.go
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ func (b *bolt4) onHelloSuccess(checkUtcPatch bool) func(*success) {
if checkUtcPatch {
useUtc := collections.SliceContains(helloSuccess.patches, "utc")
b.queue.in.hyd.useUtc = useUtc
b.queue.out.useUtc = useUtc
b.queue.out.packer.UseUtc = useUtc
}
b.connId = helloSuccess.connectionId
b.serverVersion = helloSuccess.server
Expand Down
6 changes: 3 additions & 3 deletions neo4j/internal/bolt/bolt4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func TestBolt4(outer *testing.T) {
AssertStringEqual(t, bolt.ServerName(), "serverName")
AssertTrue(t, bolt.IsAlive())
AssertTrue(t, reflect.DeepEqual(bolt.queue.in.connReadTimeout, time.Duration(-1)))
AssertFalse(t, bolt.queue.out.useUtc)
AssertFalse(t, bolt.queue.out.packer.UseUtc)
})

outer.Run("Connect success with timeout hint", func(t *testing.T) {
Expand Down Expand Up @@ -173,7 +173,7 @@ func TestBolt4(outer *testing.T) {
defer cleanup()
defer bolt.Close(context.Background())

AssertTrue(t, bolt.queue.out.useUtc)
AssertTrue(t, bolt.queue.out.packer.UseUtc)
})

outer.Run(fmt.Sprintf("[%d.%d] Connect success with unknown patch", major, minor), func(t *testing.T) {
Expand All @@ -186,7 +186,7 @@ func TestBolt4(outer *testing.T) {
defer cleanup()
defer bolt.Close(context.Background())

AssertFalse(t, bolt.queue.out.useUtc)
AssertFalse(t, bolt.queue.out.packer.UseUtc)
})
}

Expand Down
7 changes: 4 additions & 3 deletions neo4j/internal/bolt/bolt5.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,13 @@ func NewBolt5(
connReadTimeout: -1,
},
&outgoing{
chunker: newChunker(),
packer: packstream.Packer{},
chunker: newChunker(),
packer: packstream.Packer{
UseUtc: true,
},
onPackErr: func(err error) { b.setError(err, true) },
onIoErr: b.onIoError,
boltLogger: boltLog,
useUtc: true,
},
b.onNextMessage,
b.onIoError,
Expand Down
2 changes: 1 addition & 1 deletion neo4j/internal/bolt/bolt5_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func TestBolt5(outer *testing.T) {

bolt := c.(*bolt5)
assertBoltState(t, bolt5Ready, bolt)
if !bolt.queue.out.useUtc {
if !bolt.queue.out.packer.UseUtc {
t.Fatalf("Bolt 5+ connections must always send and receive UTC datetimes")
}
return bolt, cleanup
Expand Down
Loading