From c0880cb076cae55410bcb1dd1ec16bcd25eca0ee Mon Sep 17 00:00:00 2001 From: Hein Date: Sun, 26 Apr 2026 12:43:44 +0200 Subject: [PATCH] feat(pkg): preserve PostgreSQL types in mapDataType function * Add support for known PostgreSQL types and modifiers * Implement canonicalization for PostgreSQL types * Introduce unit tests for PostgreSQL type handling --- pkg/readers/dctx/reader.go | 41 +++++++++++++++++++++++++- pkg/readers/dctx/reader_test.go | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/pkg/readers/dctx/reader.go b/pkg/readers/dctx/reader.go index c0fce64..2509fc1 100644 --- a/pkg/readers/dctx/reader.go +++ b/pkg/readers/dctx/reader.go @@ -7,6 +7,7 @@ import ( "strings" "git.warky.dev/wdevs/relspecgo/pkg/models" + "git.warky.dev/wdevs/relspecgo/pkg/pgsql" "git.warky.dev/wdevs/relspecgo/pkg/readers" ) @@ -232,7 +233,19 @@ func (r *Reader) convertField(dctxField *models.DCTXField, tableName string) ([] // mapDataType maps Clarion data types to SQL types func (r *Reader) mapDataType(clarionType string, size int) (sqlType string, precision int) { - switch strings.ToUpper(clarionType) { + trimmedType := strings.TrimSpace(clarionType) + + // Preserve known PostgreSQL types (including arrays and extension types) + // from DCTX input instead of coercing them to generic text. + if pgsql.IsKnownPostgresType(trimmedType) { + pgType := canonicalizePostgresType(trimmedType) + if !pgsql.HasExplicitTypeModifier(pgType) && size > 0 && pgsql.SupportsLength(pgType) { + return pgType, size + } + return pgType, 0 + } + + switch strings.ToUpper(trimmedType) { case "LONG": if size == 8 { return "bigint", 0 @@ -306,6 +319,32 @@ func (r *Reader) mapDataType(clarionType string, size int) (sqlType string, prec } } +func canonicalizePostgresType(typeStr string) string { + t := strings.ToLower(strings.Join(strings.Fields(strings.TrimSpace(typeStr)), " ")) + if t == "" { + return "" + } + + // Handle array suffixes + arrayCount := 0 + for strings.HasSuffix(t, "[]") { + arrayCount++ + t = strings.TrimSpace(strings.TrimSuffix(t, "[]")) + } + + // Handle optional type modifier + modifier := "" + if idx := strings.Index(t, "("); idx > 0 { + if end := strings.LastIndex(t, ")"); end > idx { + modifier = t[idx : end+1] + t = strings.TrimSpace(t[:idx]) + } + } + + base := pgsql.CanonicalizeBaseType(t) + return base + modifier + strings.Repeat("[]", arrayCount) +} + // processKeys processes DCTX keys and converts them to indexes and primary keys func (r *Reader) processKeys(dctxTable *models.DCTXTable, table *models.Table, fieldGuidMap map[string]string) error { for _, dctxKey := range dctxTable.Keys { diff --git a/pkg/readers/dctx/reader_test.go b/pkg/readers/dctx/reader_test.go index 2bfc98a..a82b682 100644 --- a/pkg/readers/dctx/reader_test.go +++ b/pkg/readers/dctx/reader_test.go @@ -493,3 +493,55 @@ func TestRelationships(t *testing.T) { } } } + +func TestMapDataType_PostgresTypes(t *testing.T) { + reader := &Reader{} + + tests := []struct { + name string + inputType string + size int + wantType string + wantLength int + }{ + { + name: "integer array preserved", + inputType: "integer[]", + wantType: "integer[]", + }, + { + name: "citext array preserved", + inputType: "citext[]", + wantType: "citext[]", + }, + { + name: "vector modifier preserved", + inputType: "vector(1536)", + wantType: "vector(1536)", + }, + { + name: "alias canonicalized in array", + inputType: "int4[]", + wantType: "integer[]", + }, + { + name: "varchar length from size", + inputType: "varchar", + size: 120, + wantType: "varchar", + wantLength: 120, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotType, gotLength := reader.mapDataType(tt.inputType, tt.size) + if gotType != tt.wantType { + t.Fatalf("mapDataType(%q, %d) type = %q, want %q", tt.inputType, tt.size, gotType, tt.wantType) + } + if gotLength != tt.wantLength { + t.Fatalf("mapDataType(%q, %d) length = %d, want %d", tt.inputType, tt.size, gotLength, tt.wantLength) + } + }) + } +}