Skip to content

Commit 473ca64

Browse files
fix: measure query time excluding formatting, show < 1 ms, add unit tests
- Move startTime after BeginBatch so elapsed time brackets DB execution only, excluding formatter setup/teardown overhead - Show '< 1' instead of clamping sub-millisecond queries to 1ms - Colon format shows actual 0 for sub-ms; rate calculations still use 1ms floor - Add TestPrintStatisticsUnit with 7 subtests covering standard/colon format, sub-ms display, disabled mode, default packet size, and multi-batch math
1 parent db57765 commit 473ca64

2 files changed

Lines changed: 85 additions & 9 deletions

File tree

pkg/sqlcmd/sqlcmd.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,6 @@ func (s *Sqlcmd) getRunnableQuery(q string) string {
425425
// -102: Conversion error occurred when selecting return value
426426
func (s *Sqlcmd) runQuery(query string) (int, int64, error) {
427427
retcode := -101
428-
startTime := time.Now()
429428
s.Format.BeginBatch(query, s.vars, s.GetOutput(), s.GetError())
430429
ctx := context.Background()
431430
timeout := s.vars.QueryTimeoutSeconds()
@@ -435,6 +434,7 @@ func (s *Sqlcmd) runQuery(query string) (int, int64, error) {
435434
ctx = ct
436435
}
437436
retmsg := &sqlexp.ReturnMessage{}
437+
startTime := time.Now()
438438
rows, qe := s.db.QueryContext(ctx, query, retmsg)
439439
if qe != nil {
440440
s.Format.AddError(qe)
@@ -512,8 +512,8 @@ func (s *Sqlcmd) runQuery(query string) (int, int64, error) {
512512
s.Format.EndResultSet()
513513
}
514514
}
515-
s.Format.EndBatch()
516515
elapsedMs := time.Since(startTime).Milliseconds()
516+
s.Format.EndBatch()
517517
return retcode, elapsedMs, qe
518518
}
519519

@@ -602,22 +602,28 @@ func (s *Sqlcmd) printStatistics(elapsedMs int64, numBatches int, out io.Writer)
602602
packetSize = 4096 // default packet size
603603
}
604604

605-
// Ensure minimum 1ms for calculations
606-
if elapsedMs < 1 {
607-
elapsedMs = 1
605+
// Ensure minimum 1ms for rate calculations, but display actual 0 as "< 1"
606+
displayElapsedMs := elapsedMs
607+
calcElapsedMs := elapsedMs
608+
if calcElapsedMs < 1 {
609+
calcElapsedMs = 1
608610
}
609611

610-
avgTime := float64(elapsedMs) / float64(numBatches)
611-
batchesPerSec := float64(numBatches) / (float64(elapsedMs) / 1000.0)
612+
avgTime := float64(calcElapsedMs) / float64(numBatches)
613+
batchesPerSec := float64(numBatches) / (float64(calcElapsedMs) / 1000.0)
612614

613615
if *s.PrintStatistics == 1 {
614616
// Colon-separated format: n:x:t1:t2:t3
615617
// packetSize:numBatches:totalTime:avgTime:batchesPerSec
616-
_, _ = fmt.Fprintf(out, "\n%d:%d:%d:%.2f:%.2f\n", packetSize, numBatches, elapsedMs, avgTime, batchesPerSec)
618+
_, _ = fmt.Fprintf(out, "\n%d:%d:%d:%.2f:%.2f\n", packetSize, numBatches, displayElapsedMs, avgTime, batchesPerSec)
617619
} else {
618620
// Standard format
619621
_, _ = fmt.Fprintf(out, "\nNetwork packet size (bytes): %d\n", packetSize)
620622
_, _ = fmt.Fprintf(out, "%d xact[s]:\n", numBatches)
621-
_, _ = fmt.Fprintf(out, "Clock Time (ms.): total %7d avg %.2f (%.2f xacts per sec.)\n", elapsedMs, avgTime, batchesPerSec)
623+
if displayElapsedMs < 1 {
624+
_, _ = fmt.Fprintf(out, "Clock Time (ms.): total < 1 avg %.2f (%.2f xacts per sec.)\n", avgTime, batchesPerSec)
625+
} else {
626+
_, _ = fmt.Fprintf(out, "Clock Time (ms.): total %7d avg %.2f (%.2f xacts per sec.)\n", displayElapsedMs, avgTime, batchesPerSec)
627+
}
622628
}
623629
}

