fix(kingbase): return time columns as time of day

This commit is contained in:
李子凡
2026-10-15 17:14:27 +08:00
committed by GitHub
parent a313b99590
commit fc5fe07d25
2 changed files with 43 additions and 5 deletions
+23 -3
View File
@@ -1688,6 +1688,12 @@ func normalizeValue(value any, columnTypeName string) any {
}
return map[string]string{"$binary": base64.StdEncoding.EncodeToString(typed)}
case time.Time:
// A "time" column holds a time of day and no date. gokb decodes one into a
// time.Time on Go's zero date, so formatting a date part shows every time of
// day as 0000-01-01T<time> (issue #12188).
if isKingbaseTimeOnlyType(columnTypeName) {
return typed.Format("15:04:05.999999999")
}
// KingBase "timestamp"/"date" columns are wall-clock values with no timezone
// meaning; the gokb driver decodes them into a time.Time labeled with the
// process-local zone, which is not a real UTC instant. Formatting those with
@@ -1710,15 +1716,29 @@ func normalizeValue(value any, columnTypeName string) any {
}
func isKingbaseTimezoneLessDateTime(columnTypeName string) bool {
normalized := strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(columnTypeName), " ", ""))
switch normalized {
case "DATE", "TIMESTAMP", "TIME":
switch normalizedKingbaseColumnType(columnTypeName) {
case "DATE", "TIMESTAMP":
return true
default:
return false
}
}
// isKingbaseTimeOnlyType matches the type names a "time" column can carry: the
// driver's own name (`DatabaseTypeName`) and the information_schema spelling.
func isKingbaseTimeOnlyType(columnTypeName string) bool {
switch normalizedKingbaseColumnType(columnTypeName) {
case "TIME", "TIMEWITHOUTTIMEZONE":
return true
default:
return false
}
}
func normalizedKingbaseColumnType(columnTypeName string) string {
return strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(columnTypeName), " ", ""))
}
func isTextBytes(value []byte) bool {
for _, char := range value {
if char == 0 || char < 0x09 || char > 0x0d && char < 0x20 {
+20 -2
View File
@@ -4936,7 +4936,7 @@ func containsString(values []string, target string) bool {
}
// Regression test for https://github.com/t8y2/dbx/issues/7681: a timezone-less
// "timestamp"/"date"/"time" column must not be labeled as an absolute UTC
// "timestamp"/"date" column must not be labeled as an absolute UTC
// instant (RFC3339Nano with a "Z"/offset suffix), or clients that convert it
// to a display timezone will double-apply the shift.
func TestNormalizeValueKingbaseTimezoneLessDateTime(t *testing.T) {
@@ -4944,7 +4944,7 @@ func TestNormalizeValueKingbaseTimezoneLessDateTime(t *testing.T) {
// the process-local zone, which is not a real UTC instant.
wallClock := time.Date(2026, time.January, 30, 10, 0, 3, 0, time.UTC)
for _, columnType := range []string{"TIMESTAMP", "timestamp", "DATE", "TIME"} {
for _, columnType := range []string{"TIMESTAMP", "timestamp", "DATE"} {
got := normalizeValue(wallClock, columnType)
want := "2026-01-30T10:00:03"
gotStr, ok := got.(string)
@@ -4967,6 +4967,24 @@ func TestNormalizeValueKingbaseTimezoneLessDateTime(t *testing.T) {
}
}
// Regression test for https://github.com/t8y2/dbx/issues/12188: gokb decodes a
// "time" column into a time.Time on Go's zero date, so the date part must not
// reach the client. Showing it rendered every time of day as 0000-01-01T17:19:34.
func TestNormalizeValueKingbaseTimeColumnKeepsTimeOfDay(t *testing.T) {
timeOfDay := time.Date(0, time.January, 1, 17, 19, 34, 0, time.UTC)
for _, columnType := range []string{"TIME", "time", "time without time zone"} {
got := normalizeValue(timeOfDay, columnType)
gotStr, ok := got.(string)
if !ok {
t.Fatalf("columnType=%s: expected a string, got %#v", columnType, got)
}
if gotStr != "17:19:34" {
t.Fatalf("columnType=%s: got %q, want %q", columnType, gotStr, "17:19:34")
}
}
}
func TestCatalogColumnsPreservesJsonAndJsonbTypes(t *testing.T) {
state := &metadataDriverState{query: func(query string) (driver.Rows, error) {
if !strings.Contains(query, "WHEN t.typname = 'json' THEN 'json'") ||