Skip to content

Commit 3b93542

Browse files
authored
performance improvement for time format (#1118)
1 parent 076901a commit 3b93542

File tree

4 files changed

+129
-39
lines changed

4 files changed

+129
-39
lines changed

‎connection.go‎

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -245,44 +245,10 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
245245
ifv.IsZero(){
246246
buf=append(buf, "'0000-00-00'"...)
247247
} else{
248-
v:=v.In(mc.cfg.Loc)
249-
v=v.Add(time.Nanosecond*500) // To round under microsecond
250-
year:=v.Year()
251-
year100:=year/100
252-
year1:=year%100
253-
month:=v.Month()
254-
day:=v.Day()
255-
hour:=v.Hour()
256-
minute:=v.Minute()
257-
second:=v.Second()
258-
micro:=v.Nanosecond() /1000
259-
260-
buf=append(buf, []byte{
261-
'\'',
262-
digits10[year100], digits01[year100],
263-
digits10[year1], digits01[year1],
264-
'-',
265-
digits10[month], digits01[month],
266-
'-',
267-
digits10[day], digits01[day],
268-
' ',
269-
digits10[hour], digits01[hour],
270-
':',
271-
digits10[minute], digits01[minute],
272-
':',
273-
digits10[second], digits01[second],
274-
}...)
275-
276-
ifmicro!=0{
277-
micro10000:=micro/10000
278-
micro100:=micro/100%100
279-
micro1:=micro%100
280-
buf=append(buf, []byte{
281-
'.',
282-
digits10[micro10000], digits01[micro10000],
283-
digits10[micro100], digits01[micro100],
284-
digits10[micro1], digits01[micro1],
285-
}...)
248+
buf=append(buf, '\'')
249+
buf, err=appendDateTime(buf, v.In(mc.cfg.Loc))
250+
iferr!=nil{
251+
return"", err
286252
}
287253
buf=append(buf, '\'')
288254
}

‎packets.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error{
11161116
ifv.IsZero(){
11171117
b=append(b, "0000-00-00"...)
11181118
} else{
1119-
b=v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
1119+
b, err=appendDateTime(b, v.In(mc.cfg.Loc))
1120+
iferr!=nil{
1121+
returnerr
1122+
}
11201123
}
11211124

11221125
paramValues=appendLengthEncodedInteger(paramValues,

‎utils.go‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,55 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
276276
returnnil, fmt.Errorf("invalid DATETIME packet length %d", num)
277277
}
278278

279+
funcappendDateTime(buf []byte, t time.Time) ([]byte, error){
280+
nsec:=t.Nanosecond()
281+
// to round under microsecond
282+
ifnsec%1000>=500{// save half of time.Time.Add calls
283+
t=t.Add(500*time.Nanosecond)
284+
nsec=t.Nanosecond()
285+
}
286+
year, month, day:=t.Date()
287+
hour, min, sec:=t.Clock()
288+
micro:=nsec/1000
289+
290+
ifyear<1||year>9999{
291+
returnbuf, errors.New("year is not in the range [1, 9999]: "+strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap
292+
}
293+
year100:=year/100
294+
year1:=year%100
295+
296+
varlocalBuf [26]byte// does not escape
297+
localBuf[0], localBuf[1], localBuf[2], localBuf[3] =digits10[year100], digits01[year100], digits10[year1], digits01[year1]
298+
localBuf[4] ='-'
299+
localBuf[5], localBuf[6] =digits10[month], digits01[month]
300+
localBuf[7] ='-'
301+
localBuf[8], localBuf[9] =digits10[day], digits01[day]
302+
303+
ifhour==0&&min==0&&sec==0&&micro==0{
304+
returnappend(buf, localBuf[:10]...), nil
305+
}
306+
307+
localBuf[10] =' '
308+
localBuf[11], localBuf[12] =digits10[hour], digits01[hour]
309+
localBuf[13] =':'
310+
localBuf[14], localBuf[15] =digits10[min], digits01[min]
311+
localBuf[16] =':'
312+
localBuf[17], localBuf[18] =digits10[sec], digits01[sec]
313+
314+
ifmicro==0{
315+
returnappend(buf, localBuf[:19]...), nil
316+
}
317+
318+
micro10000:=micro/10000
319+
micro100:= (micro/100) %100
320+
micro1:=micro%100
321+
localBuf[19] ='.'
322+
localBuf[20], localBuf[21], localBuf[22], localBuf[23], localBuf[24], localBuf[25] =
323+
digits10[micro10000], digits01[micro10000], digits10[micro100], digits01[micro100], digits10[micro1], digits01[micro1]
324+
325+
returnappend(buf, localBuf[:]...), nil
326+
}
327+
279328
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
280329
// if the DATE or DATETIME has the zero value.
281330
// It must never be changed.

‎utils_test.go‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,78 @@ func TestIsolationLevelMapping(t *testing.T){
293293
}
294294
}
295295

296+
funcTestAppendDateTime(t*testing.T){
297+
tests:= []struct{
298+
t time.Time
299+
strstring
300+
}{
301+
{
302+
t: time.Date(2020, 05, 30, 0, 0, 0, 0, time.UTC),
303+
str: "2020-05-30",
304+
},
305+
{
306+
t: time.Date(2020, 05, 30, 22, 0, 0, 0, time.UTC),
307+
str: "2020-05-30 22:00:00",
308+
},
309+
{
310+
t: time.Date(2020, 05, 30, 22, 33, 0, 0, time.UTC),
311+
str: "2020-05-30 22:33:00",
312+
},
313+
{
314+
t: time.Date(2020, 05, 30, 22, 33, 44, 0, time.UTC),
315+
str: "2020-05-30 22:33:44",
316+
},
317+
{
318+
t: time.Date(2020, 05, 30, 22, 33, 44, 550000000, time.UTC),
319+
str: "2020-05-30 22:33:44.550000",
320+
},
321+
{
322+
t: time.Date(2020, 05, 30, 22, 33, 44, 550000499, time.UTC),
323+
str: "2020-05-30 22:33:44.550000",
324+
},
325+
{
326+
t: time.Date(2020, 05, 30, 22, 33, 44, 550000500, time.UTC),
327+
str: "2020-05-30 22:33:44.550001",
328+
},
329+
{
330+
t: time.Date(2020, 05, 30, 22, 33, 44, 550000567, time.UTC),
331+
str: "2020-05-30 22:33:44.550001",
332+
},
333+
{
334+
t: time.Date(2020, 05, 30, 22, 33, 44, 999999567, time.UTC),
335+
str: "2020-05-30 22:33:45",
336+
},
337+
}
338+
for_, v:=rangetests{
339+
buf:=make([]byte, 0, 32)
340+
buf, _=appendDateTime(buf, v.t)
341+
ifstr:=string(buf); str!=v.str{
342+
t.Errorf("appendDateTime(%v), have: %s, want: %s", v.t, str, v.str)
343+
return
344+
}
345+
}
346+
347+
// year out of range
348+
{
349+
v:=time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC)
350+
buf:=make([]byte, 0, 32)
351+
_, err:=appendDateTime(buf, v)
352+
iferr==nil{
353+
t.Error("want an error")
354+
return
355+
}
356+
}
357+
{
358+
v:=time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC)
359+
buf:=make([]byte, 0, 32)
360+
_, err:=appendDateTime(buf, v)
361+
iferr==nil{
362+
t.Error("want an error")
363+
return
364+
}
365+
}
366+
}
367+
296368
funcTestParseDateTime(t*testing.T){
297369
cases:= []struct{
298370
namestring

0 commit comments

Comments
(0)