Skip to content
This repository was archived by the owner on Nov 7, 2025. It is now read-only.

Commit 56f69ae

Browse files
authored
Extending map keys types discovering (#1378)
This PR: - handles `Map(LowCardinality(String),...)` - fixes `extractMapValueType` <!-- A note on testing your PR --> <!-- Basic unit test run is executed against each commit in the PR. If you want to run a full integration test suite, you can trigger it by commenting with '/run-integration-tests' or '/run-it' -->
1 parent b365143 commit 56f69ae

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

platform/clickhouse/schema.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func ResolveType(clickHouseTypeName string) reflect.Type {
240240
return reflect.TypeOf(true)
241241
case "JSON":
242242
return reflect.TypeOf(map[string]interface{}{})
243-
case "Map(String, Nullable(String))", "Map(String, String)", "Map(LowCardinality(String), String)":
243+
case "Map(String, Nullable(String))", "Map(String, String)", "Map(LowCardinality(String), String)", "Map(LowCardinality(String), Nullable(String))":
244244
return reflect.TypeOf(map[string]string{})
245245
case "Unknown":
246246
return reflect.TypeOf(UnknownType{})

platform/clickhouse/table_discovery.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -614,23 +614,27 @@ func removePrecision(str string) string {
614614
}
615615

616616
// extractMapValueType extracts the value type from a ClickHouse Map definition
617+
// extractMapValueType extracts the value type from a ClickHouse Map definition.
617618
func extractMapValueType(mapType string) (string, error) {
618-
// Regular expression to match Map(String, valueType)
619-
re := regexp.MustCompile(`Map\(String,\s*([^)]+)\)`)
620-
matches := re.FindStringSubmatch(mapType)
619+
// Regular expression to match "Map(String, <valueType>)"
620+
re := regexp.MustCompile(`Map\((?:LowCardinality\()?String\)?,\s*(.+)\)$`)
621621

622+
matches := re.FindStringSubmatch(mapType)
622623
if len(matches) < 2 {
623-
return "", fmt.Errorf("invalid map type format: %s", mapType)
624+
return "", errors.New("invalid map type format: " + mapType)
624625
}
625626

627+
// Trim spaces and return the full value type
626628
return strings.TrimSpace(matches[1]), nil
627629
}
628630

629631
func (td *tableDiscovery) enrichTableWithMapFields(inputTable map[string]map[string]columnMetadata) map[string]map[string]columnMetadata {
630632
outputTable := make(map[string]map[string]columnMetadata)
631633
for table, columns := range inputTable {
632634
for colName, columnMeta := range columns {
633-
if strings.HasPrefix(columnMeta.colType, "Map(String") {
635+
columnType := strings.TrimSpace(columnMeta.colType)
636+
if strings.HasPrefix(columnType, "Map(String") ||
637+
strings.HasPrefix(columnType, "Map(LowCardinality(String") {
634638
logger.Debug().Msgf("Discovered map column: %s.%s", table, colName)
635639
// Ensure the table exists in outputTable
636640
if _, ok := outputTable[table]; !ok {
@@ -661,7 +665,7 @@ func (td *tableDiscovery) enrichTableWithMapFields(inputTable map[string]map[str
661665
// with origin set to mapping
662666
mapKeyCol := colName + "." + key
663667
var valueType string
664-
valueType, err = extractMapValueType(columnMeta.colType)
668+
valueType, err = extractMapValueType(columnType)
665669
if err != nil {
666670
logger.Error().Msgf("Error extracting value type for table, column: %s, %s, %v", table, colName, err)
667671
continue

platform/clickhouse/table_discovery_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,35 @@ func Test_resolveColumn_Nullable(t *testing.T) {
303303
})
304304
}
305305
}
306+
307+
func TestExtractMapValueType(t *testing.T) {
308+
tests := []struct {
309+
input string
310+
expected string
311+
hasError bool
312+
}{
313+
{"Map(String, Int32)", "Int32", false},
314+
{"Map(String, Nullable(String))", "Nullable(String)", false},
315+
{"Map(String, Array(UInt8))", "Array(UInt8)", false},
316+
{"Map(LowCardinality(String), UInt64)", "UInt64", false},
317+
{"Map(LowCardinality(String), Decimal(10,2))", "Decimal(10,2)", false},
318+
{"Map(LowCardinality(String), FixedString(16))", "FixedString(16)", false},
319+
{"Map(String,)", "", true}, // Invalid format
320+
{"Map(LowCardinality(String),)", "", true}, // Invalid format
321+
{"Map(String)", "", true}, // Missing value type
322+
{"RandomType(String, Int32)", "", true}, // Not a Map type
323+
{"Map(String, Map(String, Int32))", "Map(String, Int32)", false}, // Nested map
324+
}
325+
326+
for _, test := range tests {
327+
t.Run(test.input, func(t *testing.T) {
328+
result, err := extractMapValueType(test.input)
329+
if (err != nil) != test.hasError {
330+
t.Errorf("unexpected error state for input %q: got error %v", test.input, err)
331+
}
332+
if result != test.expected {
333+
t.Errorf("expected %q, got %q for input %q", test.expected, result, test.input)
334+
}
335+
})
336+
}
337+
}

0 commit comments

Comments
 (0)