github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sink/codec/debezium/codec.go (about) 1 // Copyright 2024 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package debezium 15 16 import ( 17 "bytes" 18 "encoding/binary" 19 "fmt" 20 "io" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/pingcap/log" 26 "github.com/pingcap/tidb/pkg/parser/mysql" 27 "github.com/pingcap/tidb/pkg/types" 28 "github.com/pingcap/tidb/pkg/util/hack" 29 "github.com/pingcap/tiflow/cdc/model" 30 cerror "github.com/pingcap/tiflow/pkg/errors" 31 "github.com/pingcap/tiflow/pkg/sink/codec/common" 32 "github.com/pingcap/tiflow/pkg/util" 33 "github.com/tikv/client-go/v2/oracle" 34 "go.uber.org/zap" 35 ) 36 37 type dbzCodec struct { 38 config *common.Config 39 clusterID string 40 nowFunc func() time.Time 41 } 42 43 func (c *dbzCodec) writeDebeziumFieldValues( 44 writer *util.JSONWriter, 45 fieldName string, 46 cols []*model.Column, 47 tableInfo *model.TableInfo, 48 ) error { 49 var err error 50 colInfos := tableInfo.GetColInfosForRowChangedEvent() 51 writer.WriteObjectField(fieldName, func() { 52 for i, col := range cols { 53 err = c.writeDebeziumFieldValue(writer, col, colInfos[i].Ft) 54 if err != nil { 55 break 56 } 57 } 58 }) 59 return err 60 } 61 62 func (c *dbzCodec) writeDebeziumFieldSchema( 63 writer *util.JSONWriter, 64 col *model.Column, 65 ft *types.FieldType, 66 ) { 67 switch col.Type { 68 case mysql.TypeBit: 69 n := ft.GetFlen() 70 if n == 1 { 71 writer.WriteObjectElement(func() { 72 writer.WriteStringField("type", "boolean") 73 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 74 writer.WriteStringField("field", col.Name) 75 }) 76 } else { 77 writer.WriteObjectElement(func() { 78 writer.WriteStringField("type", "bytes") 79 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 80 writer.WriteStringField("name", "io.debezium.data.Bits") 81 writer.WriteIntField("version", 1) 82 writer.WriteObjectField("parameters", func() { 83 writer.WriteStringField("length", fmt.Sprintf("%d", n)) 84 }) 85 writer.WriteStringField("field", col.Name) 86 }) 87 } 88 89 case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeTinyBlob, 90 mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: 91 writer.WriteObjectElement(func() { 92 writer.WriteStringField("type", "string") 93 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 94 writer.WriteStringField("field", col.Name) 95 }) 96 97 case mysql.TypeEnum: 98 writer.WriteObjectElement(func() { 99 writer.WriteStringField("type", "string") 100 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 101 writer.WriteStringField("name", "io.debezium.data.Enum") 102 writer.WriteIntField("version", 1) 103 writer.WriteObjectField("parameters", func() { 104 writer.WriteStringField("allowed", strings.Join(ft.GetElems(), ",")) 105 }) 106 writer.WriteStringField("field", col.Name) 107 }) 108 109 case mysql.TypeSet: 110 writer.WriteObjectElement(func() { 111 writer.WriteStringField("type", "string") 112 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 113 writer.WriteStringField("name", "io.debezium.data.EnumSet") 114 writer.WriteIntField("version", 1) 115 writer.WriteObjectField("parameters", func() { 116 writer.WriteStringField("allowed", strings.Join(ft.GetElems(), ",")) 117 }) 118 writer.WriteStringField("field", col.Name) 119 }) 120 121 case mysql.TypeNewDecimal: 122 writer.WriteObjectElement(func() { 123 writer.WriteStringField("type", "double") 124 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 125 writer.WriteStringField("field", col.Name) 126 }) 127 128 case mysql.TypeDate, mysql.TypeNewDate: 129 writer.WriteObjectElement(func() { 130 writer.WriteStringField("type", "int32") 131 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 132 writer.WriteStringField("name", "io.debezium.time.Date") 133 writer.WriteIntField("version", 1) 134 writer.WriteStringField("field", col.Name) 135 }) 136 137 case mysql.TypeDatetime: 138 writer.WriteObjectElement(func() { 139 writer.WriteStringField("type", "int64") 140 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 141 if ft.GetDecimal() <= 3 { 142 writer.WriteStringField("name", "io.debezium.time.Timestamp") 143 } else { 144 writer.WriteStringField("name", "io.debezium.time.MicroTimestamp") 145 } 146 writer.WriteIntField("version", 1) 147 writer.WriteStringField("field", col.Name) 148 }) 149 150 case mysql.TypeTimestamp: 151 writer.WriteObjectElement(func() { 152 writer.WriteStringField("type", "string") 153 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 154 writer.WriteStringField("name", "io.debezium.time.ZonedTimestamp") 155 writer.WriteIntField("version", 1) 156 writer.WriteStringField("field", col.Name) 157 }) 158 159 case mysql.TypeDuration: 160 writer.WriteObjectElement(func() { 161 writer.WriteStringField("type", "int64") 162 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 163 writer.WriteStringField("name", "io.debezium.time.MicroTime") 164 writer.WriteIntField("version", 1) 165 writer.WriteStringField("field", col.Name) 166 }) 167 168 case mysql.TypeJSON: 169 writer.WriteObjectElement(func() { 170 writer.WriteStringField("type", "string") 171 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 172 writer.WriteStringField("name", "io.debezium.data.Json") 173 writer.WriteIntField("version", 1) 174 writer.WriteStringField("field", col.Name) 175 }) 176 177 case mysql.TypeTiny: // TINYINT 178 writer.WriteObjectElement(func() { 179 writer.WriteStringField("type", "int16") 180 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 181 writer.WriteStringField("field", col.Name) 182 }) 183 184 case mysql.TypeShort: // SMALLINT 185 writer.WriteObjectElement(func() { 186 if mysql.HasUnsignedFlag(ft.GetFlag()) { 187 writer.WriteStringField("type", "int32") 188 } else { 189 writer.WriteStringField("type", "int16") 190 } 191 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 192 writer.WriteStringField("field", col.Name) 193 }) 194 195 case mysql.TypeInt24: // MEDIUMINT 196 writer.WriteObjectElement(func() { 197 writer.WriteStringField("type", "int32") 198 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 199 writer.WriteStringField("field", col.Name) 200 }) 201 202 case mysql.TypeLong: // INT 203 writer.WriteObjectElement(func() { 204 if mysql.HasUnsignedFlag(ft.GetFlag()) { 205 writer.WriteStringField("type", "int64") 206 } else { 207 writer.WriteStringField("type", "int32") 208 } 209 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 210 writer.WriteStringField("field", col.Name) 211 }) 212 213 case mysql.TypeLonglong: // BIGINT 214 writer.WriteObjectElement(func() { 215 writer.WriteStringField("type", "int64") 216 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 217 writer.WriteStringField("field", col.Name) 218 }) 219 220 case mysql.TypeFloat: 221 writer.WriteObjectElement(func() { 222 writer.WriteStringField("type", "float") 223 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 224 writer.WriteStringField("field", col.Name) 225 }) 226 227 case mysql.TypeDouble: 228 writer.WriteObjectElement(func() { 229 writer.WriteStringField("type", "double") 230 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 231 writer.WriteStringField("field", col.Name) 232 }) 233 234 case mysql.TypeYear: 235 writer.WriteObjectElement(func() { 236 writer.WriteStringField("type", "int32") 237 writer.WriteBoolField("optional", !mysql.HasNotNullFlag(ft.GetFlag())) 238 writer.WriteStringField("name", "io.debezium.time.Year") 239 writer.WriteIntField("version", 1) 240 writer.WriteStringField("field", col.Name) 241 }) 242 243 default: 244 log.Warn( 245 "meet unsupported field type", 246 zap.Any("fieldType", col.Type), 247 zap.Any("column", col.Name), 248 ) 249 } 250 } 251 252 // See https://debezium.io/documentation/reference/stable/connectors/mysql.html#mysql-data-types 253 // 254 //revive:disable indent-error-flow 255 func (c *dbzCodec) writeDebeziumFieldValue( 256 writer *util.JSONWriter, 257 col *model.Column, 258 ft *types.FieldType, 259 ) error { 260 if col.Value == nil { 261 writer.WriteNullField(col.Name) 262 return nil 263 } 264 switch col.Type { 265 case mysql.TypeBit: 266 v, ok := col.Value.(uint64) 267 if !ok { 268 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 269 "unexpected column value type %T for bit column %s", 270 col.Value, 271 col.Name) 272 } 273 274 // Debezium behavior: 275 // BIT(1) → BOOLEAN 276 // BIT(>1) → BYTES The byte[] contains the bits in little-endian form and is sized to 277 // contain the specified number of bits. 278 n := ft.GetFlen() 279 if n == 1 { 280 writer.WriteBoolField(col.Name, v != 0) 281 return nil 282 } else { 283 var buf [8]byte 284 binary.LittleEndian.PutUint64(buf[:], v) 285 numBytes := n / 8 286 if n%8 != 0 { 287 numBytes += 1 288 } 289 c.writeBinaryField(writer, col.Name, buf[:numBytes]) 290 return nil 291 } 292 293 case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeTinyBlob, 294 mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: 295 v, ok := col.Value.([]byte) 296 if !ok { 297 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 298 "unexpected column value type %T for string column %s", 299 col.Value, 300 col.Name) 301 } 302 303 if col.Flag.IsBinary() { 304 c.writeBinaryField(writer, col.Name, v) 305 return nil 306 } else { 307 writer.WriteStringField(col.Name, string(hack.String(v))) 308 return nil 309 } 310 311 case mysql.TypeEnum: 312 v, ok := col.Value.(uint64) 313 if !ok { 314 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 315 "unexpected column value type %T for enum column %s", 316 col.Value, 317 col.Name) 318 } 319 320 enumVar, err := types.ParseEnumValue(ft.GetElems(), v) 321 if err != nil { 322 // Invalid enum value inserted in non-strict mode. 323 writer.WriteStringField(col.Name, "") 324 return nil 325 } 326 327 writer.WriteStringField(col.Name, enumVar.Name) 328 return nil 329 330 case mysql.TypeSet: 331 v, ok := col.Value.(uint64) 332 if !ok { 333 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 334 "unexpected column value type %T for set column %s", 335 col.Value, 336 col.Name) 337 } 338 339 setVar, err := types.ParseSetValue(ft.GetElems(), v) 340 if err != nil { 341 // Invalid enum value inserted in non-strict mode. 342 writer.WriteStringField(col.Name, "") 343 return nil 344 } 345 346 writer.WriteStringField(col.Name, setVar.Name) 347 return nil 348 349 case mysql.TypeNewDecimal: 350 v, ok := col.Value.(string) 351 if !ok { 352 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 353 "unexpected column value type %T for decimal column %s", 354 col.Value, 355 col.Name) 356 } 357 358 floatV, err := strconv.ParseFloat(v, 64) 359 if err != nil { 360 return cerror.WrapError( 361 cerror.ErrDebeziumEncodeFailed, 362 err) 363 } 364 365 writer.WriteFloat64Field(col.Name, floatV) 366 return nil 367 368 case mysql.TypeDate, mysql.TypeNewDate: 369 v, ok := col.Value.(string) 370 if !ok { 371 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 372 "unexpected column value type %T for date column %s", 373 col.Value, 374 col.Name) 375 } 376 377 t, err := time.Parse("2006-01-02", v) 378 if err != nil { 379 // For example, time may be invalid like 1000-00-00 380 // return nil, nil 381 if mysql.HasNotNullFlag(ft.GetFlag()) { 382 writer.WriteInt64Field(col.Name, 0) 383 return nil 384 } else { 385 writer.WriteNullField(col.Name) 386 return nil 387 } 388 } 389 390 writer.WriteInt64Field(col.Name, t.Unix()/60/60/24) 391 return nil 392 393 case mysql.TypeDatetime: 394 // Debezium behavior from doc: 395 // > Such columns are converted into epoch milliseconds or microseconds based on the 396 // > column's precision by using UTC. 397 398 // TODO: For Default Value = CURRENT_TIMESTAMP, the result is incorrect. 399 v, ok := col.Value.(string) 400 if !ok { 401 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 402 "unexpected column value type %T for datetime column %s", 403 col.Value, 404 col.Name) 405 } 406 407 t, err := time.Parse("2006-01-02 15:04:05.999999", v) 408 if err != nil { 409 // For example, time may be 1000-00-00 410 if mysql.HasNotNullFlag(ft.GetFlag()) { 411 writer.WriteInt64Field(col.Name, 0) 412 return nil 413 } else { 414 writer.WriteNullField(col.Name) 415 return nil 416 } 417 } 418 419 if ft.GetDecimal() <= 3 { 420 writer.WriteInt64Field(col.Name, t.UnixMilli()) 421 return nil 422 } else { 423 writer.WriteInt64Field(col.Name, t.UnixMicro()) 424 return nil 425 } 426 427 case mysql.TypeTimestamp: 428 // Debezium behavior from doc: 429 // > The TIMESTAMP type represents a timestamp without time zone information. 430 // > It is converted by MySQL from the server (or session's) current time zone into UTC 431 // > when writing and from UTC into the server (or session's) current time zone when reading 432 // > back the value. 433 // > Such columns are converted into an equivalent io.debezium.time.ZonedTimestamp in UTC 434 // > based on the server (or session's) current time zone. The time zone will be queried from 435 // > the server by default. If this fails, it must be specified explicitly by the database 436 // > connectionTimeZone MySQL configuration option. 437 v, ok := col.Value.(string) 438 if !ok { 439 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 440 "unexpected column value type %T for timestamp column %s", 441 col.Value, 442 col.Name) 443 } 444 445 t, err := time.ParseInLocation("2006-01-02 15:04:05.999999", v, c.config.TimeZone) 446 if err != nil { 447 // For example, time may be invalid like 1000-00-00 448 if mysql.HasNotNullFlag(ft.GetFlag()) { 449 t = time.Unix(0, 0) 450 } else { 451 writer.WriteNullField(col.Name) 452 return nil 453 } 454 } 455 456 str := t.UTC().Format("2006-01-02T15:04:05") 457 fsp := ft.GetDecimal() 458 if fsp > 0 { 459 tmp := fmt.Sprintf(".%06d", t.Nanosecond()/1000) 460 str = str + tmp[:1+fsp] 461 } 462 str += "Z" 463 464 writer.WriteStringField(col.Name, str) 465 return nil 466 467 case mysql.TypeDuration: 468 // Debezium behavior from doc: 469 // > Represents the time value in microseconds and does not include 470 // > time zone information. MySQL allows M to be in the range of 0-6. 471 v, ok := col.Value.(string) 472 if !ok { 473 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 474 "unexpected column value type %T for time column %s", 475 col.Value, 476 col.Name) 477 } 478 479 d, _, _, err := types.StrToDuration(types.DefaultStmtNoWarningContext, v, ft.GetDecimal()) 480 if err != nil { 481 return cerror.WrapError( 482 cerror.ErrDebeziumEncodeFailed, 483 err) 484 } 485 486 writer.WriteInt64Field(col.Name, d.Microseconds()) 487 return nil 488 489 case mysql.TypeLonglong: 490 if col.Flag.IsUnsigned() { 491 // Handle with BIGINT UNSIGNED. 492 // Debezium always produce INT64 instead of UINT64 for BIGINT. 493 v, ok := col.Value.(uint64) 494 if !ok { 495 return cerror.ErrDebeziumEncodeFailed.GenWithStack( 496 "unexpected column value type %T for unsigned bigint column %s", 497 col.Value, 498 col.Name) 499 } 500 501 writer.WriteInt64Field(col.Name, int64(v)) 502 return nil 503 } 504 505 // Note: Although Debezium's doc claims to use INT32 for INT, but it 506 // actually uses INT64. Debezium also uses INT32 for SMALLINT. 507 // So we only handle with TypeLonglong here. 508 } 509 510 writer.WriteAnyField(col.Name, col.Value) 511 return nil 512 } 513 514 func (c *dbzCodec) writeBinaryField(writer *util.JSONWriter, fieldName string, value []byte) { 515 // TODO: Deal with different binary output later. 516 writer.WriteBase64StringField(fieldName, value) 517 } 518 519 func (c *dbzCodec) EncodeRowChangedEvent( 520 e *model.RowChangedEvent, 521 dest io.Writer, 522 ) error { 523 jWriter := util.BorrowJSONWriter(dest) 524 defer util.ReturnJSONWriter(jWriter) 525 526 commitTime := oracle.GetTimeFromTS(e.CommitTs) 527 528 var err error 529 530 jWriter.WriteObject(func() { 531 jWriter.WriteObjectField("payload", func() { 532 jWriter.WriteObjectField("source", func() { 533 jWriter.WriteStringField("version", "2.4.0.Final") 534 jWriter.WriteStringField("connector", "TiCDC") 535 jWriter.WriteStringField("name", c.clusterID) 536 // ts_ms: In the source object, ts_ms indicates the time that the change was made in the database. 537 // https://debezium.io/documentation/reference/stable/connectors/mysql.html#mysql-create-events 538 jWriter.WriteInt64Field("ts_ms", commitTime.UnixMilli()) 539 // snapshot field is a string of true,last,false,incremental 540 jWriter.WriteStringField("snapshot", "false") 541 jWriter.WriteStringField("db", e.TableInfo.GetSchemaName()) 542 jWriter.WriteStringField("table", e.TableInfo.GetTableName()) 543 jWriter.WriteInt64Field("server_id", 0) 544 jWriter.WriteNullField("gtid") 545 jWriter.WriteStringField("file", "") 546 jWriter.WriteInt64Field("pos", 0) 547 jWriter.WriteInt64Field("row", 0) 548 jWriter.WriteInt64Field("thread", 0) 549 jWriter.WriteNullField("query") 550 551 // The followings are TiDB extended fields 552 jWriter.WriteUint64Field("commit_ts", e.CommitTs) 553 jWriter.WriteStringField("cluster_id", c.clusterID) 554 }) 555 556 // ts_ms: displays the time at which the connector processed the event 557 // https://debezium.io/documentation/reference/stable/connectors/mysql.html#mysql-create-events 558 jWriter.WriteInt64Field("ts_ms", c.nowFunc().UnixMilli()) 559 jWriter.WriteNullField("transaction") 560 561 if e.IsInsert() { 562 // op: Mandatory string that describes the type of operation that caused the connector to generate the event. 563 // Valid values are: 564 // c = create 565 // u = update 566 // d = delete 567 // r = read (applies to only snapshots) 568 // https://debezium.io/documentation/reference/stable/connectors/mysql.html#mysql-create-events 569 jWriter.WriteStringField("op", "c") 570 571 // before: An optional field that specifies the state of the row before the event occurred. 572 // When the op field is c for create, the before field is null since this change event is for new content. 573 // In a delete event value, the before field contains the values that were in the row before 574 // it was deleted with the database commit. 575 jWriter.WriteNullField("before") 576 577 // after: An optional field that specifies the state of the row after the event occurred. 578 // Optional field that specifies the state of the row after the event occurred. 579 // In a delete event value, the after field is null, signifying that the row no longer exists. 580 err = c.writeDebeziumFieldValues(jWriter, "after", e.GetColumns(), e.TableInfo) 581 } else if e.IsDelete() { 582 jWriter.WriteStringField("op", "d") 583 jWriter.WriteNullField("after") 584 err = c.writeDebeziumFieldValues(jWriter, "before", e.GetPreColumns(), e.TableInfo) 585 } else if e.IsUpdate() { 586 jWriter.WriteStringField("op", "u") 587 if c.config.DebeziumOutputOldValue { 588 err = c.writeDebeziumFieldValues(jWriter, "before", e.GetPreColumns(), e.TableInfo) 589 } 590 if err == nil { 591 err = c.writeDebeziumFieldValues(jWriter, "after", e.GetColumns(), e.TableInfo) 592 } 593 } 594 }) 595 596 if !c.config.DebeziumDisableSchema { 597 jWriter.WriteObjectField("schema", func() { 598 jWriter.WriteStringField("type", "struct") 599 jWriter.WriteBoolField("optional", false) 600 jWriter.WriteStringField("name", fmt.Sprintf("%s.%s.%s.Envelope", 601 c.clusterID, 602 e.TableInfo.GetSchemaName(), 603 e.TableInfo.GetTableName())) 604 jWriter.WriteIntField("version", 1) 605 jWriter.WriteArrayField("fields", func() { 606 // schema is the same for `before` and `after`. So we build a new buffer to 607 // build the JSON, so that content can be reused. 608 var fieldsJSON string 609 { 610 fieldsBuf := &bytes.Buffer{} 611 fieldsWriter := util.BorrowJSONWriter(fieldsBuf) 612 var validCols []*model.Column 613 if e.IsInsert() { 614 validCols = e.GetColumns() 615 } else if e.IsDelete() { 616 validCols = e.GetPreColumns() 617 } else if e.IsUpdate() { 618 validCols = e.GetColumns() 619 } 620 colInfos := e.TableInfo.GetColInfosForRowChangedEvent() 621 for i, col := range validCols { 622 c.writeDebeziumFieldSchema(fieldsWriter, col, colInfos[i].Ft) 623 } 624 util.ReturnJSONWriter(fieldsWriter) 625 fieldsJSON = fieldsBuf.String() 626 } 627 jWriter.WriteObjectElement(func() { 628 jWriter.WriteStringField("type", "struct") 629 jWriter.WriteBoolField("optional", true) 630 jWriter.WriteStringField("name", fmt.Sprintf("%s.%s.%s.Value", 631 c.clusterID, 632 e.TableInfo.GetSchemaName(), 633 e.TableInfo.GetTableName())) 634 jWriter.WriteStringField("field", "before") 635 jWriter.WriteArrayField("fields", func() { 636 jWriter.WriteRaw(fieldsJSON) 637 }) 638 }) 639 jWriter.WriteObjectElement(func() { 640 jWriter.WriteStringField("type", "struct") 641 jWriter.WriteBoolField("optional", true) 642 jWriter.WriteStringField("name", fmt.Sprintf("%s.%s.%s.Value", 643 c.clusterID, 644 e.TableInfo.GetSchemaName(), 645 e.TableInfo.GetTableName())) 646 jWriter.WriteStringField("field", "after") 647 jWriter.WriteArrayField("fields", func() { 648 jWriter.WriteRaw(fieldsJSON) 649 }) 650 }) 651 jWriter.WriteObjectElement(func() { 652 jWriter.WriteStringField("type", "struct") 653 jWriter.WriteArrayField("fields", func() { 654 jWriter.WriteObjectElement(func() { 655 jWriter.WriteStringField("type", "string") 656 jWriter.WriteBoolField("optional", false) 657 jWriter.WriteStringField("field", "version") 658 }) 659 jWriter.WriteObjectElement(func() { 660 jWriter.WriteStringField("type", "string") 661 jWriter.WriteBoolField("optional", false) 662 jWriter.WriteStringField("field", "connector") 663 }) 664 jWriter.WriteObjectElement(func() { 665 jWriter.WriteStringField("type", "string") 666 jWriter.WriteBoolField("optional", false) 667 jWriter.WriteStringField("field", "name") 668 }) 669 jWriter.WriteObjectElement(func() { 670 jWriter.WriteStringField("type", "int64") 671 jWriter.WriteBoolField("optional", false) 672 jWriter.WriteStringField("field", "ts_ms") 673 }) 674 jWriter.WriteObjectElement(func() { 675 jWriter.WriteStringField("type", "string") 676 jWriter.WriteBoolField("optional", true) 677 jWriter.WriteStringField("name", "io.debezium.data.Enum") 678 jWriter.WriteIntField("version", 1) 679 jWriter.WriteObjectField("parameters", func() { 680 jWriter.WriteStringField("allowed", "true,last,false,incremental") 681 }) 682 jWriter.WriteStringField("default", "false") 683 jWriter.WriteStringField("field", "snapshot") 684 }) 685 jWriter.WriteObjectElement(func() { 686 jWriter.WriteStringField("type", "string") 687 jWriter.WriteBoolField("optional", false) 688 jWriter.WriteStringField("field", "db") 689 }) 690 jWriter.WriteObjectElement(func() { 691 jWriter.WriteStringField("type", "string") 692 jWriter.WriteBoolField("optional", true) 693 jWriter.WriteStringField("field", "sequence") 694 }) 695 jWriter.WriteObjectElement(func() { 696 jWriter.WriteStringField("type", "string") 697 jWriter.WriteBoolField("optional", true) 698 jWriter.WriteStringField("field", "table") 699 }) 700 jWriter.WriteObjectElement(func() { 701 jWriter.WriteStringField("type", "int64") 702 jWriter.WriteBoolField("optional", false) 703 jWriter.WriteStringField("field", "server_id") 704 }) 705 jWriter.WriteObjectElement(func() { 706 jWriter.WriteStringField("type", "string") 707 jWriter.WriteBoolField("optional", true) 708 jWriter.WriteStringField("field", "gtid") 709 }) 710 jWriter.WriteObjectElement(func() { 711 jWriter.WriteStringField("type", "string") 712 jWriter.WriteBoolField("optional", false) 713 jWriter.WriteStringField("field", "file") 714 }) 715 jWriter.WriteObjectElement(func() { 716 jWriter.WriteStringField("type", "int64") 717 jWriter.WriteBoolField("optional", false) 718 jWriter.WriteStringField("field", "pos") 719 }) 720 jWriter.WriteObjectElement(func() { 721 jWriter.WriteStringField("type", "int32") 722 jWriter.WriteBoolField("optional", false) 723 jWriter.WriteStringField("field", "row") 724 }) 725 jWriter.WriteObjectElement(func() { 726 jWriter.WriteStringField("type", "int64") 727 jWriter.WriteBoolField("optional", true) 728 jWriter.WriteStringField("field", "thread") 729 }) 730 jWriter.WriteObjectElement(func() { 731 jWriter.WriteStringField("type", "string") 732 jWriter.WriteBoolField("optional", true) 733 jWriter.WriteStringField("field", "query") 734 }) 735 // Below are extra TiDB fields 736 // jWriter.WriteObjectElement(func() { 737 // jWriter.WriteStringField("type", "int64") 738 // jWriter.WriteBoolField("optional", false) 739 // jWriter.WriteStringField("field", "commit_ts") 740 // }) 741 // jWriter.WriteObjectElement(func() { 742 // jWriter.WriteStringField("type", "string") 743 // jWriter.WriteBoolField("optional", false) 744 // jWriter.WriteStringField("field", "cluster_id") 745 // }) 746 }) 747 jWriter.WriteBoolField("optional", false) 748 jWriter.WriteStringField("name", "io.debezium.connector.mysql.Source") 749 jWriter.WriteStringField("field", "source") 750 }) 751 jWriter.WriteObjectElement(func() { 752 jWriter.WriteStringField("type", "string") 753 jWriter.WriteBoolField("optional", false) 754 jWriter.WriteStringField("field", "op") 755 }) 756 jWriter.WriteObjectElement(func() { 757 jWriter.WriteStringField("type", "int64") 758 jWriter.WriteBoolField("optional", true) 759 jWriter.WriteStringField("field", "ts_ms") 760 }) 761 jWriter.WriteObjectElement(func() { 762 jWriter.WriteStringField("type", "struct") 763 jWriter.WriteArrayField("fields", func() { 764 jWriter.WriteObjectElement(func() { 765 jWriter.WriteStringField("type", "string") 766 jWriter.WriteBoolField("optional", false) 767 jWriter.WriteStringField("field", "id") 768 }) 769 jWriter.WriteObjectElement(func() { 770 jWriter.WriteStringField("type", "int64") 771 jWriter.WriteBoolField("optional", false) 772 jWriter.WriteStringField("field", "total_order") 773 }) 774 jWriter.WriteObjectElement(func() { 775 jWriter.WriteStringField("type", "int64") 776 jWriter.WriteBoolField("optional", false) 777 jWriter.WriteStringField("field", "data_collection_order") 778 }) 779 }) 780 jWriter.WriteBoolField("optional", true) 781 jWriter.WriteStringField("name", "event.block") 782 jWriter.WriteIntField("version", 1) 783 jWriter.WriteStringField("field", "transaction") 784 }) 785 }) 786 }) 787 } 788 }) 789 790 return err 791 }