@@ -893,6 +893,16 @@ func quoteTrinoIdentifier(name string) string {
893893 return name
894894}
895895
896+ // unwrapMessageDataForColumns returns the map to use for column lookups.
897+ // If msgData is {"value": {...}, "_metadata": {...}}, returns the inner value map.
898+ // Otherwise returns msgData as-is (plain columnar format).
899+ func unwrapMessageDataForColumns (msgData map [string ]interface {}) map [string ]interface {} {
900+ if v , ok := msgData ["value" ].(map [string ]interface {}); ok && len (msgData ) <= 2 {
901+ return v
902+ }
903+ return msgData
904+ }
905+
896906// extractDataAndMetadata extracts data and _metadata from a rawMode message.
897907// If msg.Data is {"value": ..., "_metadata": ...}, uses those; otherwise uses whole data and msg.Metadata.
898908func extractDataAndMetadata (msg * types.Message ) (dataStr , metaStr string ) {
@@ -1103,13 +1113,24 @@ func (t *TrinoSinkConnector) Write(ctx context.Context, messages <-chan *types.M
11031113 }
11041114}
11051115
1116+ // hasRawModeColumns checks if the table has data and _metadata columns (required for raw mode).
1117+ func (t * TrinoSinkConnector ) hasRawModeColumns (columns []TableColumnInfo ) bool {
1118+ hasData , hasMeta := false , false
1119+ for _ , col := range columns {
1120+ if col .Name == "data" {
1121+ hasData = true
1122+ }
1123+ if col .Name == "_metadata" {
1124+ hasMeta = true
1125+ }
1126+ }
1127+ return hasData && hasMeta
1128+ }
1129+
11061130func (t * TrinoSinkConnector ) executeBatch (ctx context.Context , batch []* types.Message ) error {
11071131 if len (batch ) == 0 {
11081132 return nil
11091133 }
1110- if t .rawMode () {
1111- return t .executeBatchRaw (ctx , batch )
1112- }
11131134
11141135 // Get table columns (use cached if available, otherwise fetch)
11151136 t .columnsMu .RLock ()
@@ -1128,6 +1149,16 @@ func (t *TrinoSinkConnector) executeBatch(ctx context.Context, batch []*types.Me
11281149 t .columnsMu .Unlock ()
11291150 }
11301151
1152+ // Use raw mode only when configured AND table has data/_metadata columns.
1153+ // If rawMode is true but table has different schema (e.g. columnar), fall back to schema-based insert.
1154+ if t .rawMode () && t .hasRawModeColumns (tableColumns ) {
1155+ return t .executeBatchRaw (ctx , batch )
1156+ }
1157+ if t .rawMode () && ! t .hasRawModeColumns (tableColumns ) {
1158+ t .logger .Info ("rawMode is true but target table does not have data/_metadata columns, using schema-based insert" ,
1159+ "table" , fmt .Sprintf ("%s.%s.%s" , t .config .Catalog , t .config .Schema , t .config .Table ))
1160+ }
1161+
11311162 // Create a map for fast lookup of table columns by name
11321163 tableColumnsMap := make (map [string ]TableColumnInfo )
11331164 for _ , col := range tableColumns {
@@ -1146,8 +1177,9 @@ func (t *TrinoSinkConnector) executeBatch(ctx context.Context, batch []*types.Me
11461177 return fmt .Errorf ("failed to parse message JSON at index %d: %w" , i , err )
11471178 }
11481179
1149- // Collect all keys from all messages
1150- for k := range msgData {
1180+ // Collect all keys from all messages (unwrap {"value": {...}} format if present)
1181+ dataForColumns := unwrapMessageDataForColumns (msgData )
1182+ for k := range dataForColumns {
11511183 allMessageKeys [k ] = true
11521184 }
11531185 }
@@ -1167,13 +1199,14 @@ func (t *TrinoSinkConnector) executeBatch(ctx context.Context, batch []*types.Me
11671199 var firstMsgData map [string ]interface {}
11681200 if err := json .Unmarshal (batch [0 ].Data , & firstMsgData ); err == nil {
11691201 // Log first message structure (limit size to avoid huge logs)
1170- firstMsgJSON , _ := json .Marshal (firstMsgData )
1202+ dataForPreview := unwrapMessageDataForColumns (firstMsgData )
1203+ firstMsgJSON , _ := json .Marshal (dataForPreview )
11711204 msgPreview := string (firstMsgJSON )
11721205 if len (msgPreview ) > 500 {
11731206 msgPreview = msgPreview [:500 ] + "..."
11741207 }
1175- firstMsgKeys := make ([]string , 0 , len (firstMsgData ))
1176- for k := range firstMsgData {
1208+ firstMsgKeys := make ([]string , 0 , len (dataForPreview ))
1209+ for k := range dataForPreview {
11771210 firstMsgKeys = append (firstMsgKeys , k )
11781211 }
11791212 t .logger .Info ("First message in batch (preview)" ,
@@ -1244,6 +1277,9 @@ func (t *TrinoSinkConnector) executeBatch(ctx context.Context, batch []*types.Me
12441277 return fmt .Errorf ("failed to parse message JSON: %w" , err )
12451278 }
12461279
1280+ // Unwrap {"value": {...}} format if present (e.g. from Kafka with rawMode source)
1281+ data = unwrapMessageDataForColumns (data )
1282+
12471283 // Build values for this row - use values from message or NULL
12481284 values := make ([]string , len (columnsToUse ))
12491285 for j , col := range columnsToUse {
0 commit comments