mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 08:18:52 +00:00
fix(kingbase): return time columns as time of day
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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'") ||
|
||||
|
||||
Reference in New Issue
Block a user