@@ -64,7 +64,7 @@ func buildIcebergDDLEvents(
6464 fmt .Sprintf (
6565 "CREATE TABLE IF NOT EXISTS %s (%s)" ,
6666 commonType .QuoteSchema (curr .SchemaName , curr .TableName ),
67- strings .Join (icebergColumnSQLDefs (curr .Columns ), "," ),
67+ strings .Join (icebergCreateTableSQLDefs (curr .Columns ), "," ),
6868 ),
6969 tableInfo ,
7070 model .ActionCreateTable ,
@@ -243,6 +243,32 @@ func icebergColumnSQLDefs(columns []sinkiceberg.Column) []string {
243243 return defs
244244}
245245
246+ func icebergCreateTableSQLDefs (columns []sinkiceberg.Column ) []string {
247+ defs := icebergColumnSQLDefs (columns )
248+ if primaryKey := icebergPrimaryKeySQLDef (columns ); primaryKey != "" {
249+ defs = append (defs , primaryKey )
250+ }
251+ return defs
252+ }
253+
254+ func icebergPrimaryKeySQLDef (columns []sinkiceberg.Column ) string {
255+ primaryKeyColumns := make ([]string , 0 , len (columns ))
256+ for _ , col := range columns {
257+ if ! icebergColumnIsPrimaryKey (col ) {
258+ continue
259+ }
260+ primaryKeyColumns = append (primaryKeyColumns , commonType .QuoteName (col .Name ))
261+ }
262+ if len (primaryKeyColumns ) == 0 {
263+ return ""
264+ }
265+ return fmt .Sprintf ("PRIMARY KEY (%s)" , strings .Join (primaryKeyColumns , "," ))
266+ }
267+
268+ func icebergColumnIsPrimaryKey (col sinkiceberg.Column ) bool {
269+ return col .OriginalTableCol != nil && strings .EqualFold (strings .TrimSpace (col .OriginalTableCol .IsPK ), "true" )
270+ }
271+
246272func icebergColumnSQLDef (col sinkiceberg.Column ) string {
247273 return fmt .Sprintf ("%s %s %s" ,
248274 commonType .QuoteName (col .Name ),
@@ -259,6 +285,10 @@ func icebergColumnNullability(required bool) string {
259285}
260286
261287func icebergColumnSQLType (col sinkiceberg.Column ) string {
288+ if sqlType , ok := originalIcebergColumnSQLType (col ); ok {
289+ return sqlType
290+ }
291+
262292 switch strings .TrimSpace (col .Type ) {
263293 case "int" :
264294 return "INT"
@@ -283,6 +313,10 @@ func icebergColumnSQLType(col sinkiceberg.Column) string {
283313}
284314
285315func icebergColumnToTableCol (col sinkiceberg.Column ) (cloudstorage.TableCol , error ) {
316+ if original := cloneOriginalIcebergTableCol (col ); original != nil {
317+ return * original , nil
318+ }
319+
286320 result := cloudstorage.TableCol {
287321 Name : col .Name ,
288322 }
@@ -318,6 +352,65 @@ func icebergColumnToTableCol(col sinkiceberg.Column) (cloudstorage.TableCol, err
318352 return result , nil
319353}
320354
355+ func originalIcebergColumnSQLType (col sinkiceberg.Column ) (string , bool ) {
356+ if col .OriginalTableCol == nil {
357+ return "" , false
358+ }
359+
360+ switch strings .ToUpper (strings .TrimSpace (col .OriginalTableCol .Tp )) {
361+ case "CHAR" , "VARCHAR" , "VAR_STRING" ,
362+ "BINARY" , "VARBINARY" ,
363+ "TINYTEXT" , "TEXT" , "MEDIUMTEXT" , "LONGTEXT" ,
364+ "TINYBLOB" , "BLOB" , "MEDIUMBLOB" , "LONGBLOB" ,
365+ "ENUM" , "SET" :
366+ return formatTableColSQLType (* cloneOriginalIcebergTableCol (col )), true
367+ default :
368+ return "" , false
369+ }
370+ }
371+
372+ func cloneOriginalIcebergTableCol (col sinkiceberg.Column ) * cloudstorage.TableCol {
373+ if col .OriginalTableCol == nil {
374+ return nil
375+ }
376+
377+ cloned := * col .OriginalTableCol
378+ cloned .Name = col .Name
379+ if col .Required {
380+ cloned .Nullable = "false"
381+ } else {
382+ cloned .Nullable = ""
383+ }
384+ return & cloned
385+ }
386+
387+ func formatTableColSQLType (col cloudstorage.TableCol ) string {
388+ tp := strings .ToUpper (strings .TrimSpace (col .Tp ))
389+ baseType := strings .TrimSuffix (tp , " UNSIGNED" )
390+
391+ switch baseType {
392+ case "CHAR" , "VARCHAR" , "VAR_STRING" , "BINARY" , "VARBINARY" , "BIT" :
393+ if col .Precision != "" {
394+ return fmt .Sprintf ("%s(%s)" , tp , col .Precision )
395+ }
396+ case "ENUM" , "SET" :
397+ if len (col .Elems ) > 0 {
398+ return fmt .Sprintf ("%s(%s)" , tp , quoteSQLStringList (col .Elems ))
399+ }
400+ }
401+ return tp
402+ }
403+
404+ func quoteSQLStringList (values []string ) string {
405+ quoted := make ([]string , 0 , len (values ))
406+ for _ , value := range values {
407+ escaped := strings .ReplaceAll (value , "\\ " , "\\ \\ " )
408+ escaped = strings .ReplaceAll (escaped , "'" , "''" )
409+ quoted = append (quoted , fmt .Sprintf ("'%s'" , escaped ))
410+ }
411+ return strings .Join (quoted , "," )
412+ }
413+
321414func parseIcebergDecimalType (raw string ) (int , int , bool ) {
322415 s := strings .TrimSpace (raw )
323416 if ! strings .HasPrefix (s , "decimal(" ) || ! strings .HasSuffix (s , ")" ) {
@@ -514,7 +607,7 @@ func (c *consumer) handleIceberg(ctx context.Context, round uint64) error {
514607 }
515608 }
516609
517- if err := c .flushDMLEvents (ctx , tableID ); err != nil {
610+ if err := c .flushIcebergDMLEvents (ctx , tableID ); err != nil {
518611 return err
519612 }
520613 state .lastMetadataVersion = metadataVersion
0 commit comments