pkg/sqlcmd/sqlcmd_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,3 +749,73 @@ func TestPrintStatisticsDisabled(t *testing.T) {
749749
assert.NotContains(t, output, "Network packet size", "Should not contain packet size when disabled")
750750
assert.NotContains(t, output, "xact[s]:", "Should not contain xacts label when disabled")
751751
}
752+
753+
func TestPrintStatisticsUnit(t *testing.T) {
754+
newSqlcmd := func(format int, packetSize int) *Sqlcmd {
755+
s := &Sqlcmd{Connect: &ConnectSettings{}}
756+
s.PrintStatistics = &format
757+
s.Connect.PacketSize = packetSize
758+
return s
759+
}
760+
761+
t.Run("standard format", func(t *testing.T) {
762+
s := newSqlcmd(0, 4096)
763+
var buf bytes.Buffer
764+
s.printStatistics(150, 3, &buf)
765+
out := buf.String()
766+
assert.Contains(t, out, "Network packet size (bytes): 4096")
767+
assert.Contains(t, out, "3 xact[s]:")
768+
assert.Contains(t, out, "Clock Time (ms.): total 150")
769+
assert.Contains(t, out, "xacts per sec.")
770+
})
771+
772+
t.Run("colon format", func(t *testing.T) {
773+
s := newSqlcmd(1, 8192)
774+
var buf bytes.Buffer
775+
s.printStatistics(200, 2, &buf)
776+
out := buf.String()
777+
assert.Contains(t, out, "8192:2:200:")
778+
})
779+
780+
t.Run("sub-millisecond standard", func(t *testing.T) {
781+
s := newSqlcmd(0, 4096)
782+
var buf bytes.Buffer
783+
s.printStatistics(0, 1, &buf)
784+
out := buf.String()
785+
assert.Contains(t, out, "total < 1", "sub-ms should show < 1")
786+
})
787+
788+
t.Run("sub-millisecond colon", func(t *testing.T) {
789+
s := newSqlcmd(1, 4096)
790+
var buf bytes.Buffer
791+
s.printStatistics(0, 1, &buf)
792+
out := buf.String()
793+
assert.Contains(t, out, "4096:1:0:", "colon format should show 0 for sub-ms")
794+
})
795+
796+
t.Run("disabled", func(t *testing.T) {
797+
s := &Sqlcmd{Connect: &ConnectSettings{}}
798+
var buf bytes.Buffer
799+
s.printStatistics(100, 1, &buf)
800+
assert.Empty(t, buf.String(), "should produce no output when disabled")
801+
})
802+
803+
t.Run("default packet size", func(t *testing.T) {
804+
s := newSqlcmd(0, 0)
805+
var buf bytes.Buffer
806+
s.printStatistics(50, 1, &buf)
807+
out := buf.String()
808+
assert.Contains(t, out, "Network packet size (bytes): 4096", "should default to 4096")
809+
})
810+
811+
t.Run("multiple batches", func(t *testing.T) {
812+
s := newSqlcmd(0, 4096)
813+
var buf bytes.Buffer
814+
s.printStatistics(1000, 10, &buf)
815+
out := buf.String()
816+
assert.Contains(t, out, "10 xact[s]:")
817+
assert.Contains(t, out, "total 1000")
818+
assert.Contains(t, out, "avg 100.00")
819+
assert.Contains(t, out, "10.00 xacts per sec.")
820+
})
821+
}

0 commit comments

Comments
 (0)