├── .gitignore ├── LICENSE ├── README.md ├── VERSION ├── binlog.go ├── buffer.go ├── checksum.go ├── compress.go ├── conn.go ├── driver.go ├── error.go ├── net.go ├── prot_auth.go ├── prot_binary.go ├── prot_binlog.go ├── prot_conn.go ├── prot_text.go ├── result.go ├── rows.go ├── ssl.go ├── stmt.go ├── tx.go ├── types.go ├── url.go ├── util.go └── uuid.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | 25 | # Vim swap file 26 | *.swp 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nirbhay Choubey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Go driver for MariaDB/MySQL server 2 | ======= 3 | 4 | * A native Go driver for MariaDB/MySQL database server. 5 | * License : The MIT License (MIT) 6 | * SSL support. 7 | * Compression support. 8 | * Support for Prepared Statements (PS). 9 | * Binlog API to access replication binary logs. 10 | * Extended error handling support. 11 | * Wiki : https://github.com/vaquita/mysql/wiki 12 | * GoDoc : https://godoc.org/github.com/vaquita/mysql 13 | 14 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | MAJOR_VERSION=0 2 | MINOR_VERSION=8 3 | -------------------------------------------------------------------------------- /binlog.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "encoding/binary" 29 | "fmt" 30 | "strconv" 31 | "time" 32 | ) 33 | 34 | const ( 35 | UNKNOWN_EVENT = iota 36 | START_EVENT_V3 37 | QUERY_EVENT 38 | STOP_EVENT 39 | ROTATE_EVENT 40 | INTVAR_EVENT 41 | LOAD_EVENT 42 | SLAVE_EVENT 43 | CREATE_FILE_EVENT 44 | APPEND_BLOCK_EVENT 45 | EXEC_LOAD_EVENT 46 | DELETE_FILE_EVENT 47 | NEW_LOAD_EVENT 48 | RAND_EVENT 49 | USER_VAR_EVENT 50 | FORMAT_DESCRIPTION_EVENT 51 | XID_EVENT 52 | BEGIN_LOAD_QUERY_EVENT 53 | EXECUTE_LOAD_QUERY_EVENT 54 | TABLE_MAP_EVENT 55 | PRE_GA_WRITE_ROWS_EVENT 56 | PRE_GA_UPDATE_ROWS_EVENT 57 | PRE_GA_DELETE_ROWS_EVENT 58 | WRITE_ROWS_EVENT_V1 59 | UPDATE_ROWS_EVENT_V1 60 | DELETE_ROWS_EVENT_V1 61 | INCIDENT_EVENT 62 | HEARTBEAT_LOG_EVENT 63 | IGNORABLE_LOG_EVENT 64 | ROWS_QUERY_LOG_EVENT 65 | WRITE_ROWS_EVENT 66 | UPDATE_ROWS_EVENT 67 | DELETE_ROWS_EVENT 68 | GTID_LOG_EVENT 69 | ANONYMOUS_GTID_LOG_EVENT 70 | PREVIOUS_GTIDS_LOG_EVENT 71 | TRANSACTION_CONTEXT_EVENT 72 | VIEW_CHANGE_EVENT 73 | XA_PREPARE_LOG_EVENT 74 | 75 | // new Oracle MySQL events should go right above this comment 76 | _ // MYSQL_EVENTS_END 77 | _ // MARIA_EVENTS_BEGIN 78 | 79 | // MariaDB specific event numbers start from here 80 | ANNOTATE_ROWS_EVENT = 160 81 | BINLOG_CHECKPOINT_EVENT = 161 82 | GTID_EVENT = 162 83 | GTID_LIST_EVENT = 163 84 | ) 85 | 86 | // Binlog represents the binlog context 87 | type Binlog struct { 88 | reader binlogReader 89 | index binlogIndex 90 | checksum checksumVerifier 91 | desc eventDescription 92 | tableMap *TableMapEvent 93 | p properties 94 | } 95 | 96 | type binlogReader interface { 97 | begin(index binlogIndex) error 98 | close() error 99 | next() bool 100 | event() []byte 101 | error() error 102 | } 103 | 104 | // received from format descriptor event 105 | type eventDescription struct { 106 | binlogVersion uint16 107 | serverVersion string 108 | creationTime time.Time 109 | commonHeaderLength uint8 110 | postHeaderLength []byte 111 | checksumAlg uint8 112 | } 113 | 114 | type binlogIndex struct { 115 | position uint32 116 | file string 117 | // TODO: add GTID support 118 | } 119 | 120 | func (b *Binlog) Connect(dsn string) error { 121 | var ( 122 | err error 123 | p properties 124 | ) 125 | 126 | // parse the dsn 127 | if err = p.parseUrl(dsn); err != nil { 128 | return err 129 | } 130 | 131 | switch p.scheme { 132 | case "mysql": 133 | nr := new(netReader) 134 | 135 | // initialize netReader 136 | if err = nr.init(p); err != nil { 137 | return err 138 | } else { 139 | b.reader = nr 140 | if b.checksum, err = fetchBinlogChecksum(nr.conn); err != nil { 141 | return err 142 | } 143 | } 144 | 145 | case "file": 146 | fr := new(fileReader) 147 | if err = fr.init(p); err != nil { 148 | return err 149 | } else { 150 | b.reader = fr 151 | b.checksum = new(checksumOff) 152 | } 153 | 154 | default: 155 | return myError(ErrScheme, p.scheme) 156 | 157 | } 158 | 159 | b.p = p 160 | 161 | return nil 162 | } 163 | 164 | func (b *Binlog) SetPosition(position uint32) { 165 | b.index.position = position 166 | } 167 | 168 | func (b *Binlog) GetPosition() uint32 { 169 | return b.index.position 170 | } 171 | 172 | func (b *Binlog) SetFile(file string) { 173 | b.index.file = file 174 | } 175 | 176 | func (b *Binlog) GetFile() string { 177 | return b.index.file 178 | } 179 | 180 | func (b *Binlog) Begin() error { 181 | return b.reader.begin(b.index) 182 | } 183 | 184 | func (b *Binlog) Next() bool { 185 | return b.reader.next() 186 | } 187 | 188 | func (b *Binlog) RawEvent() (re RawEvent, err error) { 189 | var ( 190 | off int 191 | end int 192 | ) 193 | 194 | err = b.Error() 195 | 196 | if err != nil { 197 | return 198 | } 199 | 200 | re.body = b.reader.event() 201 | re.header, off = parseEventHeader(re.body) 202 | 203 | end = len(re.body) 204 | end -= _BINLOG_CHECKSUM_LENGTH 205 | 206 | //if b.checksum.algorithm() != BINLOG_CHECKSUM_ALG_OFF { 207 | // // exclude the event checksum 208 | // end -= _BINLOG_CHECKSUM_LENGTH 209 | //} 210 | 211 | switch re.header.type_ { 212 | case START_EVENT_V3: 213 | ev := new(StartEventV3) 214 | b.parseStartEventV3(re.body[off:end], ev) 215 | 216 | // now that we have parsed START_EVENT_V3, we can 217 | // update binlog description 218 | b.desc.binlogVersion = ev.binlogVersion 219 | b.desc.serverVersion = ev.serverVersion 220 | b.desc.creationTime = ev.creationTime 221 | case FORMAT_DESCRIPTION_EVENT: 222 | ev := new(FormatDescriptionEvent) 223 | 224 | //end -= _BINLOG_CHECKSUM_LENGTH 225 | b.parseFormatDescriptionEvent(re.body[off:end], ev) 226 | 227 | // now that we have parsed FORMAT_DESCRIPTION_EVENT, we can 228 | // update binlog description 229 | b.desc.binlogVersion = ev.binlogVersion 230 | b.desc.serverVersion = ev.serverVersion 231 | b.desc.creationTime = ev.creationTime 232 | b.desc.commonHeaderLength = ev.commonHeaderLength 233 | // number of events 234 | b.desc.postHeaderLength = make([]byte, len(ev.postHeaderLength)) 235 | copy(b.desc.postHeaderLength, ev.postHeaderLength) 236 | // checksum algorithm 237 | b.desc.checksumAlg = ev.checksumAlg 238 | // update event checksum verifier 239 | updateChecksumVerifier(b) 240 | default: // do nothing 241 | } 242 | re.binlog = b 243 | 244 | // verify event checksum 245 | if b.p.binlogVerifyChecksum && !b.checksum.test(re.body) { 246 | err = myError(ErrEventChecksumFailure) 247 | return 248 | } 249 | 250 | return 251 | } 252 | 253 | func (b *Binlog) Close() error { 254 | return b.reader.close() 255 | } 256 | 257 | func (b *Binlog) Error() error { 258 | return b.reader.error() 259 | } 260 | 261 | type eventHeader struct { 262 | timestamp uint32 263 | type_ uint8 264 | serverId uint32 265 | size uint32 266 | position uint32 267 | flags uint16 268 | } 269 | 270 | type Event interface { 271 | Time() time.Time 272 | Type() uint8 273 | ServerId() uint32 274 | Size() uint32 275 | Position() uint32 276 | } 277 | 278 | type RawEvent struct { 279 | header eventHeader 280 | binlog *Binlog 281 | body []byte 282 | } 283 | 284 | func (e *RawEvent) Time() time.Time { 285 | return time.Unix(int64(e.header.timestamp), 0) 286 | } 287 | 288 | func (e *RawEvent) Type() uint8 { 289 | return e.header.type_ 290 | } 291 | 292 | func (e *RawEvent) Name() string { 293 | return eventName(e.header.type_) 294 | } 295 | 296 | func (e *RawEvent) ServerId() uint32 { 297 | return e.header.serverId 298 | } 299 | 300 | func (e *RawEvent) Size() uint32 { 301 | return e.header.size 302 | } 303 | 304 | func (e *RawEvent) Position() uint32 { 305 | return e.header.position 306 | } 307 | 308 | func (e *RawEvent) Body() []byte { 309 | return e.body 310 | } 311 | 312 | func (re *RawEvent) Event() Event { 313 | binlog := re.binlog 314 | header := re.header 315 | buf := re.body 316 | 317 | // move past event header, as it has already been parsed 318 | off := 19 319 | 320 | end := len(re.body) 321 | 322 | if binlog.checksum.algorithm() != BINLOG_CHECKSUM_ALG_OFF { 323 | // exclude the event checksum 324 | end -= _BINLOG_CHECKSUM_LENGTH 325 | } 326 | 327 | switch re.header.type_ { 328 | case START_EVENT_V3: 329 | ev := new(StartEventV3) 330 | ev.header = re.header 331 | 332 | /* 333 | no need to parse the payload, it has already been parsed in 334 | RawEvent(). 335 | */ 336 | desc := re.binlog.desc 337 | ev.binlogVersion = desc.binlogVersion 338 | ev.serverVersion = desc.serverVersion 339 | ev.creationTime = desc.creationTime 340 | return ev 341 | 342 | case QUERY_EVENT: 343 | ev := new(QueryEvent) 344 | ev.header = header 345 | binlog.parseQueryEvent(buf[off:end], ev) 346 | return ev 347 | 348 | case STOP_EVENT: 349 | ev := new(StopEvent) 350 | ev.header = header 351 | // STOP_EVENT has no post-header or payload 352 | return ev 353 | 354 | case ROTATE_EVENT: 355 | ev := new(RotateEvent) 356 | ev.header = header 357 | binlog.parseRotateEvent(buf[off:end], ev) 358 | return ev 359 | 360 | case INTVAR_EVENT: 361 | ev := new(IntvarEvent) 362 | ev.header = header 363 | binlog.parseIntvarEvent(buf[off:end], ev) 364 | return ev 365 | 366 | case LOAD_EVENT: 367 | fallthrough 368 | case NEW_LOAD_EVENT: 369 | ev := new(LoadEvent) 370 | ev.header = header 371 | binlog.parseLoadEvent(buf[off:end], ev) 372 | return ev 373 | 374 | case SLAVE_EVENT: 375 | ev := new(SlaveEvent) 376 | ev.header = header 377 | binlog.parseSlaveEvent(buf[off:end], ev) 378 | return ev 379 | 380 | case CREATE_FILE_EVENT: 381 | ev := new(CreateFileEvent) 382 | ev.header = header 383 | binlog.parseCreateFileEvent(buf[off:end], ev) 384 | return ev 385 | 386 | case APPEND_BLOCK_EVENT: 387 | ev := new(AppendBlockEvent) 388 | ev.header = header 389 | binlog.parseAppendBlockEvent(buf[off:end], ev) 390 | return ev 391 | 392 | case EXEC_LOAD_EVENT: 393 | ev := new(ExecLoadEvent) 394 | ev.header = header 395 | binlog.parseExecLoadEvent(buf[off:end], ev) 396 | return ev 397 | 398 | case DELETE_FILE_EVENT: 399 | ev := new(DeleteFileEvent) 400 | ev.header = header 401 | binlog.parseDeleteFileEvent(buf[off:end], ev) 402 | return ev 403 | 404 | case RAND_EVENT: 405 | ev := new(RandEvent) 406 | ev.header = header 407 | binlog.parseRandEvent(buf[off:end], ev) 408 | return ev 409 | 410 | case USER_VAR_EVENT: 411 | ev := new(UserVarEvent) 412 | ev.header = header 413 | binlog.parseUserVarEvent(buf[off:end], ev) 414 | return ev 415 | 416 | case FORMAT_DESCRIPTION_EVENT: 417 | ev := new(FormatDescriptionEvent) 418 | ev.header = header 419 | 420 | /* 421 | no need to parse the payload, it has already been parsed in 422 | RawEvent(). 423 | */ 424 | desc := re.binlog.desc 425 | ev.binlogVersion = desc.binlogVersion 426 | ev.serverVersion = desc.serverVersion 427 | ev.creationTime = desc.creationTime 428 | ev.commonHeaderLength = desc.commonHeaderLength 429 | // number of events 430 | ev.postHeaderLength = make([]byte, len(desc.postHeaderLength)) 431 | copy(ev.postHeaderLength, desc.postHeaderLength) 432 | ev.checksumAlg = desc.checksumAlg 433 | return ev 434 | 435 | case XID_EVENT: 436 | ev := new(XidEvent) 437 | ev.header = header 438 | binlog.parseXidEvent(buf[off:end], ev) 439 | return ev 440 | 441 | case BEGIN_LOAD_QUERY_EVENT: 442 | ev := new(BeginLoadQueryEvent) 443 | ev.header = header 444 | binlog.parseBeginLoadQueryEvent(buf[off:end], ev) 445 | return ev 446 | 447 | case EXECUTE_LOAD_QUERY_EVENT: 448 | ev := new(ExecuteLoadQueryEvent) 449 | ev.header = header 450 | binlog.parseExecuteLoadQueryEvent(buf[off:end], ev) 451 | return ev 452 | 453 | case TABLE_MAP_EVENT: 454 | ev := new(TableMapEvent) 455 | ev.header = header 456 | binlog.parseTableMapEvent(buf[off:end], ev) 457 | binlog.tableMap = ev 458 | return ev 459 | 460 | case PRE_GA_UPDATE_ROWS_EVENT, UPDATE_ROWS_EVENT_V1, 461 | UPDATE_ROWS_EVENT, PRE_GA_WRITE_ROWS_EVENT, 462 | WRITE_ROWS_EVENT_V1, WRITE_ROWS_EVENT, 463 | PRE_GA_DELETE_ROWS_EVENT, DELETE_ROWS_EVENT_V1, 464 | DELETE_ROWS_EVENT: 465 | ev := new(RowsEvent) 466 | ev.header = header 467 | binlog.parseRowsEvent(buf[off:end], ev) 468 | return ev 469 | 470 | case INCIDENT_EVENT: 471 | ev := new(IncidentEvent) 472 | ev.header = header 473 | binlog.parseIncidentEvent(buf[off:end], ev) 474 | return ev 475 | 476 | case HEARTBEAT_LOG_EVENT: 477 | ev := new(HeartbeatLogEvent) 478 | ev.header = header 479 | return ev 480 | 481 | case IGNORABLE_LOG_EVENT: 482 | ev := new(IgnorableLogEvent) 483 | ev.header = header 484 | return ev 485 | 486 | case ROWS_QUERY_LOG_EVENT: 487 | ev := new(RowsQueryLogEvent) 488 | ev.header = header 489 | binlog.parseRowsQueryLogEvent(buf[off:end], ev) 490 | return ev 491 | 492 | case GTID_LOG_EVENT: 493 | ev := new(GtidLogEvent) 494 | ev.header = header 495 | binlog.parseGtidLogEvent(buf[off:end], ev) 496 | return ev 497 | 498 | case ANONYMOUS_GTID_LOG_EVENT: 499 | ev := new(GtidLogEvent) 500 | ev.header = header 501 | binlog.parseGtidLogEvent(buf[off:end], ev) 502 | return ev 503 | 504 | case PREVIOUS_GTIDS_LOG_EVENT: 505 | ev := new(PreviousGtidsLogEvent) 506 | ev.header = header 507 | binlog.parsePreviousGtidsLogEvent(buf[off:end], ev) 508 | return ev 509 | 510 | case ANNOTATE_ROWS_EVENT: 511 | ev := new(AnnotateRowsEvent) 512 | ev.header = header 513 | binlog.parseAnnotateRowsEvent(buf[off:end], ev) 514 | return ev 515 | 516 | case BINLOG_CHECKPOINT_EVENT: 517 | ev := new(BinlogCheckpointEvent) 518 | ev.header = header 519 | binlog.parseBinlogCheckpointEvent(buf[off:end], ev) 520 | return ev 521 | 522 | case GTID_EVENT: 523 | ev := new(GtidEvent) 524 | ev.header = header 525 | binlog.parseGtidEvent(buf[off:end], ev) 526 | return ev 527 | 528 | case GTID_LIST_EVENT: 529 | ev := new(GtidListEvent) 530 | ev.header = header 531 | binlog.parseGtidListEvent(buf[off:end], ev) 532 | return ev 533 | 534 | default: // unimplemented events 535 | } 536 | return nil 537 | } 538 | 539 | // QUERY_EVENT 540 | type QueryEvent struct { 541 | header eventHeader 542 | slaveProxyId uint32 543 | executionTime time.Time 544 | errorCode uint16 545 | schema string 546 | query string 547 | statusVars string 548 | } 549 | 550 | func (e *QueryEvent) Time() time.Time { 551 | return time.Unix(int64(e.header.timestamp), 0) 552 | } 553 | 554 | func (e *QueryEvent) Type() uint8 { 555 | return e.header.type_ 556 | } 557 | 558 | func (e *QueryEvent) ServerId() uint32 { 559 | return e.header.serverId 560 | } 561 | 562 | func (e *QueryEvent) Size() uint32 { 563 | return e.header.size 564 | } 565 | 566 | func (e *QueryEvent) Position() uint32 { 567 | return e.header.position 568 | } 569 | 570 | func (e *QueryEvent) SlaveProxyId() uint32 { 571 | return e.slaveProxyId 572 | } 573 | 574 | func (e *QueryEvent) ExecutionTime() time.Time { 575 | return e.executionTime 576 | } 577 | 578 | func (e *QueryEvent) Error() uint16 { 579 | return e.errorCode 580 | } 581 | 582 | func (e *QueryEvent) Schema() string { 583 | return e.schema 584 | } 585 | 586 | func (e *QueryEvent) Query() string { 587 | return e.query 588 | } 589 | 590 | func (e *QueryEvent) StatusVars() string { 591 | return e.statusVars 592 | } 593 | 594 | const UNSIGNED = 1 595 | 596 | // USER_VAR_EVENT 597 | type UserVarEvent struct { 598 | header eventHeader 599 | name string 600 | null bool 601 | type_ uint8 602 | charset uint32 603 | value []byte 604 | flags uint8 605 | } 606 | 607 | func (e *UserVarEvent) Time() time.Time { 608 | return time.Unix(int64(e.header.timestamp), 0) 609 | } 610 | 611 | func (e *UserVarEvent) Type() uint8 { 612 | return e.header.type_ 613 | } 614 | 615 | func (e *UserVarEvent) ServerId() uint32 { 616 | return e.header.serverId 617 | } 618 | 619 | func (e *UserVarEvent) Size() uint32 { 620 | return e.header.size 621 | } 622 | 623 | func (e *UserVarEvent) Position() uint32 { 624 | return e.header.position 625 | } 626 | 627 | func (e *UserVarEvent) Name() string { 628 | return e.name 629 | } 630 | 631 | func (e *UserVarEvent) Value() interface{} { 632 | var unsigned bool 633 | 634 | if e.null { 635 | return nil 636 | } 637 | 638 | if (e.flags & uint8(UNSIGNED)) != 0 { 639 | unsigned = true 640 | } 641 | 642 | switch e.type_ { 643 | // string 644 | case _TYPE_STRING, _TYPE_VARCHAR, 645 | _TYPE_VARSTRING, _TYPE_ENUM, 646 | _TYPE_SET, _TYPE_BLOB, 647 | _TYPE_TINY_BLOB, _TYPE_MEDIUM_BLOB, 648 | _TYPE_LONG_BLOB, _TYPE_GEOMETRY, 649 | _TYPE_BIT, _TYPE_DECIMAL, 650 | _TYPE_NEW_DECIMAL: 651 | v, _ := parseString(e.value) 652 | return v 653 | 654 | // int64/uint64 655 | case _TYPE_LONG_LONG: 656 | if unsigned { 657 | return binary.LittleEndian.Uint64(e.value) 658 | } else { 659 | return parseInt64(e.value) 660 | } 661 | 662 | // int32/uint32 663 | case _TYPE_LONG, _TYPE_INT24: 664 | if unsigned { 665 | return binary.LittleEndian.Uint32(e.value) 666 | } else { 667 | return parseInt32(e.value) 668 | } 669 | 670 | // int16/uint6 671 | case _TYPE_SHORT: 672 | if unsigned { 673 | return binary.LittleEndian.Uint16(e.value) 674 | } else { 675 | return parseInt16(e.value) 676 | } 677 | 678 | // uint16 679 | case _TYPE_YEAR: 680 | return binary.LittleEndian.Uint16(e.value) 681 | 682 | // int8 683 | case _TYPE_TINY: 684 | if unsigned { 685 | return uint8(e.value[0]) 686 | } else { 687 | return int8(e.value[0]) 688 | } 689 | 690 | // float64 691 | case _TYPE_DOUBLE: 692 | return parseDouble(e.value) 693 | 694 | // float32 695 | case _TYPE_FLOAT: 696 | return parseFloat(e.value) 697 | 698 | // time.Time 699 | case _TYPE_DATE, _TYPE_DATETIME, 700 | _TYPE_TIMESTAMP: 701 | v, _ := parseDate(e.value) 702 | return v 703 | 704 | // time.Duration 705 | case _TYPE_TIME: 706 | v, _ := parseTime(e.value) 707 | return v 708 | 709 | // TODO: map the following unhandled types accordingly 710 | case _TYPE_NEW_DATE, _TYPE_TIMESTAMP2, 711 | _TYPE_DATETIME2, _TYPE_TIME2, 712 | _TYPE_NULL: 713 | fallthrough 714 | default: 715 | } 716 | return nil 717 | } 718 | 719 | // FORMAT_DESCRIPTION_EVENT 720 | type FormatDescriptionEvent struct { 721 | header eventHeader 722 | binlogVersion uint16 723 | serverVersion string 724 | creationTime time.Time 725 | commonHeaderLength uint8 726 | postHeaderLength []byte 727 | checksumAlg uint8 728 | } 729 | 730 | func (e *FormatDescriptionEvent) Time() time.Time { 731 | return time.Unix(int64(e.header.timestamp), 0) 732 | } 733 | 734 | func (e *FormatDescriptionEvent) Type() uint8 { 735 | return e.header.type_ 736 | } 737 | 738 | func (e *FormatDescriptionEvent) ServerId() uint32 { 739 | return e.header.serverId 740 | } 741 | 742 | func (e *FormatDescriptionEvent) Size() uint32 { 743 | return e.header.size 744 | } 745 | 746 | func (e *FormatDescriptionEvent) Position() uint32 { 747 | return e.header.position 748 | } 749 | 750 | func (e *FormatDescriptionEvent) BinlogVersion() uint16 { 751 | return e.binlogVersion 752 | } 753 | 754 | func (e *FormatDescriptionEvent) ServerVersion() string { 755 | return e.serverVersion 756 | } 757 | 758 | func (e *FormatDescriptionEvent) CreationTime() time.Time { 759 | return e.creationTime 760 | } 761 | 762 | func (e *FormatDescriptionEvent) ChecksumAlgorithm() uint8 { 763 | return e.checksumAlg 764 | } 765 | 766 | // STOP_EVENT 767 | type StopEvent struct { 768 | header eventHeader 769 | } 770 | 771 | func (e *StopEvent) Time() time.Time { 772 | return time.Unix(int64(e.header.timestamp), 0) 773 | } 774 | 775 | func (e *StopEvent) Type() uint8 { 776 | return e.header.type_ 777 | } 778 | 779 | func (e *StopEvent) ServerId() uint32 { 780 | return e.header.serverId 781 | } 782 | 783 | func (e *StopEvent) Size() uint32 { 784 | return e.header.size 785 | } 786 | 787 | func (e *StopEvent) Position() uint32 { 788 | return e.header.position 789 | } 790 | 791 | // ROTATE_EVENT 792 | type RotateEvent struct { 793 | header eventHeader 794 | position uint64 795 | file string 796 | } 797 | 798 | func (e *RotateEvent) Time() time.Time { 799 | return time.Unix(int64(e.header.timestamp), 0) 800 | } 801 | 802 | func (e *RotateEvent) Type() uint8 { 803 | return e.header.type_ 804 | } 805 | 806 | func (e *RotateEvent) ServerId() uint32 { 807 | return e.header.serverId 808 | } 809 | 810 | func (e *RotateEvent) Size() uint32 { 811 | return e.header.size 812 | } 813 | 814 | func (e *RotateEvent) Position() uint32 { 815 | return e.header.position 816 | } 817 | 818 | func (e *RotateEvent) NextFile() string { 819 | return e.file 820 | } 821 | 822 | func (e *RotateEvent) NextPosition() uint64 { 823 | return e.position 824 | } 825 | 826 | // START_EVENT_V3 827 | type StartEventV3 struct { 828 | header eventHeader 829 | binlogVersion uint16 830 | serverVersion string 831 | creationTime time.Time 832 | } 833 | 834 | func (e *StartEventV3) Time() time.Time { 835 | return time.Unix(int64(e.header.timestamp), 0) 836 | } 837 | 838 | func (e *StartEventV3) Type() uint8 { 839 | return e.header.type_ 840 | } 841 | 842 | func (e *StartEventV3) ServerId() uint32 { 843 | return e.header.serverId 844 | } 845 | 846 | func (e *StartEventV3) Size() uint32 { 847 | return e.header.size 848 | } 849 | 850 | func (e *StartEventV3) Position() uint32 { 851 | return e.header.position 852 | } 853 | 854 | func (e *StartEventV3) BinlogVersion() uint16 { 855 | return e.binlogVersion 856 | } 857 | 858 | func (e *StartEventV3) ServerVersion() string { 859 | return e.serverVersion 860 | } 861 | 862 | func (e *StartEventV3) CreationTime() time.Time { 863 | return e.creationTime 864 | } 865 | 866 | // HEARTBEAT_LOG_EVENT 867 | type HeartbeatLogEvent struct { 868 | header eventHeader 869 | } 870 | 871 | func (e *HeartbeatLogEvent) Time() time.Time { 872 | return time.Unix(int64(e.header.timestamp), 0) 873 | } 874 | 875 | func (e *HeartbeatLogEvent) Type() uint8 { 876 | return e.header.type_ 877 | } 878 | 879 | func (e *HeartbeatLogEvent) ServerId() uint32 { 880 | return e.header.serverId 881 | } 882 | 883 | func (e *HeartbeatLogEvent) Size() uint32 { 884 | return e.header.size 885 | } 886 | 887 | func (e *HeartbeatLogEvent) Position() uint32 { 888 | return e.header.position 889 | } 890 | 891 | // IGNORABLE_LOG_EVENT 892 | type IgnorableLogEvent struct { 893 | header eventHeader 894 | } 895 | 896 | func (e *IgnorableLogEvent) Time() time.Time { 897 | return time.Unix(int64(e.header.timestamp), 0) 898 | } 899 | 900 | func (e *IgnorableLogEvent) Type() uint8 { 901 | return e.header.type_ 902 | } 903 | 904 | func (e *IgnorableLogEvent) ServerId() uint32 { 905 | return e.header.serverId 906 | } 907 | 908 | func (e *IgnorableLogEvent) Size() uint32 { 909 | return e.header.size 910 | } 911 | 912 | func (e *IgnorableLogEvent) Position() uint32 { 913 | return e.header.position 914 | } 915 | 916 | // ROWS_QUERY_LOG_EVENT 917 | type RowsQueryLogEvent struct { 918 | header eventHeader 919 | query string 920 | } 921 | 922 | func (e *RowsQueryLogEvent) Time() time.Time { 923 | return time.Unix(int64(e.header.timestamp), 0) 924 | } 925 | 926 | func (e *RowsQueryLogEvent) Type() uint8 { 927 | return e.header.type_ 928 | } 929 | 930 | func (e *RowsQueryLogEvent) ServerId() uint32 { 931 | return e.header.serverId 932 | } 933 | 934 | func (e *RowsQueryLogEvent) Size() uint32 { 935 | return e.header.size 936 | } 937 | 938 | func (e *RowsQueryLogEvent) Position() uint32 { 939 | return e.header.position 940 | } 941 | 942 | func (e *RowsQueryLogEvent) Query() string { 943 | return e.query 944 | } 945 | 946 | // XID_EVENT 947 | type XidEvent struct { 948 | header eventHeader 949 | xid uint64 950 | } 951 | 952 | func (e *XidEvent) Time() time.Time { 953 | return time.Unix(int64(e.header.timestamp), 0) 954 | } 955 | 956 | func (e *XidEvent) Type() uint8 { 957 | return e.header.type_ 958 | } 959 | 960 | func (e *XidEvent) ServerId() uint32 { 961 | return e.header.serverId 962 | } 963 | 964 | func (e *XidEvent) Size() uint32 { 965 | return e.header.size 966 | } 967 | 968 | func (e *XidEvent) Position() uint32 { 969 | return e.header.position 970 | } 971 | 972 | func (e *XidEvent) Xid() uint64 { 973 | return e.xid 974 | } 975 | 976 | const ( 977 | INCIDENT_NONE = 0 978 | INCIDENT_LOST_EVENTS = 1 979 | ) 980 | 981 | // INCIDENT_EVENT 982 | type IncidentEvent struct { 983 | header eventHeader 984 | type_ uint16 985 | message string 986 | } 987 | 988 | func (e *IncidentEvent) Time() time.Time { 989 | return time.Unix(int64(e.header.timestamp), 0) 990 | } 991 | 992 | func (e *IncidentEvent) Type() uint8 { 993 | return e.header.type_ 994 | } 995 | 996 | func (e *IncidentEvent) ServerId() uint32 { 997 | return e.header.serverId 998 | } 999 | 1000 | func (e *IncidentEvent) Size() uint32 { 1001 | return e.header.size 1002 | } 1003 | 1004 | func (e *IncidentEvent) Position() uint32 { 1005 | return e.header.position 1006 | } 1007 | 1008 | func (e *IncidentEvent) IncidentType() uint16 { 1009 | return e.type_ 1010 | } 1011 | 1012 | func (e *IncidentEvent) IncidentMessage() string { 1013 | return e.message 1014 | } 1015 | 1016 | // RAND_EVENT 1017 | type RandEvent struct { 1018 | header eventHeader 1019 | seed1 uint64 1020 | seed2 uint64 1021 | } 1022 | 1023 | func (e *RandEvent) Time() time.Time { 1024 | return time.Unix(int64(e.header.timestamp), 0) 1025 | } 1026 | 1027 | func (e *RandEvent) Type() uint8 { 1028 | return e.header.type_ 1029 | } 1030 | 1031 | func (e *RandEvent) ServerId() uint32 { 1032 | return e.header.serverId 1033 | } 1034 | 1035 | func (e *RandEvent) Size() uint32 { 1036 | return e.header.size 1037 | } 1038 | 1039 | func (e *RandEvent) Position() uint32 { 1040 | return e.header.position 1041 | } 1042 | 1043 | func (e *RandEvent) Seed1() uint64 { 1044 | return e.seed1 1045 | } 1046 | 1047 | func (e *RandEvent) Seed2() uint64 { 1048 | return e.seed2 1049 | } 1050 | 1051 | const ( 1052 | INVALID_INT_EVENT = iota 1053 | LAST_INSERT_ID_EVENT 1054 | INSERT_ID_EVENT 1055 | ) 1056 | 1057 | // INTVAR_EVENT 1058 | type IntvarEvent struct { 1059 | header eventHeader 1060 | type_ uint8 1061 | value uint64 1062 | } 1063 | 1064 | func (e *IntvarEvent) Time() time.Time { 1065 | return time.Unix(int64(e.header.timestamp), 0) 1066 | } 1067 | 1068 | func (e *IntvarEvent) Type() uint8 { 1069 | return e.header.type_ 1070 | } 1071 | 1072 | func (e *IntvarEvent) ServerId() uint32 { 1073 | return e.header.serverId 1074 | } 1075 | 1076 | func (e *IntvarEvent) Size() uint32 { 1077 | return e.header.size 1078 | } 1079 | 1080 | func (e *IntvarEvent) Position() uint32 { 1081 | return e.header.position 1082 | } 1083 | 1084 | func (e *IntvarEvent) IntvarType() uint8 { 1085 | return e.type_ 1086 | } 1087 | 1088 | func (e *IntvarEvent) Value() uint64 { 1089 | return e.value 1090 | } 1091 | 1092 | // OptFlags 1093 | const ( 1094 | DUMPFILE_FLAG = 1 << iota 1095 | OPT_ENCLOSED_FLAG 1096 | REPLAVE_FLAG 1097 | IGNORE_FLAG 1098 | ) 1099 | 1100 | // EmptyFlags 1101 | const ( 1102 | FIELD_TERM_EMPTY = 1 << iota 1103 | ENCLOSED_EMPTY 1104 | LINE_TERM_EMPTY 1105 | LINE_START_EMPTY 1106 | ESCAPE_EMPTY 1107 | ) 1108 | 1109 | // LOAD_EVENT 1110 | type LoadEvent struct { 1111 | header eventHeader 1112 | slaveProxyId uint32 1113 | executionTime time.Time 1114 | skipLines uint32 1115 | fieldCount uint32 1116 | fieldTerminator string 1117 | enclosedBy string 1118 | lineTerminator string 1119 | lineStart string 1120 | escapedBy string 1121 | optFlags []byte 1122 | emptyFlags uint8 1123 | fields []string 1124 | table string 1125 | schema string 1126 | file string 1127 | } 1128 | 1129 | func (e *LoadEvent) Time() time.Time { 1130 | return time.Unix(int64(e.header.timestamp), 0) 1131 | } 1132 | 1133 | func (e *LoadEvent) Type() uint8 { 1134 | return e.header.type_ 1135 | } 1136 | 1137 | func (e *LoadEvent) ServerId() uint32 { 1138 | return e.header.serverId 1139 | } 1140 | 1141 | func (e *LoadEvent) Size() uint32 { 1142 | return e.header.size 1143 | } 1144 | 1145 | func (e *LoadEvent) Position() uint32 { 1146 | return e.header.position 1147 | } 1148 | 1149 | func (e *LoadEvent) SlaveProxyId() uint32 { 1150 | return e.slaveProxyId 1151 | } 1152 | 1153 | func (e *LoadEvent) ExecutionTime() time.Time { 1154 | return e.executionTime 1155 | } 1156 | 1157 | func (e *LoadEvent) SkipLines() uint32 { 1158 | return e.skipLines 1159 | } 1160 | 1161 | func (e *LoadEvent) FieldCount() uint32 { 1162 | return e.fieldCount 1163 | } 1164 | 1165 | func (e *LoadEvent) FieldTerminator() string { 1166 | return e.fieldTerminator 1167 | } 1168 | 1169 | func (e *LoadEvent) EnclosedBy() string { 1170 | return e.enclosedBy 1171 | } 1172 | 1173 | func (e *LoadEvent) LineTerminator() string { 1174 | return e.lineTerminator 1175 | } 1176 | 1177 | func (e *LoadEvent) LineStart() string { 1178 | return e.lineStart 1179 | } 1180 | 1181 | func (e *LoadEvent) EscapedBy() string { 1182 | return e.escapedBy 1183 | } 1184 | 1185 | func (e *LoadEvent) OptFlags() []byte { 1186 | return e.optFlags 1187 | } 1188 | 1189 | func (e *LoadEvent) EmptyFlags() uint8 { 1190 | return e.emptyFlags 1191 | } 1192 | 1193 | func (e *LoadEvent) Fields() []string { 1194 | return e.fields 1195 | } 1196 | 1197 | func (e *LoadEvent) Table() string { 1198 | return e.table 1199 | } 1200 | 1201 | func (e *LoadEvent) Schema() string { 1202 | return e.schema 1203 | } 1204 | 1205 | func (e *LoadEvent) File() string { 1206 | return e.file 1207 | } 1208 | 1209 | // SLAVE_EVENT 1210 | type SlaveEvent struct { 1211 | header eventHeader 1212 | masterPosition uint64 1213 | masterPort uint16 1214 | masterHost string 1215 | masterLog string 1216 | } 1217 | 1218 | func (e *SlaveEvent) Time() time.Time { 1219 | return time.Unix(int64(e.header.timestamp), 0) 1220 | } 1221 | 1222 | func (e *SlaveEvent) Type() uint8 { 1223 | return e.header.type_ 1224 | } 1225 | 1226 | func (e *SlaveEvent) ServerId() uint32 { 1227 | return e.header.serverId 1228 | } 1229 | 1230 | func (e *SlaveEvent) Size() uint32 { 1231 | return e.header.size 1232 | } 1233 | 1234 | func (e *SlaveEvent) Position() uint32 { 1235 | return e.header.position 1236 | } 1237 | 1238 | func (e *SlaveEvent) MasterPosition() uint64 { 1239 | return e.masterPosition 1240 | } 1241 | 1242 | func (e *SlaveEvent) MasterPort() uint16 { 1243 | return e.masterPort 1244 | } 1245 | 1246 | func (e *SlaveEvent) MasterHost() string { 1247 | return e.masterHost 1248 | } 1249 | 1250 | func (e *SlaveEvent) MasterLog() string { 1251 | return e.masterLog 1252 | } 1253 | 1254 | // CREATE_FILE_EVENT 1255 | type CreateFileEvent struct { 1256 | header eventHeader 1257 | fileId uint32 1258 | data []byte 1259 | } 1260 | 1261 | func (e *CreateFileEvent) Time() time.Time { 1262 | return time.Unix(int64(e.header.timestamp), 0) 1263 | } 1264 | 1265 | func (e *CreateFileEvent) Type() uint8 { 1266 | return e.header.type_ 1267 | } 1268 | 1269 | func (e *CreateFileEvent) ServerId() uint32 { 1270 | return e.header.serverId 1271 | } 1272 | 1273 | func (e *CreateFileEvent) Size() uint32 { 1274 | return e.header.size 1275 | } 1276 | 1277 | func (e *CreateFileEvent) Position() uint32 { 1278 | return e.header.position 1279 | } 1280 | 1281 | func (e *CreateFileEvent) FileId() uint32 { 1282 | return e.fileId 1283 | } 1284 | 1285 | func (e *CreateFileEvent) Data() []byte { 1286 | return e.data 1287 | } 1288 | 1289 | // DELETE_FILE_EVENT 1290 | type DeleteFileEvent struct { 1291 | header eventHeader 1292 | fileId uint32 1293 | } 1294 | 1295 | func (e *DeleteFileEvent) Time() time.Time { 1296 | return time.Unix(int64(e.header.timestamp), 0) 1297 | } 1298 | 1299 | func (e *DeleteFileEvent) Type() uint8 { 1300 | return e.header.type_ 1301 | } 1302 | 1303 | func (e *DeleteFileEvent) ServerId() uint32 { 1304 | return e.header.serverId 1305 | } 1306 | 1307 | func (e *DeleteFileEvent) Size() uint32 { 1308 | return e.header.size 1309 | } 1310 | 1311 | func (e *DeleteFileEvent) Position() uint32 { 1312 | return e.header.position 1313 | } 1314 | 1315 | func (e *DeleteFileEvent) FileId() uint32 { 1316 | return e.fileId 1317 | } 1318 | 1319 | // APPEND_BLOCK_EVENT 1320 | type AppendBlockEvent struct { 1321 | header eventHeader 1322 | fieldId uint32 1323 | data []byte 1324 | } 1325 | 1326 | func (e *AppendBlockEvent) Time() time.Time { 1327 | return time.Unix(int64(e.header.timestamp), 0) 1328 | } 1329 | 1330 | func (e *AppendBlockEvent) Type() uint8 { 1331 | return e.header.type_ 1332 | } 1333 | 1334 | func (e *AppendBlockEvent) ServerId() uint32 { 1335 | return e.header.serverId 1336 | } 1337 | 1338 | func (e *AppendBlockEvent) Size() uint32 { 1339 | return e.header.size 1340 | } 1341 | 1342 | func (e *AppendBlockEvent) Position() uint32 { 1343 | return e.header.position 1344 | } 1345 | 1346 | func (e *AppendBlockEvent) FieldId() uint32 { 1347 | return e.fieldId 1348 | } 1349 | 1350 | func (e *AppendBlockEvent) Data() []byte { 1351 | return e.data 1352 | } 1353 | 1354 | // EXEC_LOAD_EVENT 1355 | type ExecLoadEvent struct { 1356 | header eventHeader 1357 | fileId uint32 1358 | } 1359 | 1360 | func (e *ExecLoadEvent) Time() time.Time { 1361 | return time.Unix(int64(e.header.timestamp), 0) 1362 | } 1363 | 1364 | func (e *ExecLoadEvent) Type() uint8 { 1365 | return e.header.type_ 1366 | } 1367 | 1368 | func (e *ExecLoadEvent) ServerId() uint32 { 1369 | return e.header.serverId 1370 | } 1371 | 1372 | func (e *ExecLoadEvent) Size() uint32 { 1373 | return e.header.size 1374 | } 1375 | 1376 | func (e *ExecLoadEvent) Position() uint32 { 1377 | return e.header.position 1378 | } 1379 | 1380 | func (e *ExecLoadEvent) FileId() uint32 { 1381 | return e.fileId 1382 | } 1383 | 1384 | // BEGIN_LOAD_QUERY_EVENT 1385 | type BeginLoadQueryEvent struct { 1386 | header eventHeader 1387 | fileId uint32 1388 | data []byte 1389 | } 1390 | 1391 | func (e *BeginLoadQueryEvent) Time() time.Time { 1392 | return time.Unix(int64(e.header.timestamp), 0) 1393 | } 1394 | 1395 | func (e *BeginLoadQueryEvent) Type() uint8 { 1396 | return e.header.type_ 1397 | } 1398 | 1399 | func (e *BeginLoadQueryEvent) ServerId() uint32 { 1400 | return e.header.serverId 1401 | } 1402 | 1403 | func (e *BeginLoadQueryEvent) Size() uint32 { 1404 | return e.header.size 1405 | } 1406 | 1407 | func (e *BeginLoadQueryEvent) Position() uint32 { 1408 | return e.header.position 1409 | } 1410 | 1411 | func (e *BeginLoadQueryEvent) FileId() uint32 { 1412 | return e.fileId 1413 | } 1414 | 1415 | func (e *BeginLoadQueryEvent) Data() []byte { 1416 | return e.data 1417 | } 1418 | 1419 | // EXECUTE_LOAD_QUERY_EVENT 1420 | type ExecuteLoadQueryEvent struct { 1421 | header eventHeader 1422 | slaveProxyId uint32 1423 | executionTime time.Time 1424 | schemaLength uint8 1425 | errorCode uint16 1426 | statusVarsLength uint16 1427 | fileId uint32 1428 | startPosition uint32 1429 | endPosition uint32 1430 | dupHandlingFlags uint8 1431 | } 1432 | 1433 | func (e *ExecuteLoadQueryEvent) Time() time.Time { 1434 | return time.Unix(int64(e.header.timestamp), 0) 1435 | } 1436 | 1437 | func (e *ExecuteLoadQueryEvent) Type() uint8 { 1438 | return e.header.type_ 1439 | } 1440 | 1441 | func (e *ExecuteLoadQueryEvent) ServerId() uint32 { 1442 | return e.header.serverId 1443 | } 1444 | 1445 | func (e *ExecuteLoadQueryEvent) Size() uint32 { 1446 | return e.header.size 1447 | } 1448 | 1449 | func (e *ExecuteLoadQueryEvent) Position() uint32 { 1450 | return e.header.position 1451 | } 1452 | 1453 | type EventColumns struct { 1454 | columnCount uint16 1455 | columns []*EventColumn 1456 | 1457 | // iterator 1458 | pos uint64 1459 | closed bool 1460 | } 1461 | 1462 | type EventColumn struct { 1463 | type_ uint8 1464 | meta uint16 1465 | nullable bool 1466 | } 1467 | 1468 | // TABLE_MAP_EVENT 1469 | type TableMapEvent struct { 1470 | header eventHeader 1471 | tableId uint64 1472 | flags uint16 1473 | schema string 1474 | table string 1475 | columnCount uint64 1476 | columns []EventColumn 1477 | } 1478 | 1479 | func (e *TableMapEvent) Time() time.Time { 1480 | return time.Unix(int64(e.header.timestamp), 0) 1481 | } 1482 | 1483 | func (e *TableMapEvent) Type() uint8 { 1484 | return e.header.type_ 1485 | } 1486 | 1487 | func (e *TableMapEvent) ServerId() uint32 { 1488 | return e.header.serverId 1489 | } 1490 | 1491 | func (e *TableMapEvent) Size() uint32 { 1492 | return e.header.size 1493 | } 1494 | 1495 | func (e *TableMapEvent) Position() uint32 { 1496 | return e.header.position 1497 | } 1498 | 1499 | func (e *TableMapEvent) TableId() uint64 { 1500 | return e.tableId 1501 | } 1502 | 1503 | func (e *TableMapEvent) Flags() uint16 { 1504 | return e.flags 1505 | } 1506 | 1507 | func (e *TableMapEvent) Schema() string { 1508 | return e.schema 1509 | } 1510 | 1511 | func (e *TableMapEvent) Table() string { 1512 | return e.table 1513 | } 1514 | 1515 | func (e *TableMapEvent) ColumnCount() uint64 { 1516 | return e.columnCount 1517 | } 1518 | 1519 | type RowsEvent struct { 1520 | header eventHeader 1521 | tableId uint64 1522 | flags uint16 1523 | extraData []byte 1524 | columnCount uint64 1525 | columnsPresentBitmap1 []byte 1526 | columnsPresentBitmap2 []byte 1527 | rows1 EventRows 1528 | rows2 EventRows 1529 | } 1530 | 1531 | func (e *RowsEvent) Time() time.Time { 1532 | return time.Unix(int64(e.header.timestamp), 0) 1533 | } 1534 | 1535 | func (e *RowsEvent) Type() uint8 { 1536 | return e.header.type_ 1537 | } 1538 | 1539 | func (e *RowsEvent) ServerId() uint32 { 1540 | return e.header.serverId 1541 | } 1542 | 1543 | func (e *RowsEvent) Size() uint32 { 1544 | return e.header.size 1545 | } 1546 | 1547 | func (e *RowsEvent) Position() uint32 { 1548 | return e.header.position 1549 | } 1550 | 1551 | func (e *RowsEvent) Image() EventRows { 1552 | return e.rows1 1553 | } 1554 | 1555 | func (e *RowsEvent) AfterImage() EventRows { 1556 | return e.rows2 1557 | } 1558 | 1559 | type EventRows struct { 1560 | Rows []EventRow 1561 | 1562 | // iterator 1563 | pos uint64 1564 | closed bool 1565 | } 1566 | 1567 | type EventRow struct { 1568 | Columns []interface{} 1569 | } 1570 | 1571 | // MySQL specific events 1572 | 1573 | type MysqlGtid struct { 1574 | commitFlag bool 1575 | sourceId UUID 1576 | groupNumber int64 // transaction ID 1577 | } 1578 | 1579 | func (gtid *MysqlGtid) SourceId() UUID { 1580 | return gtid.sourceId 1581 | } 1582 | 1583 | func (gtid *MysqlGtid) GroupNumber() int64 { 1584 | return gtid.groupNumber 1585 | } 1586 | 1587 | func (gtid *MysqlGtid) String() string { 1588 | return fmt.Sprintf("%s:%d", gtid.sourceId.String(), gtid.groupNumber) 1589 | } 1590 | 1591 | type GtidLogEvent struct { 1592 | header eventHeader 1593 | gtid MysqlGtid 1594 | } 1595 | 1596 | func (e *GtidLogEvent) Time() time.Time { 1597 | return time.Unix(int64(e.header.timestamp), 0) 1598 | } 1599 | 1600 | func (e *GtidLogEvent) Type() uint8 { 1601 | return e.header.type_ 1602 | } 1603 | 1604 | func (e *GtidLogEvent) ServerId() uint32 { 1605 | return e.header.serverId 1606 | } 1607 | 1608 | func (e *GtidLogEvent) Size() uint32 { 1609 | return e.header.size 1610 | } 1611 | 1612 | func (e *GtidLogEvent) Position() uint32 { 1613 | return e.header.position 1614 | } 1615 | 1616 | func (e *GtidLogEvent) String() string { 1617 | return e.gtid.String() 1618 | } 1619 | 1620 | func (e *GtidLogEvent) Gtid() MysqlGtid { 1621 | return e.gtid 1622 | } 1623 | 1624 | type PreviousGtidsLogEvent struct { 1625 | header eventHeader 1626 | data []byte 1627 | } 1628 | 1629 | func (e *PreviousGtidsLogEvent) Time() time.Time { 1630 | return time.Unix(int64(e.header.timestamp), 0) 1631 | } 1632 | 1633 | func (e *PreviousGtidsLogEvent) Type() uint8 { 1634 | return e.header.type_ 1635 | } 1636 | 1637 | func (e *PreviousGtidsLogEvent) ServerId() uint32 { 1638 | return e.header.serverId 1639 | } 1640 | 1641 | func (e *PreviousGtidsLogEvent) Size() uint32 { 1642 | return e.header.size 1643 | } 1644 | 1645 | func (e *PreviousGtidsLogEvent) Position() uint32 { 1646 | return e.header.position 1647 | } 1648 | 1649 | func (e *PreviousGtidsLogEvent) String() string { 1650 | var ( 1651 | res string 1652 | uuid UUID 1653 | off int 1654 | ) 1655 | 1656 | // read sourceId count 1657 | sidCount := binary.LittleEndian.Uint64(e.data[off:]) 1658 | off += 8 1659 | 1660 | for i := uint64(0); i < sidCount; i++ { 1661 | 1662 | // UUID separator 1663 | if i > 0 { 1664 | res += "," 1665 | } 1666 | 1667 | // read the sourceId 1668 | copy(uuid.data[0:], e.data[off:off+16]) 1669 | res += uuid.String() 1670 | off += 16 1671 | 1672 | // read the intervals count 1673 | intervalsCount := binary.LittleEndian.Uint64(e.data[off:]) 1674 | off += 8 1675 | res += ":" 1676 | 1677 | var intervalStart, intervalEnd uint64 1678 | 1679 | for j := uint64(0); j < intervalsCount; j++ { 1680 | // intervals separator 1681 | if j > 0 { 1682 | res += ":" 1683 | } 1684 | 1685 | // read one interval 1686 | intervalStart = binary.LittleEndian.Uint64(e.data[off:]) 1687 | off += 8 1688 | intervalEnd = binary.LittleEndian.Uint64(e.data[off:]) 1689 | off += 8 1690 | res += strconv.FormatUint(intervalStart, 10) + "-" + 1691 | strconv.FormatUint(intervalEnd, 10) 1692 | } 1693 | break 1694 | } 1695 | 1696 | return res 1697 | } 1698 | 1699 | // MariaDB specific events 1700 | 1701 | // ANNOTATE_ROWS_EVENT 1702 | type AnnotateRowsEvent struct { 1703 | header eventHeader 1704 | query string 1705 | } 1706 | 1707 | func (e *AnnotateRowsEvent) Time() time.Time { 1708 | return time.Unix(int64(e.header.timestamp), 0) 1709 | } 1710 | 1711 | func (e *AnnotateRowsEvent) Type() uint8 { 1712 | return e.header.type_ 1713 | } 1714 | 1715 | func (e *AnnotateRowsEvent) ServerId() uint32 { 1716 | return e.header.serverId 1717 | } 1718 | 1719 | func (e *AnnotateRowsEvent) Size() uint32 { 1720 | return e.header.size 1721 | } 1722 | 1723 | func (e *AnnotateRowsEvent) Position() uint32 { 1724 | return e.header.position 1725 | } 1726 | 1727 | func (e *AnnotateRowsEvent) Query() string { 1728 | return e.query 1729 | } 1730 | 1731 | // BINLOG_CHECKPOINT_EVENT 1732 | type BinlogCheckpointEvent struct { 1733 | header eventHeader 1734 | fileLength uint32 1735 | file string 1736 | } 1737 | 1738 | func (e *BinlogCheckpointEvent) Time() time.Time { 1739 | return time.Unix(int64(e.header.timestamp), 0) 1740 | } 1741 | 1742 | func (e *BinlogCheckpointEvent) Type() uint8 { 1743 | return e.header.type_ 1744 | } 1745 | 1746 | func (e *BinlogCheckpointEvent) ServerId() uint32 { 1747 | return e.header.serverId 1748 | } 1749 | 1750 | func (e *BinlogCheckpointEvent) Size() uint32 { 1751 | return e.header.size 1752 | } 1753 | 1754 | func (e *BinlogCheckpointEvent) Position() uint32 { 1755 | return e.header.position 1756 | } 1757 | 1758 | func (e *BinlogCheckpointEvent) FileLength() uint32 { 1759 | return e.fileLength 1760 | } 1761 | 1762 | func (e *BinlogCheckpointEvent) File() string { 1763 | return e.file 1764 | } 1765 | 1766 | type MariadbGtid struct { 1767 | domainId uint32 1768 | serverId uint32 1769 | seqno uint64 1770 | } 1771 | 1772 | func (gtid *MariadbGtid) DomainId() uint32 { 1773 | return gtid.domainId 1774 | } 1775 | 1776 | func (gtid *MariadbGtid) ServerId() uint32 { 1777 | return gtid.serverId 1778 | } 1779 | 1780 | func (gtid *MariadbGtid) Seqno() uint64 { 1781 | return gtid.seqno 1782 | } 1783 | 1784 | func (gtid *MariadbGtid) String() string { 1785 | return fmt.Sprintf("%d-%d-%d", gtid.domainId, gtid.serverId, gtid.seqno) 1786 | } 1787 | 1788 | const ( 1789 | FL_STANDALONE = 1 << iota 1790 | FL_GROUP_COMMIT_ID 1791 | FL_TRANSACTIONAL 1792 | FL_ALLOW_PARALLEL 1793 | FL_WAITED 1794 | FL_DDL 1795 | ) 1796 | 1797 | // GTID_EVENT 1798 | type GtidEvent struct { 1799 | header eventHeader 1800 | gtid MariadbGtid 1801 | commitId uint64 1802 | flags uint8 1803 | } 1804 | 1805 | func (e *GtidEvent) Time() time.Time { 1806 | return time.Unix(int64(e.header.timestamp), 0) 1807 | } 1808 | 1809 | func (e *GtidEvent) Type() uint8 { 1810 | return e.header.type_ 1811 | } 1812 | 1813 | func (e *GtidEvent) ServerId() uint32 { 1814 | return e.header.serverId 1815 | } 1816 | 1817 | func (e *GtidEvent) Size() uint32 { 1818 | return e.header.size 1819 | } 1820 | 1821 | func (e *GtidEvent) Position() uint32 { 1822 | return e.header.position 1823 | } 1824 | 1825 | func (e *GtidEvent) String() string { 1826 | return e.gtid.String() 1827 | } 1828 | 1829 | func (e *GtidEvent) Gtid() MariadbGtid { 1830 | return e.gtid 1831 | } 1832 | 1833 | func (e *GtidEvent) CommitId() uint64 { 1834 | return e.commitId 1835 | } 1836 | 1837 | func (e *GtidEvent) Flags() uint8 { 1838 | return e.flags 1839 | } 1840 | 1841 | // GTID_LIST_EVENT 1842 | type GtidListEvent struct { 1843 | header eventHeader 1844 | count uint32 1845 | flags uint8 1846 | list []MariadbGtid 1847 | } 1848 | 1849 | func (e *GtidListEvent) Time() time.Time { 1850 | return time.Unix(int64(e.header.timestamp), 0) 1851 | } 1852 | 1853 | func (e *GtidListEvent) Type() uint8 { 1854 | return e.header.type_ 1855 | } 1856 | 1857 | func (e *GtidListEvent) ServerId() uint32 { 1858 | return e.header.serverId 1859 | } 1860 | 1861 | func (e *GtidListEvent) Size() uint32 { 1862 | return e.header.size 1863 | } 1864 | 1865 | func (e *GtidListEvent) Position() uint32 { 1866 | return e.header.position 1867 | } 1868 | 1869 | func (e *GtidListEvent) Count() uint32 { 1870 | return e.count 1871 | } 1872 | 1873 | func (e *GtidListEvent) List() []MariadbGtid { 1874 | return e.list 1875 | } 1876 | 1877 | func (e *GtidListEvent) Flags() uint8 { 1878 | return e.flags 1879 | } 1880 | 1881 | func eventName(type_ uint8) string { 1882 | switch type_ { 1883 | case START_EVENT_V3: 1884 | return "Start_v3" 1885 | case QUERY_EVENT: 1886 | return "Query" 1887 | case STOP_EVENT: 1888 | return "Stop" 1889 | case ROTATE_EVENT: 1890 | return "Rotate" 1891 | case INTVAR_EVENT: 1892 | return "Intvar" 1893 | case LOAD_EVENT: 1894 | return "Load" 1895 | case SLAVE_EVENT: 1896 | return "Slave" 1897 | case CREATE_FILE_EVENT: 1898 | return "Create_file" 1899 | case APPEND_BLOCK_EVENT: 1900 | return "Append_block" 1901 | case EXEC_LOAD_EVENT: 1902 | return "Exec_load" 1903 | case DELETE_FILE_EVENT: 1904 | return "Delete_file" 1905 | case NEW_LOAD_EVENT: 1906 | return "New_load" 1907 | case RAND_EVENT: 1908 | return "Rand" 1909 | case USER_VAR_EVENT: 1910 | return "User_var" 1911 | case FORMAT_DESCRIPTION_EVENT: 1912 | return "Format_description" 1913 | case XID_EVENT: 1914 | return "Xid" 1915 | case BEGIN_LOAD_QUERY_EVENT: 1916 | return "Begin_load_query" 1917 | case EXECUTE_LOAD_QUERY_EVENT: 1918 | return "Execute_load_query" 1919 | case TABLE_MAP_EVENT: 1920 | return "Table_map" 1921 | case PRE_GA_WRITE_ROWS_EVENT: 1922 | return "Pre_ga_write_rows" 1923 | case PRE_GA_UPDATE_ROWS_EVENT: 1924 | return "Pre_ga_update_rows" 1925 | case PRE_GA_DELETE_ROWS_EVENT: 1926 | return "Pre_ga_delete_rows" 1927 | case WRITE_ROWS_EVENT_V1: 1928 | return "Write_rows_v1" 1929 | case UPDATE_ROWS_EVENT_V1: 1930 | return "Update_rows_v1" 1931 | case DELETE_ROWS_EVENT_V1: 1932 | return "Delete_rows_v1" 1933 | case INCIDENT_EVENT: 1934 | return "Incident" 1935 | case HEARTBEAT_LOG_EVENT: 1936 | return "Heartbeat_log" 1937 | case IGNORABLE_LOG_EVENT: 1938 | return "Ignorable_log" 1939 | case ROWS_QUERY_LOG_EVENT: 1940 | return "Rows_query_log" 1941 | case WRITE_ROWS_EVENT: 1942 | return "Write_rows" 1943 | case UPDATE_ROWS_EVENT: 1944 | return "Update_rows" 1945 | case DELETE_ROWS_EVENT: 1946 | return "Delete_rows" 1947 | case GTID_LOG_EVENT: 1948 | return "Gtid_log" 1949 | case ANONYMOUS_GTID_LOG_EVENT: 1950 | return "Anonymous_gtid_log" 1951 | case PREVIOUS_GTIDS_LOG_EVENT: 1952 | return "Previous_gtids_log" 1953 | case TRANSACTION_CONTEXT_EVENT: 1954 | return "Transaction_context" 1955 | case VIEW_CHANGE_EVENT: 1956 | return "View_change" 1957 | case XA_PREPARE_LOG_EVENT: 1958 | return "Xa_prepare_log" 1959 | case ANNOTATE_ROWS_EVENT: 1960 | return "Annotate_rows" 1961 | case BINLOG_CHECKPOINT_EVENT: 1962 | return "Binlog_checkpoint" 1963 | case GTID_EVENT: 1964 | return "Gtid" 1965 | case GTID_LIST_EVENT: 1966 | return "Gtid_list" 1967 | default: 1968 | } 1969 | return "Unknown" 1970 | } 1971 | -------------------------------------------------------------------------------- /buffer.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | type buffer struct { 28 | // the buffer 29 | buff []byte 30 | 31 | // capacity of the buffer 32 | cap int 33 | 34 | // offset from which read/write should happen 35 | off int 36 | 37 | // length of useful content in the buffer 38 | length int 39 | } 40 | 41 | func (b *buffer) New(cap int) { 42 | b.off, b.length = 0, 0 43 | b.buff = make([]byte, cap) 44 | b.cap = cap 45 | } 46 | 47 | func (b *buffer) Set(length int) { 48 | b.length = length 49 | } 50 | 51 | func (b *buffer) Len() int { 52 | return b.length 53 | } 54 | 55 | func (b *buffer) Reset(cap int) ([]byte, error) { 56 | b.off = 0 57 | b.length = 0 58 | 59 | if cap > b.cap { 60 | // simply discard the old buffer and allocate a new one 61 | b.buff = make([]byte, cap) 62 | b.cap = cap 63 | } 64 | 65 | return b.buff[0:], nil 66 | } 67 | 68 | func (b *buffer) Seek(off int) { 69 | b.off = off 70 | } 71 | 72 | func (b *buffer) Tell() int { 73 | return b.off 74 | } 75 | 76 | func (b *buffer) Read(length int) []byte { 77 | beg := b.off 78 | 79 | // adjust the offset 80 | b.off += length 81 | 82 | return b.buff[beg:b.off] 83 | } 84 | 85 | func (b *buffer) Write(p []byte) (int, error) { 86 | n := copy(b.buff[b.off:], p) 87 | b.off += n 88 | b.length = b.off 89 | return n, nil 90 | } 91 | -------------------------------------------------------------------------------- /checksum.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | "encoding/binary" 30 | "hash/crc32" 31 | ) 32 | 33 | const _BINLOG_CHECKSUM_LENGTH = 4 34 | 35 | const ( 36 | BINLOG_CHECKSUM_ALG_OFF = iota 37 | BINLOG_CHECKSUM_ALG_CRC32 38 | BINLOG_CHECKSUM_ALG_END 39 | BINLOG_CHECKSUM_ALG_UNDEF = 255 40 | ) 41 | 42 | type checksumVerifier interface { 43 | algorithm() uint8 44 | test(ev []byte) bool 45 | } 46 | 47 | type checksumOff struct{} 48 | 49 | func (c *checksumOff) algorithm() uint8 { 50 | return BINLOG_CHECKSUM_ALG_OFF 51 | } 52 | 53 | func (c *checksumOff) test(ev []byte) bool { 54 | return true 55 | } 56 | 57 | type checksumCRC32IEEE struct{} 58 | 59 | func (c *checksumCRC32IEEE) algorithm() uint8 { 60 | return BINLOG_CHECKSUM_ALG_CRC32 61 | } 62 | 63 | // test verifies the checksum for an event and returns true if it 64 | // passes, false otherwise. note : the checksum computed on the master is 65 | // stored in the last _BINLOG_CHECKSUM_LENGTH bytes of the event 66 | func (c *checksumCRC32IEEE) test(ev []byte) bool { 67 | var ( 68 | checksumReceived uint32 69 | checksumComputed uint32 70 | flags uint16 71 | flags_orig uint16 72 | changed bool 73 | ) 74 | 75 | beg := len(ev) - _BINLOG_CHECKSUM_LENGTH 76 | end := beg + _BINLOG_CHECKSUM_LENGTH 77 | 78 | checksumReceived = binary.LittleEndian.Uint32(ev[beg:end]) 79 | 80 | if ev[4] == FORMAT_DESCRIPTION_EVENT { 81 | /* 82 | FD event is checksummed without the 83 | _LOG_EVENT_BINLOG_IN_USE_F flag 84 | */ 85 | flags = binary.LittleEndian.Uint16(ev[_FLAGS_OFFSET:]) 86 | if (flags & _LOG_EVENT_BINLOG_IN_USE_F) != 0 { 87 | flags_orig = flags 88 | changed = true 89 | flags &= ^uint16(_LOG_EVENT_BINLOG_IN_USE_F) 90 | binary.LittleEndian.PutUint16(ev[_FLAGS_OFFSET:], flags) 91 | } 92 | 93 | checksumComputed = crc32.ChecksumIEEE(ev[0:beg]) 94 | 95 | // restore the flags 96 | if changed { 97 | binary.LittleEndian.PutUint16(ev[_FLAGS_OFFSET:], flags_orig) 98 | } 99 | } else { 100 | 101 | checksumComputed = crc32.ChecksumIEEE(ev[0:beg]) 102 | } 103 | 104 | if checksumReceived == checksumComputed { 105 | return true 106 | } 107 | return false 108 | } 109 | 110 | // notifyChecksumAwareness notifies master of its checksum capabilities. 111 | func notifyChecksumAwareness(c *Conn) error { 112 | _, err := c.handleExec("SET @master_binlog_checksum= @@global.binlog_checksum", nil) 113 | return err 114 | } 115 | 116 | // fetchBinlogChecksum get checksum algorithm. 117 | func fetchBinlogChecksum(c *Conn) (checksumVerifier, error) { 118 | var checksum checksumVerifier 119 | checksum = new(checksumOff) 120 | rows, err := c.Query("show global variables like 'binlog_checksum'", nil) 121 | if err != nil { 122 | return checksum, err 123 | } 124 | defer rows.Close() 125 | var dest = make([]driver.Value, len(rows.Columns())) 126 | err = rows.Next(dest) 127 | if err != nil { 128 | return checksum, err 129 | } 130 | switch dest[1].(string) { 131 | case "CRC32": 132 | checksum = new(checksumCRC32IEEE) 133 | default: 134 | 135 | } 136 | 137 | return checksum, err 138 | 139 | } 140 | 141 | // updateChecksumVerifier updates the current checksum verifier 142 | func updateChecksumVerifier(b *Binlog) { 143 | // return if checksum algorithm has not changed 144 | if b.checksum.algorithm() == b.desc.checksumAlg { 145 | return 146 | } 147 | 148 | switch b.desc.checksumAlg { 149 | case BINLOG_CHECKSUM_ALG_OFF: 150 | b.checksum = new(checksumOff) 151 | case BINLOG_CHECKSUM_ALG_CRC32: 152 | b.checksum = new(checksumCRC32IEEE) 153 | default: 154 | // TODO: verify? 155 | b.checksum = new(checksumOff) 156 | } 157 | 158 | return 159 | } 160 | -------------------------------------------------------------------------------- /compress.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "bytes" 29 | "compress/zlib" 30 | "io" 31 | ) 32 | 33 | type compressRW struct { 34 | c *Conn 35 | cbuff buffer // buffer to hold compressed packet 36 | ubuff buffer // buffer to hold uncompressed packet(s) 37 | seqno uint8 // packet sequence number 38 | } 39 | 40 | func (rw *compressRW) init(c *Conn) { 41 | rw.c = c 42 | rw.cbuff.New(_INITIAL_PACKET_BUFFER_SIZE) 43 | rw.ubuff.New(_INITIAL_PACKET_BUFFER_SIZE) 44 | } 45 | 46 | // read reads a compressed protocol packet from network (when required), 47 | // uncompresses and caches it so that the uncompressed content of requested 48 | // size can be copied to given buffer. 49 | func (rw *compressRW) read(b []byte, length int) (int, error) { 50 | var ( 51 | n, unread int 52 | err error 53 | ) 54 | 55 | // unread bytes in the buffer 56 | unread = rw.ubuff.Len() - rw.ubuff.Tell() 57 | 58 | // read a compressed packet if the local buffer (ubuff) is either 59 | // empty, fully read or does not have enough requested unread bytes 60 | if length > unread { 61 | if err = rw.readCompressedPacket(unread); err != nil { 62 | return 0, err 63 | } 64 | } 65 | 66 | // fill the supplied buffer with contents from locally cached 67 | // uncompressed packet buffer of specified length. 68 | n = copy(b, rw.ubuff.Read(length)) 69 | 70 | return n, nil 71 | } 72 | 73 | // readCompressedPacket reads a compressed protocol packet from network into 74 | // the local compressed packet buffer (cbuff), uncompresses it and caches it 75 | // into the local cache for uncompressed packets (ubuff) 76 | func (rw *compressRW) readCompressedPacket(unread int) error { 77 | var ( 78 | payloadLength, origPayloadLength int 79 | cbuff, old []byte 80 | err error 81 | ) 82 | 83 | // save unread bytes from the buffer 84 | if unread > 0 { 85 | old = make([]byte, unread) 86 | copy(old, rw.ubuff.Read(unread)) 87 | } 88 | 89 | // read the compressed packet header into the compressed packet 90 | // buffer (cbuff) and parse it 91 | if cbuff, err = rw.cbuff.Reset(7); err != nil { 92 | return err 93 | } 94 | 95 | if _, err = rw.c.netRead(cbuff[0:7]); err != nil { 96 | return myError(ErrRead, err) 97 | } 98 | 99 | // packet payload length 100 | payloadLength = int(getUint24(cbuff[0:3])) 101 | 102 | // check for out-of-order packets 103 | if rw.seqno != cbuff[3] { 104 | return myError(ErrNetPacketsOutOfOrder) 105 | } 106 | 107 | // length of payload before compression 108 | origPayloadLength = int(getUint24(cbuff[4:7])) 109 | 110 | // increment the packet sequence number 111 | rw.seqno++ 112 | 113 | // error out if the packet is too big 114 | if payloadLength+7 > int(rw.c.p.maxPacketSize) { 115 | return myError(ErrNetPacketTooLarge) 116 | } 117 | 118 | // read compressed protocol packet payload from the network into 119 | // the compressed packet buffer (note: the header gets overwritten) 120 | if cbuff, err = rw.cbuff.Reset(payloadLength); err != nil { 121 | return err 122 | } 123 | if _, err = rw.c.netRead(cbuff[0:payloadLength]); err != nil { 124 | return myError(ErrRead, err) 125 | } 126 | 127 | // at this point we have the packet payload stored into the compressed 128 | // packet buffer (cbuff), uncompress (if needed) and store it into the 129 | // uncompressed packet buffer (ubuff). 130 | 131 | if origPayloadLength != 0 { // its a compressed payload 132 | var ( 133 | src io.ReadCloser 134 | ) 135 | 136 | if _, err = rw.ubuff.Reset(origPayloadLength + unread); err != nil { 137 | return err 138 | } 139 | 140 | // reload the unread bytes from old buffer 141 | if unread > 0 { 142 | rw.ubuff.Write(old) 143 | } 144 | 145 | if src, err = zlib.NewReader(bytes.NewReader(cbuff[0:payloadLength])); err != nil { 146 | return myError(ErrCompression, err) 147 | } else if _, err = io.Copy(&rw.ubuff, src); err != nil { 148 | return myError(ErrCompression, err) 149 | } 150 | } else { // its an uncompressed payload, simply copy it 151 | if _, err = rw.ubuff.Reset(payloadLength + unread); err != nil { 152 | return err 153 | } 154 | 155 | // reload the unread bytes from old buffer 156 | if unread > 0 { 157 | rw.ubuff.Write(old) 158 | } 159 | 160 | rw.ubuff.Write(cbuff[0:payloadLength]) 161 | } 162 | 163 | // reset for reading 164 | rw.ubuff.Seek(0) 165 | 166 | return nil 167 | } 168 | 169 | // write creates a compressed protocol packet with the specified payload and 170 | // writes it to the network. 171 | func (rw *compressRW) write(b []byte) (int, error) { 172 | var ( 173 | cbuff []byte 174 | n int 175 | err error 176 | ) 177 | 178 | // TODO: add a property for compression threshold 179 | if len(b) > 50 { // compress the payload 180 | if cbuff, err = rw.createCompPacket(b); err != nil { 181 | return 0, err 182 | } 183 | } else { // no need to compress the payload 184 | if cbuff, err = rw.createRegPacket(b); err != nil { 185 | return 0, err 186 | } 187 | } 188 | 189 | // increment the packet sequence number 190 | rw.seqno++ 191 | 192 | if n, err = rw.c.netWrite(cbuff); err != nil { 193 | return n, myError(ErrWrite, err) 194 | } 195 | 196 | return n, nil 197 | } 198 | 199 | // createCompPacket generates a compressed protocol packet after 200 | // compressing the given payload. 201 | func (rw *compressRW) createCompPacket(b []byte) ([]byte, error) { 202 | var ( 203 | w *zlib.Writer 204 | z bytes.Buffer 205 | cbuff []byte 206 | err error 207 | payloadLength int 208 | off int 209 | ) 210 | 211 | // TODO: add a property for compression level 212 | if w, err = zlib.NewWriterLevel(&z, zlib.DefaultCompression); err != nil { 213 | goto E 214 | } 215 | 216 | if _, err = w.Write(b); err != nil { 217 | goto E 218 | } 219 | 220 | if err = w.Close(); err != nil { 221 | goto E 222 | } 223 | 224 | payloadLength = z.Len() 225 | 226 | if cbuff, err = rw.cbuff.Reset(7 + payloadLength); err != nil { 227 | return nil, err 228 | } 229 | 230 | // compressed header 231 | // - size of compressed payload 232 | putUint24(cbuff[0:3], uint32(payloadLength)) 233 | // - packet sequence number 234 | cbuff[3] = rw.seqno 235 | // - size of payload before it was compressed 236 | putUint24(cbuff[4:7], uint32(len(b))) 237 | off += 7 238 | 239 | // copy the compressed payload 240 | off += copy(cbuff[7:], z.Bytes()) 241 | 242 | return cbuff[0:off], nil 243 | 244 | E: 245 | return nil, myError(ErrCompression, err) 246 | } 247 | 248 | // createRegPacket generates a non-compressed protocol packet from the specified 249 | // payload. 250 | func (rw *compressRW) createRegPacket(b []byte) ([]byte, error) { 251 | var ( 252 | cbuff []byte 253 | off, payloadLength int 254 | err error 255 | ) 256 | 257 | payloadLength = len(b) 258 | 259 | if cbuff, err = rw.cbuff.Reset(7 + payloadLength); err != nil { 260 | return nil, err 261 | } 262 | 263 | // compressed header 264 | // - size of compressed payload 265 | putUint24(cbuff[0:3], uint32(payloadLength)) 266 | 267 | // - packet sequence number 268 | cbuff[3] = rw.seqno 269 | 270 | // - = 0, because the payload is not being compressed 271 | putUint24(cbuff[4:7], uint32(0)) 272 | off += 7 273 | 274 | // store the payload (as is) 275 | off += copy(cbuff[7:], b) 276 | 277 | return cbuff[0:off], nil 278 | } 279 | 280 | // reset resets the packet sequence number. 281 | func (rw *compressRW) reset() { 282 | rw.seqno = 0 283 | } 284 | -------------------------------------------------------------------------------- /conn.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | ) 30 | 31 | func (c *Conn) Prepare(query string) (driver.Stmt, error) { 32 | return c.handleStmtPrepare(query) 33 | } 34 | 35 | func (c *Conn) Close() error { 36 | return c.handleQuit() 37 | } 38 | 39 | func (c *Conn) Begin() (driver.Tx, error) { 40 | if _, err := c.handleExec("START TRANSACTION", nil); err != nil { 41 | return nil, err 42 | } 43 | tx := new(Tx) 44 | tx.c = c 45 | return tx, nil 46 | } 47 | 48 | func (c *Conn) Exec(query string, args []driver.Value) (driver.Result, error) { 49 | return c.handleExec(query, args) 50 | } 51 | 52 | func (c *Conn) Query(query string, args []driver.Value) (driver.Rows, error) { 53 | return c.handleQuery(query, args) 54 | } 55 | -------------------------------------------------------------------------------- /driver.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql" 29 | "database/sql/driver" 30 | ) 31 | 32 | type Driver struct { 33 | } 34 | 35 | // init registers the driver 36 | func init() { 37 | sql.Register("vaquita", &Driver{}) 38 | } 39 | 40 | func (d Driver) Open(dsn string) (driver.Conn, error) { 41 | var ( 42 | err error 43 | p properties 44 | ) 45 | 46 | // parse the dsn 47 | if err = p.parseUrl(dsn); err != nil { 48 | return nil, err 49 | } 50 | 51 | if p.scheme != "mysql" { 52 | return nil, myError(ErrScheme, p.scheme) 53 | } 54 | 55 | return open(p) 56 | } 57 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "fmt" 29 | "time" 30 | ) 31 | 32 | type Error struct { 33 | code uint16 34 | sqlState string 35 | message string 36 | warnings uint16 37 | when time.Time 38 | } 39 | 40 | // client error codes 41 | const ( 42 | ErrWarning = 0 43 | ErrUnknown = 9000 + iota 44 | ErrConnection 45 | ErrRead 46 | ErrWrite 47 | ErrSSLSupport 48 | ErrSSLConnection 49 | ErrCompressionSupport 50 | ErrCompression 51 | ErrInvalidType 52 | ErrInvalidDSN 53 | ErrInvalidProperty 54 | ErrScheme 55 | ErrCursor 56 | ErrFile 57 | ErrInvalidPacket 58 | ErrInvalidPropertyValue 59 | ErrNetPacketTooLarge 60 | ErrNetPacketsOutOfOrder 61 | ErrEventChecksumFailure 62 | ) 63 | 64 | var errFormat = map[uint16]string{ 65 | ErrWarning: "Execution of last statement resulted in warning(s)", 66 | ErrUnknown: "Unknown error", 67 | ErrConnection: "Can't connect to the server (%s)", 68 | ErrRead: "Can't read data from connection (%s)", 69 | ErrWrite: "Can't write data to connection (%s)", 70 | ErrSSLSupport: "Server does not support SSL connection", 71 | ErrSSLConnection: "Can't establish SSL connection with the server (%s)", 72 | ErrCompressionSupport: "Server does not support packet compression", 73 | ErrCompression: "Compression error (%s)", 74 | ErrInvalidType: "Invalid type (%s)", 75 | ErrInvalidDSN: "Can't parse data source name (%s)", 76 | ErrInvalidProperty: "Invalid property '%s'", 77 | ErrScheme: "Unsupported scheme '%s'", 78 | ErrCursor: "Cursor is closed", 79 | ErrFile: "File operation failed (%s)", 80 | ErrInvalidPacket: "Invalid/unexpected packet received", 81 | ErrInvalidPropertyValue: "Invalid value for property '%s' (%v)", 82 | ErrNetPacketTooLarge: "Got a packet bigger than MaxAllowedPacket", 83 | ErrNetPacketsOutOfOrder: "Got packets out of order", 84 | ErrEventChecksumFailure: "Replication event checksum failed", 85 | } 86 | 87 | func myError(code uint16, a ...interface{}) *Error { 88 | return &Error{code: code, 89 | message: fmt.Sprintf(errFormat[code], a...), 90 | when: time.Now()} 91 | } 92 | 93 | // Error returns the formatted error message. (also required by Go's error 94 | // interface) 95 | func (e *Error) Error() string { 96 | if e.code == ErrWarning || e.code >= ErrUnknown { 97 | // client error 98 | return fmt.Sprintf("mysql: [%d] %s", e.code, e.message) 99 | } 100 | // server error 101 | return fmt.Sprintf("mysqld: [%d] (%s) %s", e.code, e.sqlState, e.message) 102 | } 103 | 104 | // Code returns the error number. 105 | func (e *Error) Code() uint16 { 106 | return e.code 107 | } 108 | 109 | // SqlState returns the SQL STATE 110 | func (e *Error) SqlState() string { 111 | return e.sqlState 112 | } 113 | 114 | // Message returns the error message. 115 | func (e *Error) Message() string { 116 | return e.message 117 | } 118 | 119 | // When returns time when error occurred. 120 | func (e *Error) When() time.Time { 121 | return e.when 122 | } 123 | 124 | func (e *Error) Warnings() uint16 { 125 | return e.warnings 126 | } 127 | -------------------------------------------------------------------------------- /net.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "net" 29 | ) 30 | 31 | // dial opens a connection with the server; prefer socket if specified. 32 | func dial(address, socket string) (net.Conn, error) { 33 | var ( 34 | c net.Conn 35 | addr string 36 | network string 37 | err error 38 | ) 39 | 40 | if socket != "" { 41 | network, addr = "socket", socket 42 | } else { 43 | network, addr = "tcp", address 44 | } 45 | 46 | if c, err = net.Dial(network, addr); err != nil { 47 | return nil, myError(ErrConnection, err) 48 | } 49 | return c, nil 50 | 51 | } 52 | 53 | // readWriter is a generic interface to read/write protocol packets to/from 54 | // the network. 55 | type readWriter interface { 56 | // init initializes the readWriter 57 | init(c *Conn) 58 | 59 | // read reads the specified number of bytes from the network and stores 60 | // them into the specified buffer. 61 | read(b []byte, length int) (int, error) 62 | 63 | // write writes the contents of the specified buffer to the network. 64 | write([]byte) (int, error) 65 | 66 | // reset can be used to performs some reset operations. 67 | reset() 68 | } 69 | 70 | // defaultReadWrited implements readWriter for non-compressed network 71 | // read/write. 72 | type defaultReadWriter struct { 73 | c *Conn 74 | } 75 | 76 | // init is no-op. 77 | func (rw *defaultReadWriter) init(c *Conn) { 78 | rw.c = c 79 | } 80 | 81 | // read reads the specified number of bytes from the network and stores them 82 | // into the connection buffer. 83 | func (rw *defaultReadWriter) read(b []byte, length int) (int, error) { 84 | return rw.c.netRead(b[0:length]) 85 | } 86 | 87 | // write writes the contents of the specified buffer to network. 88 | func (rw *defaultReadWriter) write(b []byte) (int, error) { 89 | return rw.c.netWrite(b) 90 | } 91 | 92 | // reset is no-op. 93 | func (rw *defaultReadWriter) reset() { 94 | } 95 | 96 | // netRead reads len(b) number of bytes from network and stores into the 97 | // given buffer. 98 | func (c *Conn) netRead(b []byte) (int, error) { 99 | var ( 100 | n, cur, end int 101 | err error 102 | ) 103 | 104 | end = len(b) 105 | 106 | for { 107 | if n, err = c.conn.Read(b[cur:end]); err != nil { 108 | 109 | cur += n 110 | return cur, myError(ErrRead, err) 111 | } 112 | cur += n 113 | if cur == end { 114 | break 115 | } 116 | } 117 | 118 | return end, nil 119 | } 120 | 121 | // netWrite writes the contents of the given buffer to the network. 122 | func (c *Conn) netWrite(b []byte) (int, error) { 123 | var ( 124 | n, cur, end int 125 | err error 126 | ) 127 | 128 | end = len(b) 129 | 130 | for { 131 | if n, err = c.conn.Write(b[cur:end]); err != nil { 132 | cur += n 133 | return cur, myError(ErrWrite, err) 134 | } 135 | cur += n 136 | if cur == end { 137 | break 138 | } 139 | } 140 | 141 | return end, nil 142 | } 143 | -------------------------------------------------------------------------------- /prot_auth.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "crypto/sha1" 29 | "encoding/binary" 30 | ) 31 | 32 | // 33 | 34 | // parseGreetingPacket parses handshake initialization packet received from 35 | // the server. 36 | func (c *Conn) parseGreetingPacket(b []byte) { 37 | var ( 38 | off, n int 39 | authData []byte // authentication plugin data 40 | authDataLength int 41 | authDataOff_1, authDataOff_2 int 42 | ) 43 | 44 | off++ // [0a] protocol version 45 | c.serverVersion, n = getNullTerminatedString(b[off:]) // server version (null-terminated) 46 | off += n 47 | 48 | c.connectionId = binary.LittleEndian.Uint32(b[off : off+4]) // connection ID 49 | off += 4 50 | 51 | // auth-plugin-data-part-1 (8 bytes) : note the offset & length 52 | authDataOff_1 = off 53 | authDataLength = 8 54 | off += 8 55 | 56 | off++ // [00] filler 57 | 58 | // capacity flags (lower 2 bytes) 59 | c.serverCapabilities = uint32(binary.LittleEndian.Uint16(b[off : off+2])) 60 | off += 2 61 | 62 | if len(b) > off { 63 | c.serverCharset = uint8(b[off]) 64 | off++ 65 | 66 | c.statusFlags = binary.LittleEndian.Uint16(b[off : off+2]) // status flags 67 | off += 2 68 | // capacity flags (upper 2 bytes) 69 | c.serverCapabilities |= (uint32(binary.LittleEndian.Uint16(b[off:off+2])) << 16) 70 | off += 2 71 | 72 | if (c.serverCapabilities & _CLIENT_PLUGIN_AUTH) != 0 { 73 | // update the auth plugin data length 74 | authDataLength = int(b[off]) 75 | off++ 76 | } else { 77 | off++ // [00] 78 | } 79 | 80 | off += 10 // reserved (all [00]) 81 | 82 | if (c.serverCapabilities & _CLIENT_SECURE_CONNECTION) != 0 { 83 | // auth-plugin-data-part-2 : note the offset & update 84 | // the length (max(13, authDataLength- 8) 85 | if (authDataLength - 8) > 13 { 86 | authDataLength = 13 + 8 87 | } 88 | authDataOff_2 = off 89 | off += (authDataLength - 8) 90 | authDataLength-- // ignore the 13th 0x00 byte 91 | } 92 | authData = make([]byte, authDataLength) 93 | copy(authData[0:8], b[authDataOff_1:authDataOff_1+8]) 94 | if authDataLength > 8 { 95 | copy(authData[8:], b[authDataOff_2:authDataOff_2+(authDataLength-8)]) 96 | } 97 | 98 | c.authPluginData = authData 99 | 100 | if (c.serverCapabilities & _CLIENT_PLUGIN_AUTH) != 0 { 101 | // auth-plugin name (null-terminated) 102 | c.authPluginName, n = getNullTerminatedString(b[off:]) 103 | off += n 104 | } 105 | } 106 | } 107 | 108 | // createHandshakeResponsePacket generates the handshake response packet. 109 | func (c *Conn) createHandshakeResponsePacket() ([]byte, error) { 110 | var ( 111 | authData []byte // auth response data 112 | b []byte 113 | off, payloadLength int 114 | err error 115 | ) 116 | 117 | payloadLength = (4 + 4 + 1 + 23) 118 | 119 | authData = c.authResponseData() 120 | payloadLength += c.handshakeResponse2Length(len(authData)) 121 | 122 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 123 | return nil, err 124 | } 125 | 126 | off += 4 // placeholder for protocol packet header 127 | off += c.populateHandshakeResponse1(b[off:]) 128 | off += c.populateHandshakeResponse2(b[off:], authData) 129 | 130 | return b[0:off], nil 131 | } 132 | 133 | // createSSLRequestPacket generates the SSL request packet to initiate SSL 134 | // handshake. It is sent to the server over plain connection after which the 135 | // communication switches to SSL. 136 | func (c *Conn) createSSLRequestPacket() ([]byte, error) { 137 | var ( 138 | b []byte 139 | off, payloadLength int 140 | err error 141 | ) 142 | 143 | payloadLength = (4 + 4 + 1 + 23) 144 | 145 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 146 | return nil, err 147 | } 148 | 149 | off += 4 150 | off += c.populateHandshakeResponse1(b[4:]) 151 | 152 | return b[0:off], nil 153 | } 154 | 155 | // populateHandshakeResponse1 populates the specified slice with the 156 | // information from 1st part of protocol's handshake response packet 157 | // (before user name) and returns the final offset. 158 | func (c *Conn) populateHandshakeResponse1(b []byte) int { 159 | var off int 160 | 161 | // client capability flags 162 | binary.LittleEndian.PutUint32(b[off:off+4], c.p.clientCapabilities) 163 | off += 4 164 | 165 | // max packet size 166 | binary.LittleEndian.PutUint32(b[off:off+4], c.p.maxPacketSize) 167 | off += 4 168 | 169 | // client character set 170 | b[off] = byte(c.clientCharset) // client character set 171 | off++ 172 | 173 | zerofy(b[off : off+23]) 174 | off += 23 // reserved (all [0]) 175 | 176 | return off 177 | } 178 | 179 | // populateHandshakeResponse2 populates the specified slice with the 180 | // information from 2st part of protocol's handshake response packet 181 | // (starting user name) and returns the final offset. 182 | func (c *Conn) populateHandshakeResponse2(b []byte, authData []byte) int { 183 | var off int 184 | 185 | off += putNullTerminatedString(b[off:], c.p.username) 186 | 187 | if (c.serverCapabilities & _CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) != 0 { 188 | off += putLenencString(b[off:], string(authData)) 189 | } else if (c.serverCapabilities & _CLIENT_SECURE_CONNECTION) != 0 { 190 | b[off] = byte(len(authData)) 191 | off++ 192 | off += copy(b[off:], authData) 193 | } else { 194 | off += putNullTerminatedString(b[off:], string(authData)) 195 | } 196 | 197 | if (c.p.schema != "") && ((c.serverCapabilities & _CLIENT_CONNECT_WITH_DB) != 0) { 198 | off += putNullTerminatedString(b[off:], c.p.schema) 199 | } 200 | 201 | if (c.serverCapabilities & _CLIENT_PLUGIN_AUTH) != 0 { 202 | off += putNullTerminatedString(b[off:], c.authPluginName) 203 | } 204 | 205 | if (c.serverCapabilities & _CLIENT_CONNECT_ATTRS) != 0 { 206 | // TODO: handle connection attributes 207 | } 208 | return off 209 | } 210 | 211 | // handshakeResponse2Length returns the extra payload length of the handshake 212 | // response packet starting user name. 213 | func (c *Conn) handshakeResponse2Length(authLength int) (length int) { 214 | length += (len(c.p.username) + 1) // null-terminated username 215 | length += authLength 216 | 217 | if (c.serverCapabilities & _CLIENT_CONNECT_WITH_DB) != 0 { 218 | length += (len(c.p.schema) + 1) // null-terminated schema name 219 | } 220 | 221 | if (c.serverCapabilities & _CLIENT_PLUGIN_AUTH) != 0 { 222 | length += (len(c.authPluginName) + 1) // null-terminated authentication plugin name 223 | } 224 | 225 | if (c.serverCapabilities & _CLIENT_CONNECT_ATTRS) != 0 { 226 | // TODO: handle connection attributes 227 | } 228 | return 229 | } 230 | 231 | // handshake performs handshake during connection establishment 232 | func (c *Conn) handshake() error { 233 | var ( 234 | b []byte 235 | useSSL bool 236 | useCompression bool 237 | err error 238 | ) 239 | 240 | // read handshake initialization packet. 241 | if b, err = c.readPacket(); err != nil { 242 | return err 243 | } 244 | 245 | c.parseGreetingPacket(b) 246 | 247 | // note : server capabilities can only be checked after receiving the 248 | // "greeting" packet 249 | if c.p.clientCapabilities&_CLIENT_SSL != 0 { 250 | if c.serverCapabilities&_CLIENT_SSL == 0 { 251 | // error: client requested for SSL but server doesn't 252 | // support SSL. 253 | return myError(ErrSSLSupport) 254 | } else { 255 | useSSL = true 256 | } 257 | } 258 | 259 | if c.p.clientCapabilities&_CLIENT_COMPRESS != 0 { 260 | if c.serverCapabilities&_CLIENT_COMPRESS == 0 { 261 | // error: client requested for packet compression but server doesn't 262 | // support compression protocol. 263 | return myError(ErrCompressionSupport) 264 | } else { 265 | useCompression = true 266 | } 267 | } 268 | 269 | if !useSSL { 270 | // send plain handshake response packet 271 | if b, err = c.createHandshakeResponsePacket(); err != nil { 272 | return err 273 | } 274 | if err = c.writePacket(b); err != nil { 275 | return err 276 | } 277 | } else { 278 | // send SSL request packet (1st part of handshake response 279 | // packet) 280 | if b, err = c.createSSLRequestPacket(); err != nil { 281 | return err 282 | } 283 | if err = c.writePacket(b); err != nil { 284 | return err 285 | } 286 | 287 | // switch to tls 288 | if err = c.sslConnect(); err != nil { 289 | return err 290 | } 291 | 292 | // 293 | 294 | // now send the entire handshake response packet 295 | if b, err = c.createHandshakeResponsePacket(); err != nil { 296 | return err 297 | } 298 | if err = c.writePacket(b); err != nil { 299 | return err 300 | } 301 | } 302 | 303 | // read server response 304 | if b, err = c.readPacket(); err != nil { 305 | return err 306 | } 307 | 308 | switch b[0] { 309 | case _PACKET_ERR: 310 | c.parseErrPacket(b) 311 | return &c.e 312 | case _PACKET_OK: 313 | c.parseOkPacket(b) 314 | default: 315 | // TODO: invalid packet 316 | } 317 | 318 | if useCompression { // switch to compression protocol 319 | c.rw = &compressRW{} 320 | c.rw.init(c) 321 | // 322 | } 323 | return nil 324 | } 325 | 326 | // authResponseData returns the authentication response data to be sent to the 327 | // server. 328 | func (c *Conn) authResponseData() []byte { 329 | return scramble41(c.p.password, c.authPluginData) 330 | } 331 | 332 | // scraamble41 returns a scramble buffer based on the following formula: 333 | // SHA1(password) XOR SHA1("20-byte public seed from server" SHA1( SHA1( password))) 334 | func scramble41(password string, seed []byte) (buf []byte) { 335 | if len(password) == 0 { 336 | return 337 | } 338 | 339 | hash := sha1.New() 340 | 341 | // stage 1: SHA1(password) 342 | hash.Write([]byte(password)) 343 | hashStage1 := hash.Sum(nil) 344 | 345 | // stage 2: SHA1(SHA1(password)) 346 | hash.Reset() 347 | hash.Write(hashStage1) 348 | hashStage2 := hash.Sum(nil) 349 | 350 | // SHA1("20-byte public seed from server" SHA1(SHA1(password))) 351 | hash.Reset() 352 | hash.Write(seed) 353 | hash.Write(hashStage2) 354 | buf = hash.Sum(nil) 355 | 356 | for i := 0; i < sha1.Size; i++ { 357 | buf[i] ^= hashStage1[i] 358 | } 359 | return 360 | } 361 | -------------------------------------------------------------------------------- /prot_binary.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | "encoding/binary" 30 | "math" 31 | "math/big" 32 | "strconv" 33 | "time" 34 | ) 35 | 36 | // createComStmtPrepare generates the COM_STMT_PREPARE packet. 37 | func (c *Conn) createComStmtPrepare(query string) ([]byte, error) { 38 | var ( 39 | b []byte 40 | off, payloadLength int 41 | err error 42 | ) 43 | 44 | payloadLength = 1 + // _COM_STMT_PREPARE 45 | len(query) // length of query 46 | 47 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 48 | return nil, err 49 | } 50 | 51 | off += 4 // placeholder for protocol packet header 52 | b[off] = _COM_STMT_PREPARE 53 | off++ 54 | off += copy(b[off:], query) 55 | 56 | return b[0:off], nil 57 | } 58 | 59 | // createComStmtExecute generates the COM_STMT_EXECUTE packet. 60 | func (c *Conn) createComStmtExecute(s *Stmt, args []driver.Value) ([]byte, error) { 61 | var ( 62 | b, nullBitmap []byte 63 | off, payloadLength, nullBitmapSize, paramCount int 64 | err error 65 | ) 66 | 67 | // TODO : assert(s.paramCount == len(args)) 68 | paramCount = int(s.paramCount) 69 | 70 | // null bitmap, size = (paramCount + 7) / 8 71 | nullBitmapSize = int((paramCount + 7) / 8) 72 | 73 | payloadLength = int(comStmtExecutePayloadLength(s, args)) 74 | 75 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 76 | return nil, err 77 | } 78 | 79 | off += 4 // placeholder for protocol packet header 80 | 81 | b[off] = _COM_STMT_EXECUTE 82 | off++ 83 | 84 | binary.LittleEndian.PutUint32(b[off:off+4], s.id) 85 | off += 4 86 | 87 | b[off] = s.flags 88 | off++ 89 | 90 | binary.LittleEndian.PutUint32(b[off:off+4], s.iterationCount) 91 | off += 4 92 | 93 | if paramCount > 0 { 94 | nullBitmap = b[off : off+nullBitmapSize] 95 | // clear the null bitmap 96 | zerofy(nullBitmap) 97 | off += nullBitmapSize 98 | 99 | b[off] = s.newParamsBoundFlag 100 | off++ 101 | 102 | if s.newParamsBoundFlag == 1 { 103 | poff := off // offset to keep track of parameter types 104 | off += (2 * int(s.paramCount)) 105 | 106 | for i := 0; i < int(s.paramCount); i++ { 107 | switch v := args[i].(type) { 108 | case int64: 109 | binary.LittleEndian.PutUint16(b[poff:poff+2], uint16(_TYPE_LONG_LONG)) 110 | poff += 2 111 | off += writeUint64(b[off:], uint64(v)) 112 | case float64: 113 | binary.LittleEndian.PutUint16(b[poff:poff+2], 114 | uint16(_TYPE_DOUBLE)) 115 | poff += 2 116 | off += writeDouble(b[off:], v) 117 | case bool: 118 | binary.LittleEndian.PutUint16(b[poff:poff+2], 119 | uint16(_TYPE_TINY)) 120 | poff += 2 121 | value := uint8(0) 122 | if v == true { 123 | value = 1 124 | } 125 | off += writeUint8(b[off:], value) 126 | case []byte: 127 | binary.LittleEndian.PutUint16(b[poff:poff+2], 128 | uint16(_TYPE_BLOB)) 129 | poff += 2 130 | off += writeString(b[off:], string(v)) 131 | case string: 132 | binary.LittleEndian.PutUint16(b[poff:poff+2], 133 | uint16(_TYPE_VARCHAR)) 134 | poff += 2 135 | off += writeString(b[off:], v) 136 | case time.Time: 137 | binary.LittleEndian.PutUint16(b[poff:poff+2], 138 | uint16(_TYPE_TIMESTAMP)) 139 | poff += 2 140 | off += writeDate(b[off:], v) 141 | case nil: 142 | binary.LittleEndian.PutUint16(b[poff:poff+2], 143 | uint16(_TYPE_NULL)) 144 | poff += 2 145 | // set the corresponding null bit 146 | nullBitmap[int(i/8)] |= 1 << uint(i%8) 147 | default: 148 | // TODO: handle error 149 | } 150 | } 151 | } 152 | } 153 | 154 | return b[0:off], nil 155 | } 156 | 157 | // createComStmtClose generates the COM_STMT_CLOSE packet. 158 | func (c *Conn) createComStmtClose(sid uint32) ([]byte, error) { 159 | var ( 160 | b []byte 161 | off, payloadLength int 162 | err error 163 | ) 164 | 165 | payloadLength = 5 // _COM_STMT_CLOSE(1) + s.id(4) 166 | 167 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 168 | return nil, err 169 | } 170 | 171 | off += 4 // placeholder for protocol packet header 172 | 173 | b[off] = _COM_STMT_CLOSE 174 | off++ 175 | 176 | binary.LittleEndian.PutUint32(b[off:off+4], sid) 177 | off += 4 178 | 179 | return b[0:off], nil 180 | } 181 | 182 | // createComStmtReset generates the COM_STMT_RESET packet. 183 | func (c *Conn) createComStmtReset(s *Stmt) ([]byte, error) { 184 | var ( 185 | b []byte 186 | off, payloadLength int 187 | err error 188 | ) 189 | 190 | payloadLength = 5 // _COM_STMT_RESET (1) + s.id (4) 191 | 192 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 193 | return nil, err 194 | } 195 | 196 | off += 4 // placeholder for protocol packet header 197 | 198 | b[off] = _COM_STMT_RESET 199 | off++ 200 | 201 | binary.LittleEndian.PutUint32(b[off:off+4], s.id) 202 | off += 4 203 | 204 | return b[0:off], nil 205 | } 206 | 207 | // createComStmtSendLongData generates the COM_STMT_SEND_LONG_DATA packet. 208 | func (c *Conn) createComStmtSendLongData(s *Stmt, paramId uint16, data []byte) ([]byte, error) { 209 | var ( 210 | b []byte 211 | off, payloadLength int 212 | err error 213 | ) 214 | 215 | payloadLength = 7 + // _COM_STMT_SEND_LONG_DATA(1) + s.id(4) + paramId(2) 216 | len(data) // length of data 217 | 218 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 219 | return nil, err 220 | } 221 | 222 | off += 4 // placeholder for protocol packet header 223 | 224 | b[off] = _COM_STMT_SEND_LONG_DATA 225 | off++ 226 | 227 | binary.LittleEndian.PutUint32(b[off:off+4], s.id) 228 | off += 4 229 | binary.LittleEndian.PutUint16(b[off:off+2], paramId) 230 | off += 2 231 | 232 | return b[0:off], nil 233 | } 234 | 235 | // handleStmtPrepare handles COM_STMT_PREPARE and related packets 236 | func (c *Conn) handleStmtPrepare(query string) (*Stmt, error) { 237 | var ( 238 | b []byte 239 | err error 240 | ) 241 | 242 | // reset the protocol packet sequence number 243 | c.resetSeqno() 244 | 245 | if b, err = c.createComStmtPrepare(query); err != nil { 246 | return nil, err 247 | } 248 | 249 | // write COM_STMT_PREPARE packet 250 | if err = c.writePacket(b); err != nil { 251 | return nil, err 252 | } 253 | 254 | // handle the response 255 | return c.handleComStmtPrepareResponse() 256 | } 257 | 258 | func (c *Conn) handleComStmtPrepareResponse() (*Stmt, error) { 259 | var ( 260 | s *Stmt 261 | b []byte 262 | warn bool 263 | err error 264 | ) 265 | 266 | s = new(Stmt) 267 | s.c = c 268 | 269 | s.paramDefs = make([]*ColumnDefinition, 0) 270 | s.columnDefs = make([]*ColumnDefinition, 0) 271 | 272 | // read COM_STMT_PREPARE_OK packet. 273 | if b, err = c.readPacket(); err != nil { 274 | return nil, err 275 | } 276 | 277 | switch b[0] { 278 | case _PACKET_OK: // COM_STMT_PREPARE_OK packet 279 | warn = s.parseStmtPrepareOkPacket(b) 280 | case _PACKET_ERR: 281 | c.parseErrPacket(b) 282 | return nil, &c.e 283 | default: 284 | return nil, myError(ErrInvalidPacket) 285 | } 286 | 287 | more := s.paramCount > 0 // more packets ? 288 | 289 | // parameter definition block: read param definition packet(s) 290 | for more { 291 | if b, err = c.readPacket(); err != nil { 292 | return nil, err 293 | } 294 | 295 | switch b[0] { 296 | case _PACKET_EOF: // EOF packet, done! 297 | warn = c.parseEOFPacket(b) 298 | more = false 299 | default: // column definition packet 300 | s.paramDefs = append(s.paramDefs, parseColumnDefinitionPacket(b, false)) 301 | } 302 | } 303 | 304 | more = s.columnCount > 0 305 | 306 | for more { 307 | if b, err = c.readPacket(); err != nil { 308 | return nil, err 309 | } 310 | 311 | switch b[0] { 312 | case _PACKET_EOF: // EOF packet, done! 313 | warn = c.parseEOFPacket(b) 314 | more = false 315 | default: // column definition packet 316 | s.columnDefs = append(s.columnDefs, parseColumnDefinitionPacket(b, false)) 317 | } 318 | } 319 | 320 | if warn { 321 | return s, &c.e 322 | } 323 | 324 | return s, nil 325 | } 326 | 327 | // parseStmtPrepareOk parses COM_STMT_PREPARE_OK packet. 328 | func (s *Stmt) parseStmtPrepareOkPacket(b []byte) bool { 329 | var off int 330 | 331 | off++ // [00] OK 332 | s.id = binary.LittleEndian.Uint32(b[off : off+4]) 333 | off += 4 334 | s.columnCount = binary.LittleEndian.Uint16(b[off : off+2]) 335 | off += 2 336 | s.paramCount = binary.LittleEndian.Uint16(b[off : off+2]) 337 | off += 2 338 | off++ // reserved [00] filler 339 | s.warnings = binary.LittleEndian.Uint16(b[off : off+2]) 340 | off += 2 341 | 342 | s.c.warnings = s.warnings 343 | return s.c.reportWarnings() 344 | } 345 | 346 | // handleExec handles COM_STMT_EXECUTE and related packets for Stmt's Exec() 347 | func (s *Stmt) handleExec(args []driver.Value) (*Result, error) { 348 | var ( 349 | b []byte 350 | err error 351 | ) 352 | 353 | // reset the protocol packet sequence number 354 | s.c.resetSeqno() 355 | 356 | // TODO: set me appropriately 357 | s.newParamsBoundFlag = 1 358 | 359 | if b, err = s.c.createComStmtExecute(s, args); err != nil { 360 | return nil, err 361 | } 362 | 363 | // send COM_STMT_EXECUTE to the server 364 | if err = s.c.writePacket(b); err != nil { 365 | return nil, err 366 | } 367 | 368 | return s.handleExecResponse() 369 | } 370 | 371 | // handleExecute handles COM_STMT_EXECUTE and related packets for Stmt's Query() 372 | func (s *Stmt) handleQuery(args []driver.Value) (*Rows, error) { 373 | var ( 374 | b []byte 375 | err error 376 | ) 377 | 378 | // reset the protocol packet sequence number 379 | s.c.resetSeqno() 380 | 381 | // TODO: set me appropriately 382 | s.newParamsBoundFlag = 1 383 | 384 | if b, err = s.c.createComStmtExecute(s, args); err != nil { 385 | return nil, err 386 | } 387 | 388 | // send COM_STMT_EXECUTE to the server 389 | if err := s.c.writePacket(b); err != nil { 390 | return nil, err 391 | } 392 | 393 | return s.handleQueryResponse() 394 | } 395 | 396 | // comStmtExecutePayloadLength returns the payload size of COM_STMT_EXECUTE 397 | // packet. 398 | func comStmtExecutePayloadLength(s *Stmt, args []driver.Value) (length uint64) { 399 | length = 1 + //_COM_STMT_PREPARE 400 | 9 // id(4) + flags(1) + iterationCount(4) 401 | 402 | if s.paramCount > 0 { 403 | // null bitmap, size = (paramCount + 7) / 8 404 | length += uint64((s.paramCount + 7) / 8) 405 | length++ // newParamBoundFlag(1) 406 | 407 | if s.newParamsBoundFlag == 1 { 408 | length += uint64(s.paramCount * 2) // type of each paramater 409 | for i := 0; i < int(s.paramCount); i++ { 410 | switch v := args[i].(type) { 411 | case int64, float64: 412 | length += 8 413 | case bool: 414 | length++ 415 | case []byte: 416 | length += 417 | uint64(lenencIntSize(len(v)) + len(v)) 418 | case string: 419 | length += 420 | uint64(lenencIntSize(len(v)) + len(v)) 421 | case time.Time: 422 | length += uint64(dateSize(v)) 423 | case nil: // noop 424 | default: // TODO: handle error 425 | } 426 | } 427 | 428 | } 429 | } 430 | return 431 | } 432 | 433 | func (s *Stmt) handleExecResponse() (*Result, error) { 434 | var ( 435 | err error 436 | b []byte 437 | warn bool 438 | ) 439 | 440 | c := s.c 441 | 442 | if b, err = c.readPacket(); err != nil { 443 | return nil, err 444 | } 445 | 446 | switch b[0] { 447 | 448 | case _PACKET_ERR: // expected 449 | // handle err packet 450 | c.parseErrPacket(b) 451 | return nil, &c.e 452 | 453 | case _PACKET_OK: // expected 454 | // parse Ok packet and break 455 | warn = c.parseOkPacket(b) 456 | break 457 | 458 | case _PACKET_INFILE_REQ: // expected 459 | // local infile request; handle it 460 | if err = c.handleInfileRequest(string(b[1:])); err != nil { 461 | return nil, err 462 | } 463 | default: // unexpected 464 | // the command resulted in Rows (anti-pattern ?); but since it 465 | // succeeded, we handle it and return nil 466 | columnCount, _ := getLenencInt(b) 467 | _, err = c.handleBinaryResultSet(uint16(columnCount)) // Rows ignored! 468 | return nil, err 469 | } 470 | 471 | res := new(Result) 472 | res.lastInsertId = int64(c.lastInsertId) 473 | res.rowsAffected = int64(c.affectedRows) 474 | 475 | if warn { 476 | // command resulted in warning(s), return results and error 477 | return res, &c.e 478 | } 479 | 480 | return res, nil 481 | } 482 | 483 | func (s *Stmt) handleQueryResponse() (*Rows, error) { 484 | var ( 485 | err error 486 | b []byte 487 | ) 488 | 489 | c := s.c 490 | 491 | if b, err = c.readPacket(); err != nil { 492 | return nil, err 493 | } 494 | 495 | switch b[0] { 496 | case _PACKET_ERR: // expected 497 | // handle err packet 498 | c.parseErrPacket(b) 499 | return nil, &c.e 500 | 501 | case _PACKET_OK: // unexpected! 502 | // the command resulted in a Result (anti-pattern ?); but 503 | // since it succeeded we handle it and return nil. 504 | if c.parseOkPacket(b) { 505 | // the command resulted in warning(s) 506 | return nil, &c.e 507 | } 508 | 509 | return nil, nil 510 | 511 | case _PACKET_INFILE_REQ: // unexpected! 512 | // local infile request; handle it and return nil 513 | if err = c.handleInfileRequest(string(b[1:])); err != nil { 514 | return nil, err 515 | } 516 | return nil, nil 517 | 518 | default: // expected 519 | // break and handle result set 520 | break 521 | } 522 | 523 | // handle result set 524 | columnCount, _ := getLenencInt(b) 525 | return c.handleBinaryResultSet(uint16(columnCount)) 526 | } 527 | 528 | func (c *Conn) handleBinaryResultSet(columnCount uint16) (*Rows, error) { 529 | var ( 530 | err error 531 | b []byte 532 | done, warn bool 533 | ) 534 | 535 | rs := new(Rows) 536 | rs.columnDefs = make([]*ColumnDefinition, 0) 537 | rs.rows = make([]*row, 0) 538 | rs.columnCount = columnCount 539 | 540 | // read column definition packets 541 | for i := uint16(0); i < rs.columnCount; i++ { 542 | if b, err = c.readPacket(); err != nil { 543 | return nil, err 544 | } else { 545 | rs.columnDefs = append(rs.columnDefs, 546 | parseColumnDefinitionPacket(b, false)) 547 | } 548 | } 549 | 550 | // read EOF packet 551 | if b, err = c.readPacket(); err != nil { 552 | return nil, err 553 | } else { 554 | warn = c.parseEOFPacket(b) 555 | } 556 | 557 | // read resultset row packets (each containing rs.columnCount values), 558 | // until EOF packet. 559 | for !done { 560 | if b, err = c.readPacket(); err != nil { 561 | return nil, err 562 | } 563 | 564 | switch b[0] { 565 | case _PACKET_EOF: 566 | done = true 567 | case _PACKET_ERR: 568 | c.parseErrPacket(b) 569 | return nil, &c.e 570 | default: // result set row 571 | rs.rows = append(rs.rows, 572 | c.handleBinaryResultSetRow(b, rs)) 573 | } 574 | } 575 | 576 | if warn { 577 | // command resulted in warning(s), return results and error 578 | return rs, &c.e 579 | } 580 | 581 | return rs, nil 582 | } 583 | 584 | func (c *Conn) handleBinaryResultSetRow(b []byte, rs *Rows) *row { 585 | var ( 586 | nullBitmapSize int 587 | off int 588 | ) 589 | 590 | columnCount := rs.columnCount 591 | r := new(row) 592 | r.columns = make([]interface{}, 0, columnCount) 593 | 594 | off++ // packet header [00] 595 | 596 | // null bitmap 597 | nullBitmapSize = int((columnCount + 9) / 8) 598 | nullBitmap := b[off : off+nullBitmapSize] 599 | off += nullBitmapSize 600 | 601 | for i := uint16(0); i < columnCount; i++ { 602 | if isNull(nullBitmap, i, 2) == true { 603 | r.columns = append(r.columns, nil) 604 | } else { 605 | switch rs.columnDefs[i].ColumnType { 606 | // string 607 | case _TYPE_STRING, _TYPE_VARCHAR, 608 | _TYPE_VARSTRING, _TYPE_ENUM, 609 | _TYPE_SET, _TYPE_BLOB, 610 | _TYPE_TINY_BLOB, _TYPE_MEDIUM_BLOB, 611 | _TYPE_LONG_BLOB, _TYPE_GEOMETRY, 612 | _TYPE_BIT, _TYPE_DECIMAL, 613 | _TYPE_NEW_DECIMAL: 614 | v, n := parseString(b[off:]) 615 | r.columns = append(r.columns, v) 616 | off += n 617 | 618 | // uint64 619 | case _TYPE_LONG_LONG: 620 | r.columns = append(r.columns, parseUint64(b[off:off+8])) 621 | off += 8 622 | 623 | // uint32 624 | case _TYPE_LONG, _TYPE_INT24: 625 | r.columns = append(r.columns, parseUint32(b[off:off+4])) 626 | off += 4 627 | 628 | // uint16 629 | case _TYPE_SHORT, _TYPE_YEAR: 630 | r.columns = append(r.columns, parseUint16(b[off:off+2])) 631 | off += 2 632 | 633 | // uint8 634 | case _TYPE_TINY: 635 | r.columns = append(r.columns, parseUint8(b[off:off+1])) 636 | off++ 637 | 638 | // float64 639 | case _TYPE_DOUBLE: 640 | r.columns = append(r.columns, parseDouble(b[off:off+8])) 641 | off += 8 642 | 643 | // float32 644 | case _TYPE_FLOAT: 645 | r.columns = append(r.columns, parseFloat(b[off:off+4])) 646 | off += 4 647 | 648 | // time.Time 649 | case _TYPE_DATE, _TYPE_DATETIME, 650 | _TYPE_TIMESTAMP: 651 | v, n := parseDate(b[off:]) 652 | r.columns = append(r.columns, v) 653 | off += n 654 | 655 | // time.Duration 656 | case _TYPE_TIME: 657 | v, n := parseTime(b[off:]) 658 | r.columns = append(r.columns, v) 659 | off += n 660 | 661 | // TODO: map the following unhandled types accordingly 662 | case _TYPE_NEW_DATE, _TYPE_TIMESTAMP2, 663 | _TYPE_DATETIME2, _TYPE_TIME2, 664 | _TYPE_NULL: 665 | fallthrough 666 | default: 667 | } 668 | } 669 | } 670 | return r 671 | } 672 | 673 | // mysql data types (unexported) 674 | const ( 675 | _TYPE_DECIMAL = iota 676 | _TYPE_TINY 677 | _TYPE_SHORT 678 | _TYPE_LONG 679 | _TYPE_FLOAT 680 | _TYPE_DOUBLE 681 | _TYPE_NULL 682 | _TYPE_TIMESTAMP 683 | _TYPE_LONG_LONG 684 | _TYPE_INT24 685 | _TYPE_DATE 686 | _TYPE_TIME 687 | _TYPE_DATETIME 688 | _TYPE_YEAR 689 | _TYPE_NEW_DATE 690 | _TYPE_VARCHAR 691 | _TYPE_BIT 692 | _TYPE_TIMESTAMP2 693 | _TYPE_DATETIME2 694 | _TYPE_TIME2 695 | // ... 696 | _TYPE_NEW_DECIMAL = 246 697 | _TYPE_ENUM = 247 698 | _TYPE_SET = 248 699 | _TYPE_TINY_BLOB = 249 700 | _TYPE_MEDIUM_BLOB = 250 701 | _TYPE_LONG_BLOB = 251 702 | _TYPE_BLOB = 252 703 | _TYPE_VARSTRING = 253 704 | _TYPE_STRING = 254 705 | _TYPE_GEOMETRY = 255 706 | ) 707 | 708 | // 709 | 710 | func parseString(b []byte) (string, int) { 711 | v, n := getLenencString(b) 712 | return v.value, n 713 | } 714 | 715 | func parseUint64(b []byte) uint64 { 716 | return binary.LittleEndian.Uint64(b[:8]) 717 | } 718 | 719 | func parseUint32(b []byte) uint32 { 720 | return binary.LittleEndian.Uint32(b[:4]) 721 | } 722 | 723 | func parseUint16(b []byte) uint16 { 724 | return binary.LittleEndian.Uint16(b[:2]) 725 | } 726 | 727 | func parseUint8(b []byte) uint8 { 728 | return uint8(b[0]) 729 | } 730 | 731 | func parseInt64(b []byte) int64 { 732 | return getInt64(b[:8]) 733 | } 734 | 735 | func parseInt32(b []byte) int32 { 736 | return getInt32(b[:4]) 737 | } 738 | 739 | func parseInt16(b []byte) int16 { 740 | return getInt16(b[:2]) 741 | } 742 | 743 | func parseInt8(b []byte) int8 { 744 | return int8(b[0]) 745 | } 746 | 747 | func parseDouble(b []byte) float64 { 748 | return math.Float64frombits(binary.LittleEndian.Uint64(b[:8])) 749 | } 750 | 751 | func parseFloat(b []byte) float32 { 752 | return math.Float32frombits(binary.LittleEndian.Uint32(b[:4])) 753 | } 754 | 755 | func parseNewDecimal(b []byte, size uint16) (float64, int) { 756 | var scale, precision int = int(size >> 8), int(size & 0xff) 757 | decimalSize := getDecimalBinarySize(precision, scale) 758 | 759 | positive := (b[0] & 0x80) == 0x80 760 | b[0] ^= 0x80 761 | 762 | if !positive { 763 | for i := 0; i < decimalSize; i++ { 764 | 765 | b[i] ^= 0xFF 766 | } 767 | } 768 | x := precision - scale 769 | ipDigits := x / _DIGITS_PER_INTEGER 770 | ipDigitsX := x - ipDigits*_DIGITS_PER_INTEGER 771 | ipSize := (ipDigits << 2) + _DIGITS_TO_BYTES[ipDigitsX] 772 | offset := _DIGITS_TO_BYTES[ipDigitsX] 773 | 774 | var value string 775 | 776 | if !positive { 777 | value += "-" 778 | } 779 | 780 | if offset > 0 { 781 | test := bigEndianInteger(b, 0, offset) 782 | value += strconv.FormatUint(uint64(test), 10) 783 | 784 | } 785 | 786 | for ; offset < ipSize; offset += 4 { 787 | value += strconv.FormatUint(uint64(bigEndianInteger(b, 0, offset)), 10) 788 | } 789 | shift := 0 790 | value += "." 791 | 792 | for ; shift+_DIGITS_PER_INTEGER <= scale; shift += _DIGITS_PER_INTEGER { 793 | value += strconv.FormatUint(uint64(bigEndianInteger(b, offset, 4)), 10) 794 | offset += 4 795 | } 796 | 797 | if shift < scale { 798 | value += strconv.FormatUint(uint64(bigEndianInteger(b, offset, _DIGITS_TO_BYTES[scale-shift])), 10) 799 | } 800 | 801 | rat, _ := new(big.Rat).SetString(value) 802 | result, _ := rat.Float64() 803 | return result, decimalSize 804 | } 805 | 806 | func getDecimalBinarySize(precision, scale int) int { 807 | x := precision - scale 808 | ipd := x / _DIGITS_PER_INTEGER 809 | fpd := scale / _DIGITS_PER_INTEGER 810 | return (ipd << 2) + _DIGITS_TO_BYTES[x-ipd*_DIGITS_PER_INTEGER] + 811 | (fpd << 2) + _DIGITS_TO_BYTES[scale-fpd*_DIGITS_PER_INTEGER] 812 | } 813 | 814 | func bigEndianInteger(bytes []byte, offset int, length int) int { 815 | result := 0 816 | for i := offset; i < (offset + length); i++ { 817 | b := bytes[i] 818 | if b >= 0 { 819 | result = (result << 8) | int(b) 820 | } else { 821 | result = (result << 8) | int(b>>256) 822 | } 823 | } 824 | return result 825 | } 826 | 827 | // TODO: fix location 828 | func parseDate(b []byte) (time.Time, int) { 829 | var ( 830 | year, day, hour, min, sec, msec int 831 | month time.Month 832 | loc *time.Location = time.UTC 833 | off int 834 | ) 835 | 836 | len := b[off] 837 | off++ 838 | 839 | if len >= 4 { 840 | year = int(binary.LittleEndian.Uint16(b[off : off+2])) 841 | off += 2 842 | month = time.Month(b[off]) 843 | off++ 844 | day = int(b[off]) 845 | off++ 846 | } 847 | 848 | if len >= 7 { 849 | hour = int(b[off]) 850 | off++ 851 | min = int(b[off]) 852 | off++ 853 | sec = int(b[off]) 854 | off++ 855 | } 856 | 857 | if len == 11 { 858 | msec = int(binary.LittleEndian.Uint32(b[off : off+4])) 859 | off += 4 860 | } 861 | 862 | return time.Date(year, month, day, hour, min, sec, msec*1000, loc), off 863 | } 864 | 865 | func parseTime(b []byte) (time.Duration, int) { 866 | var ( 867 | duration time.Duration 868 | neg int // multiplier 869 | off int 870 | ) 871 | 872 | len := b[off] 873 | off++ 874 | 875 | if len >= 8 { 876 | if b[off] == 1 { 877 | neg = -1 878 | } else { 879 | neg = 1 880 | } 881 | off++ 882 | 883 | duration += time.Duration(binary.LittleEndian.Uint32(b[off:off+4])) * 884 | 24 * time.Hour 885 | off += 4 886 | duration += time.Duration(b[off]) * time.Hour 887 | off++ 888 | duration += time.Duration(b[off]) * time.Minute 889 | off++ 890 | duration += time.Duration(b[off]) * time.Second 891 | off++ 892 | } 893 | 894 | if len == 12 { 895 | duration += 896 | time.Duration(binary.LittleEndian.Uint32(b[off:off+4])) * 897 | time.Microsecond 898 | } 899 | 900 | return time.Duration(neg) * duration, off 901 | } 902 | 903 | func writeString(b []byte, v string) (n int) { 904 | return putLenencString(b, v) 905 | } 906 | 907 | func writeUint64(b []byte, v uint64) (n int) { 908 | binary.LittleEndian.PutUint64(b[:8], v) 909 | return 8 910 | } 911 | 912 | func writeUint32(b []byte, v uint32) (n int) { 913 | binary.LittleEndian.PutUint32(b[:4], v) 914 | return 4 915 | } 916 | 917 | func writeUint16(b []byte, v uint16) (n int) { 918 | binary.LittleEndian.PutUint16(b[:2], v) 919 | return 2 920 | } 921 | 922 | func writeUint8(b []byte, v uint8) (n int) { 923 | b[0] = uint8(v) 924 | return 1 925 | } 926 | 927 | func writeDouble(b []byte, v float64) (n int) { 928 | binary.LittleEndian.PutUint64(b[:8], math.Float64bits(v)) 929 | return 8 930 | } 931 | 932 | func writeFloat(b []byte, v float32) (n int) { 933 | binary.LittleEndian.PutUint32(b[:4], math.Float32bits(v)) 934 | return 4 935 | } 936 | 937 | // TODO: Handle 0 date 938 | func writeDate(b []byte, v time.Time) int { 939 | var ( 940 | length, month, day, hour, min, sec uint8 941 | year uint16 942 | msec uint32 943 | off int 944 | ) 945 | 946 | year = uint16(v.Year()) 947 | month = uint8(v.Month()) 948 | day = uint8(v.Day()) 949 | hour = uint8(v.Hour()) 950 | min = uint8(v.Minute()) 951 | sec = uint8(v.Second()) 952 | msec = uint32(v.Nanosecond() / 1000) 953 | 954 | if hour == 0 && min == 0 && sec == 0 && msec == 0 { 955 | if year == 0 && month == 0 && day == 0 { 956 | return 0 957 | } else { 958 | length = 4 959 | } 960 | } else if msec == 0 { 961 | length = 7 962 | } else { 963 | length = 11 964 | } 965 | 966 | b[off] = length 967 | off++ 968 | 969 | if length >= 4 { 970 | binary.LittleEndian.PutUint16(b[off:off+2], year) 971 | off += 2 972 | b[off] = month 973 | off++ 974 | b[off] = day 975 | off++ 976 | } 977 | 978 | if length >= 7 { 979 | b[off] = hour 980 | off++ 981 | b[off] = min 982 | off++ 983 | b[off] = sec 984 | off++ 985 | } 986 | 987 | if length == 11 { 988 | binary.LittleEndian.PutUint32(b[off:off+4], msec) 989 | off += 4 990 | } 991 | 992 | return off 993 | } 994 | 995 | // dateSize returns the size needed to store a given time.Time. 996 | func dateSize(v time.Time) (length uint8) { 997 | var ( 998 | month, day, hour, min, sec uint8 999 | year uint16 1000 | msec uint32 1001 | ) 1002 | 1003 | year = uint16(v.Year()) 1004 | month = uint8(v.Month()) 1005 | day = uint8(v.Day()) 1006 | hour = uint8(v.Hour()) 1007 | min = uint8(v.Minute()) 1008 | sec = uint8(v.Second()) 1009 | msec = uint32(v.Nanosecond() / 1000) 1010 | 1011 | if hour == 0 && min == 0 && sec == 0 && msec == 0 { 1012 | if year == 0 && month == 0 && day == 0 { 1013 | return 0 1014 | } else { 1015 | length = 4 1016 | } 1017 | } else if msec == 0 { 1018 | length = 7 1019 | } else { 1020 | length = 11 1021 | } 1022 | length++ // 1 extra byte needed to store the length itself 1023 | return 1024 | } 1025 | 1026 | func writeTime(b []byte, v time.Duration) int { 1027 | var ( 1028 | length, neg, hours, mins, secs uint8 1029 | days, msecs uint32 1030 | off int 1031 | ) 1032 | 1033 | if v < 0 { 1034 | neg = 1 1035 | } // else neg = 0, positive 1036 | 1037 | days = uint32(v / (time.Hour * 24)) 1038 | v = v % (time.Hour * 24) 1039 | 1040 | hours = uint8(v / time.Hour) 1041 | v %= time.Hour 1042 | 1043 | mins = uint8(v / time.Minute) 1044 | v %= time.Minute 1045 | 1046 | secs = uint8(v / time.Second) 1047 | v %= time.Second 1048 | 1049 | msecs = uint32(v / time.Microsecond) 1050 | 1051 | if days == 0 && hours == 0 && mins == 0 && secs == 0 && msecs == 0 { 1052 | return 0 1053 | } 1054 | 1055 | if msecs == 0 { 1056 | length = 8 1057 | } else { 1058 | length = 12 1059 | } 1060 | 1061 | b[off] = length 1062 | off++ 1063 | b[off] = neg 1064 | off++ 1065 | 1066 | if length >= 8 { 1067 | binary.LittleEndian.PutUint32(b[off:off+4], days) 1068 | off += 4 1069 | b[off] = hours 1070 | off++ 1071 | b[off] = mins 1072 | off++ 1073 | b[off] = secs 1074 | off++ 1075 | } 1076 | 1077 | if length == 12 { 1078 | binary.LittleEndian.PutUint32(b[off:off+4], msecs) 1079 | off += 4 1080 | } 1081 | return off 1082 | } 1083 | 1084 | // handleClose handles COM_STMT_CLOSE and related packets 1085 | func (s *Stmt) handleClose() error { 1086 | var ( 1087 | b []byte 1088 | err error 1089 | ) 1090 | 1091 | // reset the protocol packet sequence number 1092 | s.c.resetSeqno() 1093 | 1094 | if b, err = s.c.createComStmtClose(s.id); err != nil { 1095 | return err 1096 | } 1097 | 1098 | // write COM_STMT_CLOSE packet 1099 | if err := s.c.writePacket(b); err != nil { 1100 | return err 1101 | } 1102 | 1103 | // note: expect no response from the server 1104 | return nil 1105 | } 1106 | -------------------------------------------------------------------------------- /prot_binlog.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "encoding/binary" 29 | "io" 30 | "os" 31 | "strconv" 32 | "strings" 33 | "time" 34 | ) 35 | 36 | const ( 37 | _LOG_EVENT_BINLOG_IN_USE_F = 0x1 38 | _LOG_EVENT_THREAD_SPECIFIC_F = 0x4 39 | _LOG_EVENT_SUPPRESS_USE_F = 0x8 40 | _LOG_EVENT_ARTIFICIAL_F = 0x20 41 | _LOG_EVENT_RELAY_LOG_F = 0x40 42 | _LOG_EVENT_SKIP_REPLICATION_F = 0x8000 43 | ) 44 | 45 | const ( 46 | _EVENT_TYPE_OFFSET = 4 47 | _FLAGS_OFFSET = 17 48 | ) 49 | 50 | type netReader struct { 51 | conn *Conn 52 | slave binlogSlave 53 | nonBlocking bool 54 | 55 | first bool 56 | eof bool 57 | closed bool 58 | 59 | e error 60 | nextEvent []byte 61 | } 62 | 63 | // init 64 | func (nr *netReader) init(p properties) error { 65 | var ( 66 | err error 67 | port uint64 68 | ) 69 | 70 | // initialize slave structure 71 | v := strings.Split(p.address, ":") 72 | nr.slave.host = v[0] 73 | 74 | if port, err = strconv.ParseUint(v[1], 10, 16); err != nil { 75 | return myError(ErrInvalidDSN, err) 76 | } else { 77 | nr.slave.port = uint16(port) 78 | } 79 | 80 | nr.slave.id = p.binlogSlaveId 81 | nr.slave.username = p.username 82 | nr.slave.password = p.password 83 | nr.slave.replicationRank = 0 84 | nr.slave.masterId = 0 85 | 86 | nr.nonBlocking = p.binlogDumpNonBlock 87 | 88 | // establish a connection with the master server 89 | if nr.conn, err = open(p); err != nil { 90 | nr.closed = true 91 | return err 92 | } 93 | 94 | // notify master about checksum awareness 95 | if p.binlogVerifyChecksum { 96 | if err = notifyChecksumAwareness(nr.conn); err != nil { 97 | return err 98 | } 99 | } 100 | 101 | // send COM_REGISTER_SLAVE to (master) server 102 | if err = nr.registerSlave(); err != nil { 103 | return err 104 | } 105 | 106 | return nil 107 | } 108 | 109 | type binlogSlave struct { 110 | id uint32 111 | host string 112 | username string 113 | password string 114 | port uint16 115 | replicationRank uint32 // ?? 116 | masterId uint32 // ?? 117 | } 118 | 119 | type event struct { 120 | header eventHeader 121 | body []byte 122 | } 123 | 124 | func (nr *netReader) begin(index binlogIndex) error { 125 | return nr.binlogDump(index) 126 | } 127 | 128 | func (nr *netReader) binlogDump(index binlogIndex) error { 129 | var ( 130 | b []byte 131 | err error 132 | ) 133 | 134 | c := nr.conn 135 | 136 | // reset the protocol packet sequence number 137 | c.resetSeqno() 138 | 139 | if b, err = c.createComBinlogDump(nr.slave, index, nr.nonBlocking); err != nil { 140 | return err 141 | } 142 | 143 | // send COM_BINLOG_DUMP packet to (master) server 144 | if err = c.writePacket(b); err != nil { 145 | return err 146 | } 147 | 148 | if err = nr.readEvent(); err != nil { 149 | nr.eof = true 150 | return err 151 | } 152 | 153 | nr.first = true 154 | return nil 155 | } 156 | 157 | func (nr *netReader) close() error { 158 | if err := nr.conn.Close(); err != nil { 159 | return err 160 | } 161 | nr.closed = true 162 | return nil 163 | } 164 | 165 | func (nr *netReader) registerSlave() error { 166 | var ( 167 | err error 168 | b []byte 169 | ) 170 | 171 | c := nr.conn 172 | 173 | // reset the protocol packet sequence number 174 | c.resetSeqno() 175 | 176 | if b, err = c.createComRegisterSlave(nr.slave); err != nil { 177 | return err 178 | } 179 | 180 | // send COM_REGISTER_SLAVE packet to (master) server 181 | if err = c.writePacket(b); err != nil { 182 | return err 183 | } 184 | 185 | if b, err = c.readPacket(); err != nil { 186 | return err 187 | } 188 | 189 | switch b[0] { 190 | case _PACKET_OK: //expected 191 | // parse OK packet 192 | if warn := c.parseOkPacket(b); warn { 193 | return &c.e 194 | } 195 | 196 | case _PACKET_ERR: //expected 197 | // parse err packet 198 | c.parseErrPacket(b) 199 | return &c.e 200 | 201 | default: // unexpected 202 | return myError(ErrInvalidPacket) 203 | } 204 | 205 | return nil 206 | } 207 | 208 | func (nr *netReader) next() bool { 209 | var err error 210 | 211 | // reset last error 212 | nr.e = nil 213 | 214 | if nr.closed || nr.eof { 215 | return false 216 | } 217 | 218 | if nr.first { // first event has already been read 219 | nr.first = false 220 | } else if err = nr.readEvent(); err != nil { // read the next event 221 | nr.eof = true 222 | if err != io.EOF { 223 | nr.e = err 224 | } 225 | // no more events to read 226 | return false 227 | } 228 | return true 229 | } 230 | 231 | func (nr *netReader) event() []byte { 232 | return nr.nextEvent 233 | } 234 | 235 | func (nr *netReader) error() error { 236 | return nr.e 237 | } 238 | 239 | func (c *Conn) createComRegisterSlave(s binlogSlave) ([]byte, error) { 240 | var ( 241 | b []byte 242 | off, payloadLength int 243 | err error 244 | ) 245 | 246 | payloadLength = 18 + len(s.host) + len(s.username) + len(s.password) 247 | 248 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 249 | return nil, err 250 | } 251 | 252 | off += 4 // placeholder for protocol packet header 253 | 254 | b[off] = _COM_REGISTER_SLAVE 255 | off++ 256 | 257 | binary.LittleEndian.PutUint32(b[off:off+4], s.id) 258 | off += 4 259 | 260 | b[off] = uint8(len(s.host)) 261 | off++ 262 | copy(b[off:], s.host) 263 | off += len(s.host) 264 | 265 | b[off] = uint8(len(s.username)) 266 | off++ 267 | copy(b[off:], s.username) 268 | off += len(s.username) 269 | 270 | b[off] = uint8(len(s.password)) 271 | off++ 272 | copy(b[off:], s.password) 273 | off += len(s.password) 274 | 275 | binary.LittleEndian.PutUint16(b[off:off+2], s.port) 276 | off += 2 277 | 278 | binary.LittleEndian.PutUint32(b[off:off+4], s.replicationRank) 279 | off += 4 280 | 281 | binary.LittleEndian.PutUint32(b[off:off+4], s.masterId) 282 | off += 4 283 | 284 | return b[0:off], nil 285 | } 286 | 287 | func (c *Conn) createComBinlogDump(slave binlogSlave, index binlogIndex, 288 | nonBlocking bool) ([]byte, error) { 289 | var ( 290 | b []byte 291 | off, payloadLength int 292 | err error 293 | ) 294 | 295 | payloadLength = 11 + len(index.file) 296 | 297 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 298 | return nil, err 299 | } 300 | 301 | off += 4 // placeholder for protocol packet header 302 | 303 | b[off] = _COM_BINLOG_DUMP 304 | off++ 305 | 306 | binary.LittleEndian.PutUint32(b[off:off+4], index.position) 307 | off += 4 308 | if nonBlocking { 309 | // flags (0x01 : BINLOG_DUMP_NON_BLOCK) 310 | binary.LittleEndian.PutUint16(b[off:off+2], uint16(0x01)) 311 | } else { 312 | binary.LittleEndian.PutUint16(b[off:off+2], uint16(0)) 313 | } 314 | 315 | off += 2 316 | binary.LittleEndian.PutUint32(b[off:off+4], slave.id) 317 | off += 4 318 | 319 | off += copy(b[off:], index.file) 320 | 321 | return b[0:off], nil 322 | } 323 | 324 | func parseEventHeader(b []byte) (eventHeader, int) { 325 | var ( 326 | off int 327 | header eventHeader 328 | ) 329 | 330 | header.timestamp = binary.LittleEndian.Uint32(b[off : off+4]) 331 | off += 4 332 | header.type_ = b[off] 333 | off++ 334 | header.serverId = binary.LittleEndian.Uint32(b[off : off+4]) 335 | off += 4 336 | header.size = binary.LittleEndian.Uint32(b[off : off+4]) 337 | off += 4 338 | header.position = binary.LittleEndian.Uint32(b[off : off+4]) 339 | off += 4 340 | header.flags = binary.LittleEndian.Uint16(b[off : off+2]) 341 | off += 2 342 | 343 | return header, off 344 | } 345 | 346 | func (nr *netReader) readEvent() error { 347 | var ( 348 | err error 349 | b []byte 350 | ) 351 | 352 | c := nr.conn 353 | 354 | if b, err = c.readPacket(); err != nil { 355 | return err 356 | } 357 | 358 | switch b[0] { 359 | case _PACKET_OK: // expected 360 | // move past [00] 361 | nr.nextEvent = b[1:] 362 | 363 | case _PACKET_ERR: //expected 364 | // handle err packet 365 | c.parseErrPacket(b) 366 | 367 | return &c.e 368 | 369 | case _PACKET_EOF: // expected 370 | if warn := c.parseEOFPacket(b); warn { 371 | // save warning (if any) 372 | nr.e = &c.e 373 | } 374 | return io.EOF 375 | 376 | default: //unexpected 377 | return myError(ErrInvalidPacket) 378 | } 379 | 380 | return nil 381 | } 382 | 383 | func (b *Binlog) parseStartEventV3(buf []byte, ev *StartEventV3) (err error) { 384 | var off int 385 | 386 | ev.binlogVersion = binary.LittleEndian.Uint16(buf) 387 | off += 2 388 | 389 | ev.serverVersion = string(buf[off : off+50]) 390 | off += 50 391 | 392 | ev.creationTime = time.Unix(int64(binary.LittleEndian.Uint32(buf[off:])), 0) 393 | 394 | return 395 | } 396 | 397 | func (b *Binlog) parseQueryEvent(buf []byte, ev *QueryEvent) (err error) { 398 | var ( 399 | off int 400 | schemaLength int 401 | varLength int 402 | ) 403 | 404 | ev.slaveProxyId = binary.LittleEndian.Uint32(buf) 405 | off += 4 406 | 407 | ev.executionTime = time.Unix(int64(binary.LittleEndian.Uint32(buf[off:])), 0) 408 | off += 4 409 | 410 | // move past schema length 411 | schemaLength = int(buf[off]) 412 | off++ 413 | 414 | ev.errorCode = binary.LittleEndian.Uint16(buf[off:]) 415 | off += 2 416 | 417 | if b.desc.binlogVersion >= 4 { 418 | varLength = int(binary.LittleEndian.Uint16(buf[off:])) 419 | off += 2 420 | } 421 | 422 | ev.statusVars = string(buf[off : off+varLength]) 423 | off += varLength 424 | 425 | ev.schema = string(buf[off : off+schemaLength]) 426 | off += schemaLength 427 | off++ 428 | 429 | ev.query = string(buf[off:]) 430 | return nil 431 | } 432 | 433 | func (b *Binlog) parseRotateEvent(buf []byte, ev *RotateEvent) (err error) { 434 | var off int 435 | 436 | ev.position = binary.LittleEndian.Uint64(buf) 437 | off += 8 438 | 439 | ev.file = string(buf[off:]) 440 | 441 | return 442 | } 443 | 444 | func (b *Binlog) parseIntvarEvent(buf []byte, ev *IntvarEvent) (err error) { 445 | var off int 446 | ev.type_ = uint8(buf[0]) 447 | off++ 448 | 449 | ev.value = binary.LittleEndian.Uint64(buf[off:]) 450 | 451 | return 452 | } 453 | 454 | // parseLoadEvent parses LOAD_EVENT as well as NEW_LOAD_EVENT 455 | func (b *Binlog) parseLoadEvent(buf []byte, ev *LoadEvent) (err error) { 456 | var off int 457 | 458 | ev.slaveProxyId = binary.LittleEndian.Uint32(buf[off:]) 459 | off += 4 460 | 461 | ev.executionTime = time.Unix(int64(binary.LittleEndian.Uint32(buf[off:])), 0) 462 | off += 4 463 | 464 | ev.skipLines = binary.LittleEndian.Uint32(buf[off:]) 465 | off += 4 466 | 467 | // table name length 468 | off++ 469 | 470 | // schema name length 471 | off++ 472 | 473 | ev.fieldCount = binary.LittleEndian.Uint32(buf[off:]) 474 | off += 4 475 | 476 | if ev.header.type_ == LOAD_EVENT { 477 | ev.fieldTerminator = string(buf[off]) 478 | off++ 479 | 480 | ev.enclosedBy = string(buf[off]) 481 | off++ 482 | 483 | ev.lineTerminator = string(buf[off]) 484 | off++ 485 | 486 | ev.lineStart = string(buf[off]) 487 | off++ 488 | 489 | ev.escapedBy = string(buf[off]) 490 | off++ 491 | 492 | ev.optFlags = make([]byte, 1) 493 | ev.optFlags[0] = buf[off] 494 | off++ 495 | 496 | ev.emptyFlags = uint8(buf[off]) 497 | off++ 498 | } else { // NEW_LOAD_EVENT 499 | var length int 500 | 501 | length = int(buf[off]) 502 | off++ 503 | ev.fieldTerminator = string(buf[off : off+length]) 504 | off += length 505 | 506 | length = int(buf[off]) 507 | off++ 508 | ev.enclosedBy = string(buf[off : off+length]) 509 | off += length 510 | 511 | length = int(buf[off]) 512 | off++ 513 | ev.lineTerminator = string(buf[off : off+length]) 514 | off += length 515 | 516 | length = int(buf[off]) 517 | off++ 518 | ev.lineStart = string(buf[off : off+length]) 519 | off += length 520 | 521 | length = int(buf[off]) 522 | off++ 523 | ev.escapedBy = string(buf[off : off+length]) 524 | off += length 525 | 526 | length = int(buf[off]) 527 | off++ 528 | ev.optFlags = make([]byte, length) 529 | copy(ev.optFlags, buf[off:off+length]) 530 | off += length 531 | } 532 | 533 | /* 534 | we do not really need individual field name lengths as 535 | field names are null-terminated. 536 | */ 537 | off += int(ev.fieldCount) 538 | 539 | ev.fields = make([]string, ev.fieldCount) 540 | 541 | var i, n int 542 | for i = 0; i < int(ev.fieldCount); i++ { 543 | ev.fields[i], n = getNullTerminatedString(buf[off:]) 544 | off += n 545 | } 546 | 547 | ev.table, n = getNullTerminatedString(buf[off:]) 548 | off += n 549 | 550 | ev.schema, n = getNullTerminatedString(buf[off:]) 551 | off += n 552 | 553 | ev.file, n = getNullTerminatedString(buf[off:]) 554 | off += n 555 | 556 | return 557 | } 558 | 559 | func (b *Binlog) parseSlaveEvent(buf []byte, ev *SlaveEvent) (err error) { 560 | 561 | var off int 562 | ev.masterPosition = binary.LittleEndian.Uint64(buf[off:]) 563 | off += 8 564 | 565 | ev.masterPort = binary.LittleEndian.Uint16(buf[off:]) 566 | off += 2 567 | 568 | var n int 569 | ev.masterHost, n = getNullTerminatedString(buf[off:]) 570 | off += n 571 | 572 | ev.masterLog, n = getNullTerminatedString(buf[off:]) 573 | off += n 574 | 575 | return 576 | } 577 | 578 | func (b *Binlog) parseCreateFileEvent(buf []byte, ev *CreateFileEvent) (err error) { 579 | var off int 580 | 581 | ev.fileId = binary.LittleEndian.Uint32(buf[off:]) 582 | off += 4 583 | 584 | ev.data = buf[off:] 585 | return 586 | } 587 | 588 | func (b *Binlog) parseAppendBlockEvent(buf []byte, ev *AppendBlockEvent) (err error) { 589 | var off int 590 | 591 | ev.fieldId = binary.LittleEndian.Uint32(buf[off:]) 592 | off += 4 593 | 594 | ev.data = buf[off:] 595 | return 596 | } 597 | 598 | func (b *Binlog) parseExecLoadEvent(buf []byte, ev *ExecLoadEvent) (err error) { 599 | var off int 600 | ev.fileId = binary.LittleEndian.Uint32(buf[off:]) 601 | return 602 | } 603 | 604 | func (b *Binlog) parseDeleteFileEvent(buf []byte, ev *DeleteFileEvent) (err error) { 605 | ev.fileId = binary.LittleEndian.Uint32(buf[0:]) 606 | return 607 | } 608 | 609 | func (b *Binlog) parseRandEvent(buf []byte, ev *RandEvent) (err error) { 610 | var off int 611 | 612 | ev.seed1 = binary.LittleEndian.Uint64(buf[off:]) 613 | off += 8 614 | 615 | ev.seed2 = binary.LittleEndian.Uint64(buf[off:]) 616 | 617 | return 618 | } 619 | 620 | func (b *Binlog) parseUserVarEvent(buf []byte, ev *UserVarEvent) (err error) { 621 | var off int 622 | 623 | // name length 624 | length := int(binary.LittleEndian.Uint32(buf[off:])) 625 | off += 4 626 | ev.name = string(buf[off : off+length]) 627 | off += length 628 | 629 | if buf[off] != 0 { 630 | ev.null = true 631 | } 632 | off++ 633 | 634 | if !ev.null { 635 | ev.type_ = uint8(buf[off]) 636 | off++ 637 | 638 | ev.charset = binary.LittleEndian.Uint32(buf[off:]) 639 | off += 4 640 | 641 | var valueLen int 642 | valueLen = int(binary.LittleEndian.Uint32(buf[off:])) 643 | off += 4 644 | 645 | ev.value = make([]byte, valueLen) 646 | copy(ev.value, buf[off:off+valueLen]) 647 | off += valueLen 648 | } 649 | 650 | // more data? 651 | if len(buf[off:]) > 0 { 652 | ev.flags = uint8(buf[off]) 653 | } 654 | 655 | return 656 | } 657 | 658 | func (b *Binlog) parseFormatDescriptionEvent(buf []byte, ev *FormatDescriptionEvent) (err error) { 659 | var off int 660 | 661 | ev.binlogVersion = binary.LittleEndian.Uint16(buf) 662 | off += 2 663 | 664 | ev.serverVersion = string(buf[off : off+50]) 665 | off += 50 666 | 667 | ev.creationTime = time.Unix(int64(binary.LittleEndian.Uint32(buf[off:])), 0) 668 | off += 4 669 | 670 | ev.commonHeaderLength = uint8(buf[off]) 671 | off++ 672 | 673 | // TODO: check server version and/or binlog version to see if it 674 | // supports event checksum. For now consider and store rest of 675 | // unread buffer to postHeaderLength. 676 | ev.postHeaderLength = buf[off:] 677 | 678 | // Checksum algorithm descriptor (1 byte), its placed right before the 679 | // checksum value (4 bytes), excluded by the caller 680 | ev.checksumAlg = uint8(ev.postHeaderLength[len(ev.postHeaderLength)-1]) 681 | return 682 | } 683 | 684 | func (b *Binlog) parseRowsQueryLogEvent(buf []byte, ev *RowsQueryLogEvent) (err error) { 685 | length := buf[0] 686 | ev.query = string(buf[1:length]) 687 | return 688 | } 689 | 690 | func (b *Binlog) parseXidEvent(buf []byte, ev *XidEvent) (err error) { 691 | ev.xid = binary.LittleEndian.Uint64(buf[0:]) 692 | return 693 | } 694 | 695 | func (b *Binlog) parseBeginLoadQueryEvent(buf []byte, ev *BeginLoadQueryEvent) (err error) { 696 | var off int 697 | 698 | ev.fileId = binary.LittleEndian.Uint32(buf[off:]) 699 | off += 4 700 | 701 | ev.data = buf[off:] 702 | return 703 | } 704 | 705 | func (b *Binlog) parseExecuteLoadQueryEvent(buf []byte, ev *ExecuteLoadQueryEvent) (err error) { 706 | var off int 707 | 708 | ev.slaveProxyId = binary.LittleEndian.Uint32(buf[off:]) 709 | off += 4 710 | 711 | ev.executionTime = time.Unix(int64(binary.LittleEndian.Uint32(buf[off:])), 0) 712 | off += 4 713 | 714 | ev.schemaLength = buf[off] 715 | off++ 716 | 717 | ev.errorCode = binary.LittleEndian.Uint16(buf[off:]) 718 | off += 2 719 | 720 | ev.statusVarsLength = binary.LittleEndian.Uint16(buf[off:]) 721 | off += 4 722 | 723 | ev.fileId = binary.LittleEndian.Uint32(buf[off:]) 724 | off += 4 725 | 726 | ev.startPosition = binary.LittleEndian.Uint32(buf[off:]) 727 | off += 4 728 | 729 | ev.endPosition = binary.LittleEndian.Uint32(buf[off:]) 730 | off += 4 731 | 732 | ev.dupHandlingFlags = uint8(buf[off]) 733 | 734 | return 735 | } 736 | 737 | func (b *Binlog) parseTableMapEvent(buf []byte, ev *TableMapEvent) (err error) { 738 | var ( 739 | off int 740 | length int 741 | i uint64 742 | ) 743 | 744 | if b.desc.postHeaderLength[ev.header.type_-1] == 6 { 745 | ev.tableId = uint64(binary.LittleEndian.Uint32(buf[off:])) 746 | off += 4 747 | } else { 748 | ev.tableId = getUint48(buf) 749 | off += 6 750 | } 751 | 752 | ev.flags = binary.LittleEndian.Uint16(buf[off:]) 753 | off += 2 754 | 755 | length = int(buf[off]) 756 | off++ 757 | ev.schema = string(buf[off : off+length]) 758 | off += length 759 | off++ 760 | 761 | length = int(buf[off]) 762 | off++ 763 | ev.table = string(buf[off : off+length]) 764 | off += length 765 | off++ 766 | 767 | ev.columnCount, length = getLenencInt(buf[off:]) 768 | off += length 769 | 770 | ev.columns = make([]EventColumn, ev.columnCount) 771 | 772 | // field type 773 | for i = 0; i < ev.columnCount; i++ { 774 | ev.columns[i].type_ = uint8(buf[off]) 775 | off++ 776 | } 777 | 778 | // field meta data 779 | var meta uint16 780 | 781 | _, length = getLenencInt(buf[off:]) 782 | off += length 783 | 784 | // read meta data and store them into the respective columns 785 | // TODO: verify that buffer consumed is equal to the meta data size 786 | for i = 0; i < ev.columnCount; i++ { 787 | switch getMetaDataSize(ev.columns[i].type_) { 788 | case 2: 789 | meta = binary.LittleEndian.Uint16(buf[off:]) 790 | off += 2 791 | case 1: 792 | meta = uint16(buf[off]) 793 | off++ 794 | default: 795 | meta = 0 796 | } 797 | ev.columns[i].meta = meta 798 | } 799 | 800 | // null bitmap 801 | nullBitmapSize := int((ev.columnCount + 7) / 8) 802 | nullBitmap := buf[off : off+nullBitmapSize] 803 | for i = 0; i < ev.columnCount; i++ { 804 | if isNull(nullBitmap, uint16(i), 0) { 805 | ev.columns[i].nullable = true 806 | } 807 | } 808 | 809 | return 810 | } 811 | 812 | func getMetaDataSize(type_ uint8) uint8 { 813 | switch type_ { 814 | case _TYPE_TINY_BLOB, _TYPE_BLOB, _TYPE_MEDIUM_BLOB, _TYPE_LONG_BLOB, 815 | _TYPE_DOUBLE, _TYPE_FLOAT, _TYPE_GEOMETRY, _TYPE_TIME2, 816 | _TYPE_DATETIME2, _TYPE_TIMESTAMP2: 817 | return 1 818 | 819 | case _TYPE_SET, _TYPE_ENUM, _TYPE_STRING, _TYPE_BIT, _TYPE_VARCHAR, _TYPE_NEW_DECIMAL: 820 | return 2 821 | 822 | default: 823 | } 824 | return 0 825 | } 826 | 827 | func (b *Binlog) parseIncidentEvent(buf []byte, ev *IncidentEvent) (err error) { 828 | var ( 829 | off int 830 | length int 831 | ) 832 | 833 | ev.type_ = binary.LittleEndian.Uint16(buf[off:]) 834 | off += 2 835 | 836 | length = int(buf[off]) 837 | off++ 838 | 839 | ev.message = string(buf[off : off+length]) 840 | return 841 | } 842 | 843 | // Note: There was no after-image in v0. 844 | func (b *Binlog) parseRowsEvent(buf []byte, ev *RowsEvent) (err error) { 845 | var ( 846 | off int 847 | length int 848 | ) 849 | 850 | if b.desc.postHeaderLength[ev.header.type_-1] == 6 { 851 | ev.tableId = uint64(binary.LittleEndian.Uint32(buf[off:])) 852 | off += 4 853 | } else { 854 | ev.tableId = getUint48(buf) 855 | off += 6 856 | } 857 | 858 | ev.flags = binary.LittleEndian.Uint16(buf[off:]) 859 | off += 2 860 | 861 | if b.desc.postHeaderLength[ev.header.type_-1] == 10 { 862 | length = int(binary.LittleEndian.Uint16(buf[off:])) - 2 863 | off += 2 864 | ev.extraData = buf[off : off+length] 865 | off += length 866 | } 867 | 868 | ev.columnCount, length = getLenencInt(buf[off:]) 869 | off += length 870 | 871 | length = int((ev.columnCount + 7) / 8) 872 | ev.columnsPresentBitmap1 = buf[off : off+length] 873 | off += length 874 | if (ev.header.type_ == UPDATE_ROWS_EVENT_V1) || 875 | (ev.header.type_ == UPDATE_ROWS_EVENT) { 876 | ev.columnsPresentBitmap2 = buf[off : off+length] 877 | off += length 878 | } 879 | 880 | ev.rows1.Rows = make([]EventRow, 0) 881 | if (ev.header.type_ == UPDATE_ROWS_EVENT_V1) || 882 | (ev.header.type_ == UPDATE_ROWS_EVENT) { 883 | ev.rows2.Rows = make([]EventRow, 0) 884 | } 885 | 886 | var ( 887 | n int 888 | r EventRow 889 | ) 890 | 891 | for off < len(buf) { 892 | r, n = b.parseEventRow(buf[off:], ev.columnCount, 893 | ev.columnsPresentBitmap1) 894 | ev.rows1.Rows = append(ev.rows1.Rows, r) 895 | off += n 896 | if (ev.header.type_ == UPDATE_ROWS_EVENT_V1) || 897 | (ev.header.type_ == UPDATE_ROWS_EVENT) { 898 | r, n = b.parseEventRow(buf[off:], ev.columnCount, 899 | ev.columnsPresentBitmap2) 900 | ev.rows2.Rows = append(ev.rows2.Rows, r) 901 | off += n 902 | } 903 | } 904 | 905 | return 906 | } 907 | 908 | func (b *Binlog) parseEventRow(buf []byte, columnCount uint64, 909 | columnsPresentBitmap []byte) (EventRow, int) { 910 | var ( 911 | off int 912 | r EventRow 913 | ) 914 | 915 | r.Columns = make([]interface{}, 0, columnCount) 916 | 917 | nullBitmapSize := int((setBitCount(columnsPresentBitmap) + 7) / 8) 918 | nullBitmap := buf[off : off+nullBitmapSize] 919 | off += nullBitmapSize 920 | 921 | for i := uint64(0); i < columnCount; i++ { 922 | if isNull(nullBitmap, uint16(i), 0) == true { 923 | r.Columns = append(r.Columns, nil) 924 | } else { 925 | switch b.tableMap.columns[i].type_ { 926 | // string 927 | case _TYPE_VARCHAR, _TYPE_VARSTRING: 928 | v, n := parseString2(buf[off:], b.tableMap.columns[i].meta) 929 | r.Columns = append(r.Columns, v) 930 | off += n 931 | 932 | case _TYPE_STRING, _TYPE_ENUM, 933 | _TYPE_SET, _TYPE_BLOB, 934 | _TYPE_TINY_BLOB, _TYPE_MEDIUM_BLOB, 935 | _TYPE_LONG_BLOB, _TYPE_GEOMETRY, 936 | _TYPE_BIT, _TYPE_DECIMAL: 937 | v, n := parseString(buf[off:]) 938 | r.Columns = append(r.Columns, v) 939 | off += n 940 | case _TYPE_NEW_DECIMAL: 941 | v, n := parseNewDecimal(buf[off:], b.tableMap.columns[i].meta) 942 | r.Columns = append(r.Columns, v) 943 | off += n 944 | // int64 945 | case _TYPE_LONG_LONG: 946 | r.Columns = append(r.Columns, parseInt64(buf[off:off+8])) 947 | off += 8 948 | 949 | // int32 950 | case _TYPE_LONG, _TYPE_INT24: 951 | r.Columns = append(r.Columns, parseInt32(buf[off:off+4])) 952 | off += 4 953 | 954 | // int16 955 | case _TYPE_SHORT, _TYPE_YEAR: 956 | r.Columns = append(r.Columns, parseInt16(buf[off:off+2])) 957 | off += 2 958 | 959 | // int8 960 | case _TYPE_TINY: 961 | r.Columns = append(r.Columns, parseInt8(buf[off:off+1])) 962 | off++ 963 | 964 | // float64 965 | case _TYPE_DOUBLE: 966 | r.Columns = append(r.Columns, parseDouble(buf[off:off+8])) 967 | off += 8 968 | 969 | // float32 970 | case _TYPE_FLOAT: 971 | r.Columns = append(r.Columns, parseFloat(buf[off:off+4])) 972 | off += 4 973 | 974 | // time.Time 975 | case _TYPE_DATE, _TYPE_DATETIME, 976 | _TYPE_TIMESTAMP: 977 | v, n := parseDate(buf[off:]) 978 | r.Columns = append(r.Columns, v) 979 | off += n 980 | 981 | // time.Duration 982 | case _TYPE_TIME: 983 | v, n := parseTime(buf[off:]) 984 | r.Columns = append(r.Columns, v) 985 | off += n 986 | 987 | // TODO: map the following unhandled types accordingly 988 | case _TYPE_NEW_DATE, _TYPE_TIMESTAMP2, 989 | _TYPE_DATETIME2, _TYPE_TIME2, 990 | _TYPE_NULL: 991 | fallthrough 992 | default: 993 | } 994 | } 995 | } 996 | return r, off 997 | } 998 | 999 | func (b *Binlog) parseGtidLogEvent(buf []byte, ev *GtidLogEvent) { 1000 | var off int 1001 | ev.gtid.commitFlag = (buf[off] != 0) 1002 | off++ 1003 | copy(ev.gtid.sourceId.data[0:], buf[off:off+16]) 1004 | off += 16 1005 | ev.gtid.groupNumber = getInt64(buf[off:]) 1006 | return 1007 | } 1008 | 1009 | func (b *Binlog) parsePreviousGtidsLogEvent(buf []byte, ev *PreviousGtidsLogEvent) { 1010 | ev.data = make([]byte, len(buf)) 1011 | copy(ev.data, buf[:]) 1012 | return 1013 | } 1014 | 1015 | func (b *Binlog) parseAnnotateRowsEvent(buf []byte, ev *AnnotateRowsEvent) { 1016 | ev.query = string(buf) 1017 | return 1018 | } 1019 | 1020 | func (b *Binlog) parseBinlogCheckpointEvent(buf []byte, ev *BinlogCheckpointEvent) { 1021 | var off int 1022 | ev.fileLength = binary.LittleEndian.Uint32(buf[off:]) 1023 | off += 4 1024 | ev.file = string(buf[off:]) 1025 | return 1026 | } 1027 | 1028 | func (b *Binlog) parseGtidEvent(buf []byte, ev *GtidEvent) { 1029 | var off int 1030 | 1031 | ev.gtid.seqno = binary.LittleEndian.Uint64(buf[off:]) 1032 | off += 8 1033 | 1034 | ev.gtid.domainId = binary.LittleEndian.Uint32(buf[off:]) 1035 | off += 4 1036 | 1037 | ev.flags = uint8(buf[off]) 1038 | off++ 1039 | 1040 | if (ev.flags & FL_GROUP_COMMIT_ID) != 0 { 1041 | ev.commitId = binary.LittleEndian.Uint64(buf[off:]) 1042 | } 1043 | 1044 | ev.gtid.serverId = ev.header.serverId 1045 | 1046 | return 1047 | } 1048 | 1049 | func (b *Binlog) parseGtidListEvent(buf []byte, ev *GtidListEvent) { 1050 | var off int 1051 | val := binary.LittleEndian.Uint32(buf[off:]) 1052 | off += 4 1053 | 1054 | ev.count = val & ((1 << 28) - 1) 1055 | ev.flags = uint8(val & (0xF << 28)) 1056 | off++ 1057 | 1058 | ev.list = make([]MariadbGtid, ev.count) 1059 | 1060 | for i := uint32(0); i < ev.count; i++ { 1061 | ev.list[i].domainId = binary.LittleEndian.Uint32(buf[off:]) 1062 | ev.list[i].serverId = binary.LittleEndian.Uint32(buf[off+4:]) 1063 | ev.list[i].seqno = binary.LittleEndian.Uint64(buf[off+8:]) 1064 | off += 16 1065 | } 1066 | 1067 | return 1068 | } 1069 | 1070 | type fileReader struct { 1071 | name string 1072 | file *os.File 1073 | closed bool 1074 | first bool 1075 | eof bool 1076 | e error 1077 | nextEvent []byte 1078 | } 1079 | 1080 | func (fr *fileReader) begin(index binlogIndex) error { 1081 | var err error 1082 | 1083 | if index.file != "" && index.file != fr.name { 1084 | fr.name = index.file 1085 | 1086 | // close the previously opened file 1087 | if !fr.closed { 1088 | if err = fr.close(); err != nil { 1089 | return myError(ErrFile, err) 1090 | } 1091 | } 1092 | // open the new file 1093 | if fr.file, err = os.Open(fr.name); err != nil { 1094 | return myError(ErrFile, err) 1095 | } 1096 | fr.closed = false 1097 | } 1098 | 1099 | if index.position > 0 { 1100 | if _, err = fr.file.Seek(int64(index.position), 0); err != nil { 1101 | // seek operation failed 1102 | return myError(ErrFile, err) 1103 | } 1104 | } 1105 | 1106 | // read and verify magic number 1107 | magic := make([]byte, 4) 1108 | if _, err = fr.file.Read(magic); err != nil { 1109 | return myError(ErrFile, err) 1110 | 1111 | } else { 1112 | // TODO: verify magic number [0xfe, 'b', 'i', 'n'] 1113 | } 1114 | if err = fr.readEvent(); err != nil { 1115 | return err 1116 | } 1117 | 1118 | fr.first = true 1119 | return nil 1120 | } 1121 | 1122 | func (fr *fileReader) close() error { 1123 | var err error 1124 | 1125 | if !fr.closed { 1126 | if err = fr.file.Close(); err != nil { 1127 | // file close operation failed 1128 | return myError(ErrFile, err) 1129 | } 1130 | } 1131 | fr.closed = true 1132 | return nil 1133 | } 1134 | 1135 | func (fr *fileReader) next() bool { 1136 | var err error 1137 | 1138 | // reset last error 1139 | fr.e = nil 1140 | 1141 | if fr.eof { 1142 | return false 1143 | } 1144 | 1145 | if fr.first { // first event has already been read 1146 | fr.first = false 1147 | } else if err = fr.readEvent(); err != nil { // read the next event 1148 | fr.eof = true 1149 | if err != io.EOF { 1150 | fr.e = err 1151 | } 1152 | return false 1153 | } 1154 | return true 1155 | } 1156 | 1157 | func (fr *fileReader) event() []byte { 1158 | return fr.nextEvent 1159 | } 1160 | 1161 | func (fr *fileReader) init(p properties) error { 1162 | var err error 1163 | fr.name = p.file 1164 | 1165 | if fr.file, err = os.Open(fr.name); err != nil { 1166 | fr.closed = true 1167 | return myError(ErrFile, err) 1168 | } 1169 | fr.closed = false 1170 | return nil 1171 | } 1172 | 1173 | func (fr *fileReader) readEvent() error { 1174 | var ( 1175 | err error 1176 | headerBuf, bodyBuf, eventBuf []byte 1177 | header eventHeader 1178 | ) 1179 | 1180 | // read the binlog header 1181 | headerBuf = make([]byte, 19) 1182 | if _, err = fr.file.Read(headerBuf); err != nil { 1183 | goto E 1184 | } 1185 | 1186 | header, _ = parseEventHeader(headerBuf) 1187 | 1188 | // read the event body 1189 | bodyBuf = make([]byte, header.size-19) 1190 | _, err = fr.file.Read(bodyBuf) 1191 | if err != nil { 1192 | goto E 1193 | } 1194 | 1195 | // combine both the buffers 1196 | eventBuf = make([]byte, header.size) 1197 | copy(eventBuf, headerBuf) 1198 | copy(eventBuf[19:], bodyBuf) 1199 | 1200 | fr.nextEvent = eventBuf 1201 | return nil 1202 | 1203 | E: 1204 | if err == io.EOF { 1205 | return err 1206 | } else { 1207 | return myError(ErrFile, err) 1208 | } 1209 | } 1210 | 1211 | func (fr *fileReader) error() error { 1212 | return fr.e 1213 | } 1214 | 1215 | func parseString2(b []byte, length uint16) (string, int) { 1216 | if length < 256 { 1217 | length = uint16(b[0]) 1218 | return string(b[1 : 1+length]), int(length) + 1 1219 | } else { 1220 | length = parseUint16(b) 1221 | return string(b[2 : 2+length]), int(length) + 2 1222 | } 1223 | } 1224 | -------------------------------------------------------------------------------- /prot_conn.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "net" 29 | ) 30 | 31 | const ( 32 | _INITIAL_PACKET_BUFFER_SIZE = 4 * 1024 // 4KB 33 | ) 34 | 35 | type Conn struct { 36 | // connection properties 37 | p properties 38 | 39 | conn net.Conn 40 | buff buffer 41 | rw readWriter 42 | seqno uint8 // packet sequence number 43 | 44 | // OK packet 45 | affectedRows uint64 46 | lastInsertId uint64 47 | statusFlags uint16 48 | warnings uint16 49 | 50 | // ERR packet 51 | e Error 52 | 53 | // handshake initialization packet (from server) 54 | serverVersion string 55 | connectionId uint32 56 | serverCapabilities uint32 57 | serverCharset uint8 58 | authPluginData []byte 59 | authPluginName string 60 | 61 | // handshake response packet (from client) 62 | clientCharset uint8 63 | } 64 | 65 | func open(p properties) (*Conn, error) { 66 | var err error 67 | 68 | c := &Conn{} 69 | c.rw = &defaultReadWriter{} 70 | c.p = p 71 | 72 | // initialize the connection buffer 73 | c.buff.New(_INITIAL_PACKET_BUFFER_SIZE) 74 | 75 | // open a connection with the server 76 | if c.conn, err = dial(p.address, p.socket); err != nil { 77 | return nil, err 78 | } else { 79 | c.rw.init(c) 80 | } 81 | 82 | // perform handshake 83 | if err = c.handshake(); err != nil { 84 | return nil, err 85 | } 86 | 87 | return c, nil 88 | } 89 | 90 | // readPacket reads the next available protocol packet from the network into 91 | // the connection buffer. It also increments the packet sequence number. 92 | func (c *Conn) readPacket() ([]byte, error) { 93 | var ( 94 | err error 95 | b []byte 96 | payloadLength int 97 | ) 98 | 99 | // first read the packet header 100 | 101 | // reset the connection buffer 102 | if b, err = c.buff.Reset(4); err != nil { 103 | return nil, err 104 | } 105 | if _, err = c.rw.read(b, 4); err != nil { 106 | return nil, err 107 | } 108 | 109 | // payload length 110 | payloadLength = int(getUint24(b[0:3])) 111 | 112 | // error out in case the packet is too big. 113 | // if compression is enabled, we check it in readCompressedPacket(). 114 | if c.p.clientCapabilities&_CLIENT_COMPRESS == 0 && 115 | payloadLength+4 > int(c.p.maxPacketSize) { 116 | return nil, myError(ErrNetPacketTooLarge) 117 | } 118 | 119 | // check for out-of-order packets 120 | if c.seqno != b[3] { 121 | return nil, myError(ErrNetPacketsOutOfOrder) 122 | } 123 | 124 | // increment the packet sequence number 125 | c.seqno++ 126 | 127 | // finally, read the payload (note: the header gets overwritten) 128 | 129 | // reset the connection buffer 130 | if b, err = c.buff.Reset(payloadLength); err != nil { 131 | return nil, err 132 | } 133 | if _, err = c.rw.read(b, payloadLength); err != nil { 134 | return nil, err 135 | } 136 | 137 | return b[0:payloadLength], nil 138 | } 139 | 140 | // writePacket populates the specified packet buffer with header and writes it 141 | // to the network. 142 | func (c *Conn) writePacket(b []byte) error { 143 | var ( 144 | err error 145 | payloadLength int 146 | ) 147 | 148 | payloadLength = len(b) - 4 149 | 150 | // populate the packet header 151 | putUint24(b[0:3], uint32(payloadLength)) // payload length 152 | b[3] = c.seqno // packet sequence number 153 | 154 | // write it to the connection 155 | if _, err = c.rw.write(b); err != nil { 156 | return err 157 | } 158 | 159 | // finally, increment the packet sequence number 160 | c.seqno++ 161 | 162 | return nil 163 | } 164 | 165 | // resetSeqno resets the packet sequence number. 166 | func (c *Conn) resetSeqno() { 167 | c.seqno = 0 168 | c.rw.reset() 169 | } 170 | -------------------------------------------------------------------------------- /prot_text.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | "encoding/binary" 30 | "fmt" 31 | "os" 32 | "reflect" 33 | "strconv" 34 | "strings" 35 | "time" 36 | ) 37 | 38 | // server commands (unexported) 39 | const ( 40 | _ = iota // _COM_SLEEP 41 | _COM_QUIT 42 | _COM_INIT_DB 43 | _COM_QUERY 44 | _COM_FIELD_LIST 45 | _COM_CREATE_DB 46 | _COM_DROP_DB 47 | _COM_REFRESH 48 | _COM_SHUTDOWN 49 | _COM_STATISTICS 50 | _COM_PROCESS_INFO 51 | _ // _COM_CONNECT 52 | _ // _COM_PROCESS_KILL 53 | _ // _COM_DEBUG 54 | _ // _COM_PING 55 | _ // _COM_TIME 56 | _ // _COM_DELAYED_INSERT 57 | _ // _COM_CHANGE_USER 58 | _COM_BINLOG_DUMP 59 | _ // _COM_TABLE_DUMP 60 | _ // _COM_CONNECT_OUT 61 | _COM_REGISTER_SLAVE 62 | _COM_STMT_PREPARE 63 | _COM_STMT_EXECUTE 64 | _COM_STMT_SEND_LONG_DATA 65 | _COM_STMT_CLOSE 66 | _COM_STMT_RESET 67 | _COM_SET_OPTION 68 | _COM_STMT_FETCH 69 | _ // _COM_DAEMON 70 | _COM_END // must always be last 71 | ) 72 | 73 | // client/server capability flags (unexported) 74 | const ( 75 | _CLIENT_LONG_PASSWORD = 1 << iota 76 | _CLIENT_FOUND_ROWS 77 | _CLIENT_LONG_FLAG 78 | _CLIENT_CONNECT_WITH_DB 79 | _CLIENT_NO_SCHEMA 80 | _CLIENT_COMPRESS 81 | _CLIENT_ODBC 82 | _CLIENT_LOCAL_FILES 83 | _CLIENT_IGNORE_SPACE 84 | _CLIENT_PROTOCOL41 85 | _CLIENT_INTERACTIVE 86 | _CLIENT_SSL 87 | _CLIENT_IGNORE_SIGPIPE 88 | _CLIENT_TRANSACTIONS 89 | _CLIENT_RESERVED 90 | _CLIENT_SECURE_CONNECTION 91 | _CLIENT_MULTI_STATEMENTS 92 | _CLIENT_MULTI_RESULTS 93 | _CLIENT_PS_MULTI_RESULTS 94 | _CLIENT_PLUGIN_AUTH 95 | _CLIENT_CONNECT_ATTRS 96 | _CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA 97 | _CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS 98 | _CLIENT_SESSION_TRACK 99 | _ // unassigned, 1 << 24 100 | _ 101 | _ 102 | _ 103 | _ 104 | _CLIENT_PROGRESS // 1 << 29 105 | _CLIENT_SSL_VERIFY_SERVER_CERT 106 | _CLIENT_REMEMBER_OPTIONS 107 | ) 108 | 109 | // server status flags (unexported) 110 | const ( 111 | _SERVER_STATUS_IN_TRANS = 1 << iota 112 | _SERVER_STATUS_AUTOCOMMIT 113 | _ // unassigned, 4 114 | _SERVER_MORE_RESULTS_EXISTS 115 | _SERVER_STATUS_NO_GOOD_INDEX_USED 116 | _SERVER_STATUS_NO_INDEX_USED 117 | _SERVER_STATUS_CURSOR_EXISTS 118 | _SERVER_STATUS_LAST_ROW_SENT 119 | _SERVER_STATUS_DB_DROPPED 120 | _SERVER_STATUS_NO_BACKSHASH_ESCAPES 121 | _SERVER_STATUS_METADATA_CHANGED 122 | _SERVER_QUERY_WAS_SLOW 123 | _SERVER_PS_OUT_PARAMS 124 | _SERVER_STATUS_IN_TRANS_READONLY 125 | _SERVER_SESSION_STATE_CHANGED 126 | ) 127 | 128 | // generic response packets (unexported) 129 | const ( 130 | _PACKET_OK = 0x00 131 | _PACKET_ERR = 0xff 132 | _PACKET_EOF = 0xfe 133 | _PACKET_INFILE_REQ = 0xfb 134 | ) 135 | 136 | const _DIGITS_PER_INTEGER = 9 137 | 138 | var _DIGITS_TO_BYTES = []int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4} 139 | 140 | // parseOkPacket parses the OK packet received from the server. 141 | func (c *Conn) parseOkPacket(b []byte) bool { 142 | var off, n int 143 | 144 | off++ // [00] the OK header (= _PACKET_OK) 145 | c.affectedRows, n = getLenencInt(b[off:]) 146 | off += n 147 | c.lastInsertId, n = getLenencInt(b[off:]) 148 | off += n 149 | 150 | c.statusFlags = binary.LittleEndian.Uint16(b[off : off+2]) 151 | off += 2 152 | c.warnings = binary.LittleEndian.Uint16(b[off : off+2]) 153 | off += 2 154 | // TODO : read rest of the fields 155 | 156 | return c.reportWarnings() 157 | } 158 | 159 | // parseErrPacket parses the ERR packet received from the server. 160 | func (c *Conn) parseErrPacket(b []byte) { 161 | var off int 162 | 163 | off++ // [ff] the ERR header (= errPacket) 164 | c.e.code = binary.LittleEndian.Uint16(b[off : off+2]) 165 | off += 2 166 | off++ // '#' the sql-state marker 167 | c.e.sqlState = string(b[off : off+5]) 168 | off += 5 169 | c.e.message = string(b[off:]) 170 | c.e.when = time.Now() 171 | } 172 | 173 | // parseEOFPacket parses the EOF packet received from the server. 174 | func (c *Conn) parseEOFPacket(b []byte) bool { 175 | var off int 176 | 177 | off++ // [fe] the EOF header (= _PACKET_EOF) 178 | // TODO: reset warning count 179 | c.warnings += binary.LittleEndian.Uint16(b[off : off+2]) 180 | off += 2 181 | c.statusFlags = binary.LittleEndian.Uint16(b[off : off+2]) 182 | 183 | return c.reportWarnings() 184 | } 185 | 186 | func (c *Conn) reportWarnings() bool { 187 | if c.p.reportWarnings && c.warnings > 0 { 188 | c.e.code = 0 189 | c.e.sqlState = "01000" 190 | c.e.message = "last command resulted in warning(s)" 191 | c.e.warnings = c.warnings 192 | c.e.when = time.Now() 193 | return true // warnings reported 194 | } 195 | return false 196 | } 197 | 198 | // 199 | 200 | // createComQuit generates the COM_QUIT packet. 201 | func (c *Conn) createComQuit() ([]byte, error) { 202 | var ( 203 | b []byte 204 | off, payloadLength int 205 | err error 206 | ) 207 | 208 | payloadLength = 1 // _COM_QUIT 209 | 210 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 211 | return nil, err 212 | } 213 | 214 | off += 4 // placeholder for protocol packet header 215 | b[off] = _COM_QUIT 216 | off++ 217 | 218 | return b[0:off], nil 219 | } 220 | 221 | // createComInitDb generates the COM_INIT_DB packet. 222 | func (c *Conn) createComInitDb(schema string) ([]byte, error) { 223 | var ( 224 | b []byte 225 | off, payloadLength int 226 | err error 227 | ) 228 | 229 | payloadLength = 1 + // _COM_INIT_DB 230 | len(schema) // length of schema name 231 | 232 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 233 | return nil, err 234 | } 235 | 236 | off += 4 // placeholder for protocol packet header 237 | b[off] = _COM_INIT_DB 238 | off++ 239 | off += copy(b[off:], schema) 240 | 241 | return b[0:off], nil 242 | } 243 | 244 | // createComQuery generates the COM_QUERY packet. 245 | func (c *Conn) createComQuery(query string) ([]byte, error) { 246 | var ( 247 | b []byte 248 | off, payloadLength int 249 | err error 250 | ) 251 | 252 | payloadLength = 1 + // _COM_QUERY 253 | len(query) // length of query 254 | 255 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 256 | return nil, err 257 | } 258 | 259 | off += 4 // placeholder for protocol packet header 260 | b[off] = _COM_QUERY 261 | off++ 262 | off += copy(b[off:], query) 263 | 264 | return b[0:off], nil 265 | } 266 | 267 | // createComFieldList generates the COM_FILED_LIST packet. 268 | func (c *Conn) createComFieldList(table, fieldWildcard string) ([]byte, error) { 269 | var ( 270 | b []byte 271 | off, payloadLength int 272 | err error 273 | ) 274 | 275 | payloadLength = 1 + // _COM_FIELD_LIST 276 | len(table) + // length of table name 277 | 1 + // NULL 278 | len(fieldWildcard) // length of field wildcard 279 | 280 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 281 | return nil, err 282 | } 283 | 284 | off += 4 // placeholder for protocol packet header 285 | b[off] = _COM_FIELD_LIST 286 | off++ 287 | off += putNullTerminatedString(b[off:], table) 288 | off += copy(b[off:], fieldWildcard) 289 | 290 | return b[0:off], nil 291 | } 292 | 293 | // createComCreateDb generates the COM_CREATE_DB packet. 294 | func (c *Conn) createComCreateDb(schema string) ([]byte, error) { 295 | var ( 296 | b []byte 297 | off, payloadLength int 298 | err error 299 | ) 300 | 301 | payloadLength = 1 + // _COM_CREATE_DB 302 | len(schema) // length of schema name 303 | 304 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 305 | return nil, err 306 | } 307 | 308 | off += 4 // placeholder for protocol packet header 309 | b[off] = _COM_CREATE_DB 310 | off++ 311 | off += copy(b[off:], schema) 312 | 313 | return b[0:off], nil 314 | } 315 | 316 | // createComDropDb generates the COM_DROP_DB packet. 317 | func (c *Conn) createComDropDb(schema string) ([]byte, error) { 318 | var ( 319 | b []byte 320 | off, payloadLength int 321 | err error 322 | ) 323 | 324 | payloadLength = 1 + // _COM_DROP_DB 325 | len(schema) // length of schema name 326 | 327 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 328 | return nil, err 329 | } 330 | 331 | off += 4 // placeholder for protocol packet header 332 | b[off] = _COM_DROP_DB 333 | off++ 334 | off += copy(b[off:], schema) 335 | 336 | return b[0:off], nil 337 | } 338 | 339 | // refresh flags (exported) 340 | const ( 341 | REFRESH_GRANT = 0x01 342 | REFRESH_LOG = 0x02 343 | REFRESH_TABLES = 0x04 344 | REFRESH_HOSTS = 0x08 345 | REFRESH_STATUS = 0x10 346 | REFRESH_SLAVE = 0x20 347 | REFRESH_THREADS = 0x40 348 | REFRESH_MASTER = 0x80 349 | ) 350 | 351 | // createComRefresh generates COM_REFRESH packet. 352 | func (c *Conn) createComRefresh(subCommand uint8) ([]byte, error) { 353 | var ( 354 | b []byte 355 | off, payloadLength int 356 | err error 357 | ) 358 | 359 | payloadLength = 1 + // _COM_REFRESH 360 | 1 // subCommand length 361 | 362 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 363 | return nil, err 364 | } 365 | 366 | off += 4 // placeholder for protocol packet header 367 | b[off] = _COM_REFRESH 368 | off++ 369 | b[off] = subCommand 370 | off++ 371 | 372 | return b[0:off], nil 373 | } 374 | 375 | type MyShutdownLevel uint8 376 | 377 | // shutdown flags (exported) 378 | const ( 379 | SHUTDOWN_DEFAULT MyShutdownLevel = 0x00 380 | SHUTDOWN_WAIT_CONNECTIONS MyShutdownLevel = 0x01 381 | SHUTDOWN_WAIT_TRANSACTIONS MyShutdownLevel = 0x02 382 | SHUTDOWN_WAIT_UPDATES MyShutdownLevel = 0x08 383 | SHUTDOWN_WAIT_ALL_BUFFERS MyShutdownLevel = 0x10 384 | SHUTDOWN_WAIT_CRITICAL_BUFFERS MyShutdownLevel = 0x11 385 | SHUTDOWN_KILL_QUERY MyShutdownLevel = 0xfe 386 | SHUTDOWN_KILL_CONNECTIONS MyShutdownLevel = 0xff 387 | ) 388 | 389 | // createComShutdown generates COM_SHUTDOWN packet. 390 | func (c *Conn) createComShutdown(level MyShutdownLevel) ([]byte, error) { 391 | var ( 392 | b []byte 393 | off, payloadLength int 394 | err error 395 | ) 396 | 397 | payloadLength = 1 + // _COM_SHUTDOWN 398 | 1 // shutdown level length 399 | 400 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 401 | return nil, err 402 | } 403 | 404 | off += 4 // placeholder for protocol packet header 405 | b[off] = _COM_SHUTDOWN 406 | off++ 407 | b[off] = byte(level) 408 | off++ 409 | 410 | return b[0:off], nil 411 | } 412 | 413 | // createComStatistics generates COM_STATISTICS packet. 414 | func (c *Conn) createComStatistics() ([]byte, error) { 415 | var ( 416 | b []byte 417 | off, payloadLength int 418 | err error 419 | ) 420 | 421 | payloadLength = 1 // _COM_STATISTICS 422 | 423 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 424 | return nil, err 425 | } 426 | 427 | off += 4 // placeholder for protocol packet header 428 | b[off] = _COM_STATISTICS 429 | off++ 430 | 431 | return b[0:off], nil 432 | } 433 | 434 | // createComProcessInfo generates COM_PROCESS_INFO packet. 435 | func (c *Conn) createComProcessInfo() ([]byte, error) { 436 | var ( 437 | b []byte 438 | off, payloadLength int 439 | err error 440 | ) 441 | 442 | payloadLength = 1 // _COM_PROCESS_INFO 443 | 444 | if b, err = c.buff.Reset(4 + payloadLength); err != nil { 445 | return nil, err 446 | } 447 | 448 | off += 4 // placeholder for protocol packet header 449 | b[off] = _COM_PROCESS_INFO 450 | off++ 451 | 452 | return b[0:off], nil 453 | } 454 | 455 | // parseColumnDefinitionPacket parses the column (field) definition packet. 456 | func parseColumnDefinitionPacket(b []byte, isComFieldList bool) *ColumnDefinition { 457 | var off, n int 458 | 459 | // alloc a new columnDefinition object 460 | col := new(ColumnDefinition) 461 | 462 | col.Catalog, n = getLenencString(b[off:]) 463 | off += n 464 | col.Schema, n = getLenencString(b[off:]) 465 | off += n 466 | col.Table, n = getLenencString(b[off:]) 467 | off += n 468 | col.OrgTable, n = getLenencString(b[off:]) 469 | off += n 470 | col.Name, n = getLenencString(b[off:]) 471 | off += n 472 | col.OrgName, n = getLenencString(b[off:]) 473 | off += n 474 | col.FixedLenFieldLength, n = getLenencInt(b[off:]) 475 | off += n 476 | col.Charset = binary.LittleEndian.Uint16(b[off : off+2]) 477 | off += 2 478 | col.ColumnLength = binary.LittleEndian.Uint32(b[off : off+4]) 479 | off += 4 480 | col.ColumnType = uint8(b[off]) 481 | off++ 482 | col.Flags = binary.LittleEndian.Uint16(b[off : off+2]) 483 | off += 2 484 | col.Decimals = uint8(b[off]) 485 | off++ 486 | 487 | off += 2 //filler [00] [00] 488 | 489 | if isComFieldList == true { 490 | col.DefaultValues, _ = getLenencString(b) 491 | } 492 | 493 | return col 494 | } 495 | 496 | // handleExec handles COM_QUERY and related packets for Conn's Exec() 497 | func (c *Conn) handleExec(query string, args []driver.Value) (driver.Result, error) { 498 | var ( 499 | b []byte 500 | err error 501 | ) 502 | 503 | // reset the protocol packet sequence number 504 | c.resetSeqno() 505 | 506 | if b, err = c.createComQuery(replacePlaceholders(query, args)); err != nil { 507 | return nil, err 508 | } 509 | 510 | // send COM_QUERY to the server 511 | if err := c.writePacket(b); err != nil { 512 | return nil, err 513 | } 514 | 515 | return c.handleExecResponse() 516 | } 517 | 518 | // handleQuery handles COM_QUERY and related packets for Conn's Query() 519 | func (c *Conn) handleQuery(query string, args []driver.Value) (driver.Rows, error) { 520 | var ( 521 | b []byte 522 | err error 523 | ) 524 | 525 | // reset the protocol packet sequence number 526 | c.resetSeqno() 527 | 528 | if b, err = c.createComQuery(replacePlaceholders(query, args)); err != nil { 529 | return nil, err 530 | } 531 | 532 | // send COM_QUERY to the server 533 | if err := c.writePacket(b); err != nil { 534 | return nil, err 535 | } 536 | 537 | return c.handleQueryResponse() 538 | } 539 | 540 | func (c *Conn) handleExecResponse() (*Result, error) { 541 | var ( 542 | err error 543 | warn bool 544 | b []byte 545 | ) 546 | 547 | if b, err = c.readPacket(); err != nil { 548 | return nil, err 549 | } 550 | 551 | switch b[0] { 552 | case _PACKET_ERR: // expected 553 | // handle err packet 554 | c.parseErrPacket(b) 555 | return nil, &c.e 556 | 557 | case _PACKET_OK: // expected 558 | // parse Ok packet and break 559 | warn = c.parseOkPacket(b) 560 | 561 | case _PACKET_INFILE_REQ: // expected 562 | // local infile request; handle it 563 | if err = c.handleInfileRequest(string(b[1:])); err != nil { 564 | return nil, err 565 | } 566 | default: // unexpected 567 | // the command resulted in Rows (anti-pattern ?); but since it 568 | // succeeded, we handle it and return nil 569 | columnCount, _ := getLenencInt(b) 570 | _, err = c.handleResultSet(uint16(columnCount)) // Rows ignored! 571 | return nil, err 572 | } 573 | 574 | res := new(Result) 575 | res.lastInsertId = int64(c.lastInsertId) 576 | res.rowsAffected = int64(c.affectedRows) 577 | 578 | if warn { 579 | // command resulted in warning(s), return results and error 580 | return res, &c.e 581 | } 582 | 583 | return res, nil 584 | } 585 | 586 | func (c *Conn) handleQueryResponse() (*Rows, error) { 587 | var ( 588 | err error 589 | warn bool 590 | b []byte 591 | ) 592 | 593 | if b, err = c.readPacket(); err != nil { 594 | return nil, err 595 | } 596 | 597 | switch b[0] { 598 | case _PACKET_ERR: // expected 599 | // handle err packet 600 | c.parseErrPacket(b) 601 | return nil, &c.e 602 | 603 | case _PACKET_OK: // unexpected! 604 | // the command resulted in a Result (anti-pattern ?); but 605 | // since it succeeded we handle it and return nil. 606 | warn = c.parseOkPacket(b) 607 | 608 | if warn { 609 | return nil, &c.e 610 | } 611 | 612 | return nil, nil 613 | 614 | case _PACKET_INFILE_REQ: // unexpected! 615 | // local infile request; handle it and return nil 616 | if err = c.handleInfileRequest(string(b[1:])); err != nil { 617 | return nil, err 618 | } 619 | return nil, nil 620 | 621 | default: // expected 622 | // break and handle result set 623 | break 624 | } 625 | 626 | // handle result set 627 | columnCount, _ := getLenencInt(b) 628 | return c.handleResultSet(uint16(columnCount)) 629 | } 630 | 631 | func (c *Conn) handleResultSet(columnCount uint16) (*Rows, error) { 632 | var ( 633 | err error 634 | b []byte 635 | done, warn bool 636 | ) 637 | 638 | rs := new(Rows) 639 | rs.columnDefs = make([]*ColumnDefinition, 0) 640 | rs.rows = make([]*row, 0) 641 | rs.columnCount = columnCount 642 | 643 | // read column definition packets 644 | for i := uint16(0); i < rs.columnCount; i++ { 645 | if b, err = c.readPacket(); err != nil { 646 | return nil, err 647 | } else { 648 | rs.columnDefs = append(rs.columnDefs, 649 | parseColumnDefinitionPacket(b, false)) 650 | } 651 | } 652 | 653 | // read EOF packet 654 | if b, err = c.readPacket(); err != nil { 655 | return nil, err 656 | } else { 657 | warn = c.parseEOFPacket(b) 658 | } 659 | 660 | // read resultset row packets (each containing rs.columnCount values), 661 | // until EOF packet. 662 | for !done { 663 | if b, err = c.readPacket(); err != nil { 664 | return nil, err 665 | } 666 | 667 | switch b[0] { 668 | case _PACKET_EOF: 669 | done = true 670 | case _PACKET_ERR: 671 | c.parseErrPacket(b) 672 | return nil, &c.e 673 | default: // result set row 674 | rs.rows = append(rs.rows, 675 | c.handleResultSetRow(b, rs)) 676 | } 677 | } 678 | if warn { 679 | // command resulted in warning(s), return results and error 680 | return rs, &c.e 681 | } 682 | return rs, nil 683 | } 684 | 685 | func (c *Conn) handleResultSetRow(b []byte, rs *Rows) *row { 686 | var ( 687 | v nullString 688 | off, n int 689 | ) 690 | 691 | columnCount := rs.columnCount 692 | r := new(row) 693 | r.columns = make([]interface{}, 0, columnCount) 694 | 695 | for i := uint16(0); i < columnCount; i++ { 696 | v, n = getLenencString(b[off:]) 697 | if v.valid == true { 698 | r.columns = append(r.columns, v.value) 699 | } else { 700 | r.columns = append(r.columns, nil) 701 | } 702 | off += n 703 | } 704 | return r 705 | } 706 | 707 | func (c *Conn) handleQuit() error { 708 | var ( 709 | b []byte 710 | err error 711 | ) 712 | 713 | // reset the protocol packet sequence number 714 | c.resetSeqno() 715 | 716 | if b, err = c.createComQuit(); err != nil { 717 | return err 718 | } 719 | 720 | return c.writePacket(b) 721 | } 722 | 723 | // stringify converts the given argument of arbitrary type to string. 'quote' 724 | // decides whether to quote (single-quote) the give resulting string. 725 | func stringify(d interface{}, quote bool) string { 726 | switch v := d.(type) { 727 | case string: 728 | if quote { 729 | return "'" + v + "'" 730 | } 731 | return v 732 | case []byte: 733 | s := string(v) 734 | if quote { 735 | return "'" + s + "'" 736 | } 737 | return s 738 | case bool: 739 | if v { 740 | return "TRUE" 741 | } else { 742 | return "FALSE" 743 | } 744 | case time.Time: 745 | t := fmt.Sprintf("%d-%d-%d %d:%d:%d", v.Year(), int(v.Month()), v.Day(), v.Hour(), v.Minute(), v.Second()) 746 | if quote { 747 | return strconv.Quote(t) 748 | } 749 | return t 750 | case nil: 751 | return "NULL" 752 | } 753 | 754 | rv := reflect.ValueOf(d) 755 | switch rv.Kind() { 756 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 757 | return strconv.FormatInt(rv.Int(), 10) 758 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 759 | return strconv.FormatUint(rv.Uint(), 10) 760 | case reflect.Float32: 761 | return strconv.FormatFloat(rv.Float(), 'f', -1, 32) 762 | case reflect.Float64: 763 | return strconv.FormatFloat(rv.Float(), 'f', -1, 64) 764 | default: 765 | // TODO: unsupported type? 766 | } 767 | return fmt.Sprintf("%v", d) 768 | } 769 | 770 | // replacePlaceholders replaces all ?'s with the stringified arguments. 771 | func replacePlaceholders(query string, args []driver.Value) string { 772 | if len(args) == 0 { 773 | return query 774 | } 775 | 776 | s := strings.Split(query, "?") 777 | final := make([]string, 0) 778 | 779 | for i, arg := range args { 780 | final = append(final, s[i]) 781 | final = append(final, stringify(arg, true)) 782 | } 783 | final = append(final, s[len(s)-1]) 784 | return strings.Join(final, "") 785 | } 786 | 787 | func (c *Conn) handleInfileRequest(filename string) error { 788 | var ( 789 | err, savedErr error 790 | errSaved, warn bool 791 | b []byte 792 | ) 793 | 794 | // do not skip on error to avoid "packets out of order" 795 | if b, err = c.createInfileDataPacket(filename); err != nil { 796 | savedErr = err 797 | errSaved = true 798 | goto L 799 | } else if err = c.writePacket(b); err != nil { 800 | savedErr = err 801 | errSaved = true 802 | goto L 803 | } 804 | 805 | L: 806 | // send an empty packet 807 | if b, err = c.createEmptyPacket(); err != nil { 808 | return err 809 | } 810 | 811 | if err = c.writePacket(b); err != nil { 812 | return err 813 | } 814 | 815 | // read ok/err packet from the server 816 | if b, err = c.readPacket(); err != nil { 817 | return err 818 | } 819 | 820 | switch b[0] { 821 | case _PACKET_ERR: 822 | // handle err packet 823 | c.parseErrPacket(b) 824 | return &c.e 825 | 826 | case _PACKET_OK: 827 | // parse Ok packet 828 | warn = c.parseOkPacket(b) 829 | 830 | default: 831 | return myError(ErrInvalidPacket) 832 | } 833 | 834 | if errSaved { 835 | return savedErr 836 | } 837 | 838 | if warn { 839 | return &c.e 840 | } 841 | 842 | return nil 843 | 844 | } 845 | 846 | // createInfileDataPacket generates a packet containing contents of the 847 | // requested local file 848 | func (c *Conn) createInfileDataPacket(filename string) ([]byte, error) { 849 | var ( 850 | f *os.File 851 | fi os.FileInfo 852 | b []byte 853 | off, n int 854 | err error 855 | ) 856 | 857 | if f, err = os.Open(filename); err != nil { 858 | return nil, myError(ErrFile, err) 859 | } 860 | defer f.Close() 861 | 862 | if fi, err = f.Stat(); err != nil { 863 | return nil, myError(ErrFile, err) 864 | } 865 | 866 | if b, err = c.buff.Reset(4 + int(fi.Size())); err != nil { 867 | return nil, err 868 | } 869 | 870 | off += 4 // placeholder for protocol packet header 871 | 872 | if n, err = f.Read(b[off:]); err != nil { 873 | return nil, myError(ErrFile, err) 874 | } 875 | 876 | off += n 877 | 878 | return b[0:off], nil 879 | } 880 | 881 | // createEmptyPacket generates an empty packet. 882 | func (c *Conn) createEmptyPacket() ([]byte, error) { 883 | var ( 884 | b []byte 885 | off int 886 | err error 887 | ) 888 | 889 | if b, err = c.buff.Reset(4); err != nil { 890 | return nil, err 891 | } 892 | 893 | off += 4 // placeholder for protocol packet header 894 | 895 | return b[0:off], nil 896 | } 897 | -------------------------------------------------------------------------------- /result.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | type Result struct { 28 | lastInsertId, rowsAffected int64 29 | } 30 | 31 | func (r *Result) LastInsertId() (int64, error) { 32 | return r.lastInsertId, nil 33 | } 34 | 35 | func (r *Result) RowsAffected() (int64, error) { 36 | return r.rowsAffected, nil 37 | } 38 | -------------------------------------------------------------------------------- /rows.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | "io" 30 | ) 31 | 32 | type Rows struct { 33 | columnCount uint16 34 | columnDefs []*ColumnDefinition 35 | rows []*row 36 | 37 | // iterator-related 38 | pos uint64 39 | closed bool 40 | } 41 | 42 | type ColumnDefinition struct { 43 | Catalog nullString 44 | Schema nullString 45 | Table nullString 46 | OrgTable nullString 47 | Name nullString 48 | OrgName nullString 49 | FixedLenFieldLength uint64 50 | Charset uint16 51 | ColumnLength uint32 52 | ColumnType uint8 53 | Flags uint16 54 | Decimals uint8 55 | DefaultValues nullString 56 | } 57 | 58 | type row struct { 59 | columns []interface{} 60 | } 61 | 62 | func (r *Rows) Columns() []string { 63 | columns := make([]string, 0, r.columnCount) 64 | for i := 0; i < int(r.columnCount); i++ { 65 | columns = append(columns, r.columnDefs[i].Name.value) 66 | } 67 | return columns 68 | } 69 | 70 | func (r *Rows) Close() error { 71 | // reset the iterator position and mark it as closed 72 | r.pos = 0 73 | r.closed = true 74 | return nil 75 | } 76 | 77 | func (r *Rows) Next(dest []driver.Value) error { 78 | if r.closed == true { 79 | return myError(ErrCursor) 80 | } 81 | 82 | if r.pos >= uint64(len(r.rows)) { 83 | return io.EOF 84 | } 85 | 86 | for i, v := range r.rows[r.pos].columns { 87 | dest[i] = v 88 | } 89 | r.pos++ 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /ssl.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "crypto/tls" 29 | "crypto/x509" 30 | "io/ioutil" 31 | ) 32 | 33 | // sslConnect establishes a SSL connection with the server. 34 | func (c *Conn) sslConnect() error { 35 | var ( 36 | cert tls.Certificate 37 | certPool *x509.CertPool 38 | pemCerts []byte 39 | conn *tls.Conn 40 | err error 41 | ) 42 | 43 | if c.p.sslCA != "" { 44 | certPool = x509.NewCertPool() 45 | if pemCerts, err = ioutil.ReadFile(c.p.sslCA); err != nil { 46 | return myError(ErrSSLConnection, err) 47 | } else { 48 | certPool.AppendCertsFromPEM(pemCerts) 49 | } 50 | } 51 | 52 | if cert, err = tls.LoadX509KeyPair(c.p.sslCert, c.p.sslKey); err != nil { 53 | return myError(ErrSSLConnection, err) 54 | } 55 | 56 | config := tls.Config{Certificates: []tls.Certificate{cert}, 57 | InsecureSkipVerify: true, 58 | RootCAs: certPool} 59 | 60 | conn = tls.Client(c.conn, &config) 61 | 62 | if err = conn.Handshake(); err != nil { 63 | return myError(ErrSSLConnection, err) 64 | } 65 | 66 | // update the connection handle 67 | c.conn = conn 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /stmt.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | ) 30 | 31 | type Stmt struct { 32 | id uint32 33 | c *Conn 34 | 35 | // COM_STMT_PREPARE 36 | query string 37 | 38 | // COM_STMT_PREPARE response 39 | columnCount uint16 40 | paramCount uint16 41 | warnings uint16 42 | // TODO: where to use the following received column definitions? 43 | paramDefs []*ColumnDefinition 44 | columnDefs []*ColumnDefinition 45 | 46 | // COM_STMT_EXECUTE 47 | flags uint8 48 | iterationCount uint32 49 | newParamsBoundFlag uint8 50 | } 51 | 52 | func (s *Stmt) Close() error { 53 | return s.handleClose() 54 | } 55 | 56 | func (s *Stmt) NumInput() int { 57 | return int(s.paramCount) 58 | } 59 | 60 | func (s *Stmt) Exec(args []driver.Value) (driver.Result, error) { 61 | return s.handleExec(args) 62 | } 63 | 64 | func (s *Stmt) Query(args []driver.Value) (driver.Rows, error) { 65 | return s.handleQuery(args) 66 | } 67 | 68 | func (s *Stmt) ColumnConverter(idx int) driver.ValueConverter { 69 | return defaultParameterConverter 70 | } 71 | -------------------------------------------------------------------------------- /tx.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | type Tx struct { 28 | c *Conn 29 | } 30 | 31 | func (t *Tx) Commit() error { 32 | _, err := t.c.handleExec("COMMIT", nil) 33 | return err 34 | } 35 | 36 | func (t *Tx) Rollback() error { 37 | _, err := t.c.handleExec("ROLLBACK", nil) 38 | return err 39 | } 40 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "database/sql/driver" 29 | "fmt" 30 | "strconv" 31 | "strings" 32 | "time" 33 | ) 34 | 35 | var ( 36 | // MaxTime is the maximum allowable TIMESTAMP value in MySQL '2038-01-19 03:14:07 UTC' 37 | MaxTime time.Time = time.Date(2038, time.January, 19, 3, 14, 7, 0, time.UTC) 38 | // MinTime is the minimum allowable TIMESTAMP value in MySQL '1970-01-01 00:00:01' UTC' 39 | MinTime time.Time = time.Date(1970, time.January, 01, 0, 0, 1, 0, time.UTC) 40 | 41 | // MaxDuration is the maximum allowable TIME value in MySQL '838:59:59.000000' 42 | MaxDuration time.Duration = 838*time.Hour + 59*time.Minute + 59*time.Second 43 | // MinDuration is the minimun allowable TIME value in MySQL '-838:59:59.000000' 44 | MinDuration time.Duration = -1 * MaxDuration 45 | 46 | InvalidTime time.Time = MaxTime.Add(1 * time.Second) 47 | InvalidDuration time.Duration = MaxDuration + 1*time.Second 48 | ) 49 | 50 | // NullTime represents a Time type the may be null. 51 | type NullTime struct { 52 | Time time.Time 53 | Valid bool 54 | } 55 | 56 | // Scan implements the scanner interface. 57 | func (nt *NullTime) Scan(value interface{}) error { 58 | if value == nil { 59 | nt.Time, nt.Valid = InvalidTime, false 60 | return nil 61 | } 62 | 63 | switch v := value.(type) { 64 | case time.Time: 65 | nt.Time, nt.Valid = v, true 66 | return nil 67 | 68 | // TODO: handle other types/cases 69 | default: 70 | } 71 | return nil 72 | } 73 | 74 | // Value implements the driver's Valuer interface. 75 | func (nt NullTime) Value() (driver.Value, error) { 76 | if !nt.Valid { 77 | return nil, nil 78 | } 79 | return nt.Time, nil 80 | } 81 | 82 | type NullDuration struct { 83 | Duration time.Duration 84 | Valid bool 85 | } 86 | 87 | // Scan implements the scanner interface. 88 | func (nd *NullDuration) Scan(value interface{}) error { 89 | var err error 90 | 91 | if value == nil { 92 | nd.Duration, nd.Valid = InvalidDuration, false 93 | return nil 94 | } 95 | 96 | switch v := value.(type) { 97 | case string: 98 | if nd.Duration, err = parseDuration(v); err != nil { 99 | nd.Duration, nd.Valid = 0, false 100 | return nil 101 | } 102 | nd.Valid = true 103 | 104 | case time.Duration: 105 | nd.Duration, nd.Valid = v, true 106 | 107 | // TODO: handle other types/cases 108 | default: 109 | } 110 | 111 | return nil 112 | } 113 | 114 | // Value implements the driver's Valuer interface. 115 | func (nd NullDuration) Value() (driver.Value, error) { 116 | if !nd.Valid { 117 | return nil, nil 118 | } 119 | return formatDuration(nd.Duration), nil 120 | } 121 | 122 | // parseDuration parses the input specified in MySQL's TIME format into 123 | // mysql.Duration type. 124 | func parseDuration(s string) (time.Duration, error) { 125 | var d time.Duration 126 | 127 | v := strings.Split(s, ":") 128 | switch len(v) { 129 | case 3: 130 | if secs, err := strconv.ParseFloat(v[2], 64); err != nil { 131 | return 0, myError(ErrInvalidType, err) 132 | } else { 133 | d += time.Duration(secs*1000000) * time.Microsecond 134 | } 135 | fallthrough 136 | case 2: 137 | if mins, err := strconv.ParseInt(v[1], 10, 64); err != nil { 138 | return 0, myError(ErrInvalidType, err) 139 | } else { 140 | d += time.Duration(mins) * time.Minute 141 | } 142 | fallthrough 143 | case 1: 144 | if hours, err := strconv.ParseInt(v[0], 10, 64); err != nil { 145 | return 0, myError(ErrInvalidType, err) 146 | } else { 147 | d += time.Duration(hours) * time.Hour 148 | } 149 | default: 150 | } 151 | 152 | return d, nil 153 | } 154 | 155 | // formatDuration formats the specified time.Duration in MySQL TIME format. 156 | func formatDuration(d time.Duration) string { 157 | var neg string 158 | 159 | if d < 0 { 160 | neg = "-" 161 | d *= -1 162 | } 163 | 164 | hours := int(d / time.Hour) 165 | d %= time.Hour 166 | 167 | mins := int(d / time.Minute) 168 | d %= time.Minute 169 | 170 | secs := (float64(d/time.Microsecond) / 1000000) 171 | 172 | if secs == 0 { 173 | return fmt.Sprintf("%s%02d:%02d:%02d", neg, hours, mins, 0) 174 | } 175 | return fmt.Sprintf("%s%02d:%02d:%02f", neg, hours, mins, secs) 176 | } 177 | 178 | // for internal use only 179 | type nullString struct { 180 | value string 181 | valid bool // valid is true if 'the string' is not NULL 182 | } 183 | 184 | var defaultParameterConverter DefaultParameterConverter 185 | 186 | type DefaultParameterConverter struct{} 187 | 188 | func (DefaultParameterConverter) ConvertValue(v interface{}) (driver.Value, error) { 189 | switch s := v.(type) { 190 | case NullTime: 191 | if s.Valid == false { 192 | return nil, nil 193 | } else { 194 | return s.Time, nil 195 | } 196 | case time.Duration: 197 | return formatDuration(s), nil 198 | case NullDuration: 199 | if s.Valid == false { 200 | return nil, nil 201 | } else { 202 | return formatDuration(s.Duration), nil 203 | } 204 | default: 205 | return driver.DefaultParameterConverter.ConvertValue(v) 206 | } 207 | // shouldn't reach here 208 | return nil, nil 209 | } 210 | -------------------------------------------------------------------------------- /url.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "net/url" 29 | "strconv" 30 | "strings" 31 | ) 32 | 33 | // default properties (unexported) 34 | const ( 35 | _DEFAULT_HOST = "127.0.0.1" 36 | _DEFAULT_PORT = "3306" 37 | _DEFAULT_MAX_PACKET_SIZE = 16 * 1024 * 1024 // 16MB 38 | _DEFAULT_SLAVE_ID = 0 39 | _DEFAULT_CAPABILITIES = (_CLIENT_LONG_PASSWORD | 40 | _CLIENT_LONG_FLAG | 41 | _CLIENT_TRANSACTIONS | 42 | _CLIENT_PROTOCOL41 | 43 | _CLIENT_SECURE_CONNECTION | 44 | _CLIENT_MULTI_RESULTS | 45 | _CLIENT_PLUGIN_AUTH) 46 | _DEFAULT_BINLOG_VERIFY_CHECKSUM = false 47 | ) 48 | 49 | const ( 50 | _MAX_PACKET_SIZE_MAX = 1024 * 1024 * 1024 // 1GB 51 | ) 52 | 53 | type properties struct { 54 | scheme string // mysql or file (for binlog files) 55 | file string // file:// 56 | username string 57 | password string 58 | passwordSet bool 59 | address string // host:port 60 | schema string 61 | socket string 62 | clientCapabilities uint32 63 | maxPacketSize uint32 64 | 65 | sslCA string 66 | sslCert string 67 | sslKey string 68 | 69 | reportWarnings bool // report warnings count as error 70 | 71 | binlogSlaveId uint32 // used while registering as slave 72 | // send EOF packet instead of blocking if no more events are left 73 | binlogDumpNonBlock bool 74 | // verify checksum of binary log events 75 | binlogVerifyChecksum bool 76 | } 77 | 78 | func (p *properties) parseUrl(dsn string) error { 79 | var ( 80 | u *url.URL 81 | err error 82 | ) 83 | 84 | // initialize default properties 85 | p.clientCapabilities = _DEFAULT_CAPABILITIES 86 | 87 | if u, err = url.Parse(dsn); err != nil { 88 | return myError(ErrInvalidDSN, err) 89 | } 90 | 91 | // we check for its correctness later 92 | p.scheme = u.Scheme 93 | 94 | if p.scheme == "file" { 95 | p.file = u.Path 96 | } 97 | 98 | if u.User != nil { 99 | p.username = u.User.Username() 100 | p.password, p.passwordSet = u.User.Password() 101 | } 102 | p.address = parseHost(u.Host) 103 | 104 | p.schema = strings.TrimLeft(u.Path, "/") 105 | if p.schema != "" { 106 | p.clientCapabilities |= _CLIENT_CONNECT_WITH_DB 107 | } 108 | 109 | query := u.Query() 110 | 111 | // Socket 112 | p.socket = query.Get("Socket") 113 | 114 | // LocalInfile 115 | if val := query.Get("LocalInfile"); val != "" { 116 | if v, err := strconv.ParseBool(val); err != nil { 117 | return myError(ErrInvalidProperty, "LocalInfile", err) 118 | } else if v { 119 | p.clientCapabilities |= _CLIENT_LOCAL_FILES 120 | } 121 | } 122 | 123 | // MaxAllowedPacket 124 | if val := query.Get("MaxAllowedPacket"); val != "" { 125 | if v, err := strconv.ParseUint(val, 10, 32); err != nil { 126 | return myError(ErrInvalidProperty, "MaxAllowedPacket", err) 127 | } else { 128 | p.maxPacketSize = uint32(v) 129 | if p.maxPacketSize > _MAX_PACKET_SIZE_MAX { 130 | return myError(ErrInvalidPropertyValue, "MaxAllowedPacket", 131 | p.maxPacketSize) 132 | } 133 | } 134 | } else { 135 | p.maxPacketSize = _DEFAULT_MAX_PACKET_SIZE 136 | } 137 | 138 | // SSLCA 139 | if val := query.Get("SSLCA"); val != "" { 140 | p.sslCA = val 141 | p.clientCapabilities |= _CLIENT_SSL 142 | } 143 | 144 | // SSLCert 145 | if val := query.Get("SSLCert"); val != "" { 146 | p.sslCert = val 147 | p.clientCapabilities |= _CLIENT_SSL 148 | } 149 | 150 | // SSLKey 151 | if val := query.Get("SSLKey"); val != "" { 152 | p.sslKey = val 153 | p.clientCapabilities |= _CLIENT_SSL 154 | } 155 | 156 | // Compress 157 | if val := query.Get("Compress"); val != "" { 158 | if v, err := strconv.ParseBool(val); err != nil { 159 | return myError(ErrInvalidProperty, "Compress", err) 160 | } else if v { 161 | p.clientCapabilities |= _CLIENT_COMPRESS 162 | } 163 | } 164 | 165 | // BinlogSlaveId 166 | if val := query.Get("BinlogSlaveId"); val != "" { 167 | if v, err := strconv.ParseUint(val, 10, 32); err != nil { 168 | return myError(ErrInvalidProperty, "BinlogSlaveId", err) 169 | } else { 170 | p.binlogSlaveId = uint32(v) 171 | } 172 | } else { 173 | p.binlogSlaveId = _DEFAULT_SLAVE_ID 174 | } 175 | 176 | // ReportWarnings 177 | if val := query.Get("ReportWarnings"); val != "" { 178 | if v, err := strconv.ParseBool(val); err != nil { 179 | return myError(ErrInvalidProperty, "ReportWarnings", err) 180 | } else { 181 | p.reportWarnings = v 182 | } 183 | } 184 | 185 | // BinlogDumpNonBlock 186 | if val := query.Get("BinlogDumpNonBlock"); val != "" { 187 | if v, err := strconv.ParseBool(val); err != nil { 188 | return myError(ErrInvalidProperty, "BinlogDumpNonBlock", err) 189 | } else { 190 | p.binlogDumpNonBlock = v 191 | } 192 | } 193 | 194 | // BinlogVerifyChecksum 195 | if val := query.Get("BinlogVerifyChecksum"); val != "" { 196 | if v, err := strconv.ParseBool(val); err != nil { 197 | return myError(ErrInvalidProperty, "BinlogVerifyChecksum", err) 198 | } else { 199 | p.binlogVerifyChecksum = v 200 | } 201 | } else { 202 | p.binlogVerifyChecksum = _DEFAULT_BINLOG_VERIFY_CHECKSUM 203 | } 204 | 205 | return nil 206 | } 207 | 208 | // parseHost returns the address in 'host:port' format. default ip (127.0.0.1) and 209 | // port (3306) are used if not specified. 210 | func parseHost(addr string) string { 211 | var ( 212 | host, port string 213 | defaultAssigned bool 214 | ) 215 | 216 | v := strings.Split(addr, ":") 217 | 218 | switch len(v) { 219 | case 2: 220 | host = v[0] 221 | port = v[1] 222 | 223 | if host == "" { 224 | host = _DEFAULT_HOST 225 | defaultAssigned = true 226 | } 227 | 228 | if port == "" { 229 | port = _DEFAULT_PORT 230 | defaultAssigned = true 231 | } 232 | 233 | if defaultAssigned == false { 234 | return addr // addr is already in required format 235 | } 236 | break 237 | 238 | case 1: 239 | host = v[0] 240 | if host == "" { 241 | host = _DEFAULT_HOST 242 | } 243 | port = _DEFAULT_PORT 244 | case 0: 245 | fallthrough 246 | default: 247 | host = _DEFAULT_HOST 248 | port = _DEFAULT_PORT 249 | break 250 | } 251 | return strings.Join([]string{host, port}, ":") 252 | } 253 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | import ( 28 | "encoding/binary" 29 | ) 30 | 31 | // getUint24 converts 3-byte byte little-endian slice into uint32 32 | func getUint24(b []byte) uint32 { 33 | return uint32(b[0]) | 34 | uint32(b[1])<<8 | 35 | uint32(b[2])<<16 36 | } 37 | 38 | // getUint48 converts 6-byte byte little-endian slice into uint64 39 | func getUint48(b []byte) uint64 { 40 | return uint64(b[0]) | 41 | uint64(b[1])<<8 | 42 | uint64(b[2])<<16 | 43 | uint64(b[3])<<24 | 44 | uint64(b[4])<<32 | 45 | uint64(b[5])<<40 46 | } 47 | 48 | func getInt16(b []byte) int16 { 49 | return int16(b[0]) | 50 | int16(b[1])<<8 51 | } 52 | 53 | func getInt32(b []byte) int32 { 54 | return int32(b[0]) | 55 | int32(b[1])<<8 | 56 | int32(b[2])<<16 | 57 | int32(b[3])<<24 58 | } 59 | 60 | func getInt64(b []byte) int64 { 61 | return int64(b[0]) | 62 | int64(b[1])<<8 | 63 | int64(b[2])<<16 | 64 | int64(b[3])<<24 | 65 | int64(b[4])<<32 | 66 | int64(b[5])<<40 | 67 | int64(b[6])<<48 | 68 | int64(b[7])<<56 69 | } 70 | 71 | // putUint24 stores the given uint32 into the specified 3-byte byte slice in little-endian 72 | func putUint24(b []byte, v uint32) { 73 | b[0] = byte(v) 74 | b[1] = byte(v >> 8) 75 | b[2] = byte(v >> 16) 76 | } 77 | 78 | // getLenencInt retrieves the number from the specified buffer stored in 79 | // length-encoded integer format and returns the number of bytes read. 80 | func getLenencInt(b []byte) (v uint64, n int) { 81 | first := b[0] 82 | 83 | switch { 84 | // 1-byte 85 | case first <= 0xfb: 86 | v = uint64(first) 87 | n = 1 88 | // 2-byte 89 | case first == 0xfc: 90 | v = uint64(binary.LittleEndian.Uint16(b[1:3])) 91 | n = 3 92 | // 3-byte 93 | case first == 0xfd: 94 | v = uint64(getUint24(b[1:4])) 95 | n = 4 96 | // 8-byte 97 | case first == 0xfe: 98 | v = binary.LittleEndian.Uint64(b[1:9]) 99 | n = 9 100 | // TODO: handle error 101 | default: 102 | } 103 | return 104 | } 105 | 106 | // putLenencInt stores the given number into the specified buffer using 107 | // length-encoded integer format and returns the number of bytes written. 108 | func putLenencInt(b []byte, v uint64) (n int) { 109 | switch { 110 | case v < 251: 111 | b[0] = byte(v) 112 | n = 1 113 | case v >= 251 && v < 2^16: 114 | b[0] = 0xfc 115 | binary.LittleEndian.PutUint16(b[1:3], uint16(v)) 116 | n = 3 117 | case v >= 2^16 && v < 2^24: 118 | b[0] = 0xfd 119 | putUint24(b[1:4], uint32(v)) 120 | n = 4 121 | case v >= 2^24 && v < 2^64: 122 | b[0] = 0xfe 123 | binary.LittleEndian.PutUint64(b[1:9], v) 124 | n = 9 125 | } 126 | return 127 | } 128 | 129 | // lenencIntSize returns the size needed to store a number using the 130 | // length-encoded integer format. 131 | func lenencIntSize(v int) int { 132 | switch { 133 | case v < 251: 134 | return 1 135 | case v >= 251 && v < 2^16: 136 | return 3 137 | case v >= 2^16 && v < 2^24: 138 | return 4 139 | case v >= 2^24 && v < 2^64: 140 | return 9 141 | } 142 | // control shouldn't reach here 143 | return 0 144 | } 145 | 146 | // length-encoded string 147 | func getLenencString(b []byte) (s nullString, n int) { 148 | length, n := getLenencInt(b) 149 | 150 | if length == 0xfb { // NULL 151 | s.valid = false 152 | } else { 153 | s.value = string(b[n : n+int(length)]) 154 | s.valid = true 155 | n += int(length) 156 | } 157 | return 158 | } 159 | 160 | func putLenencString(b []byte, v string) (n int) { 161 | n = putLenencInt(b[0:], uint64(len(v))) 162 | n += copy(b[n:], v) 163 | return 164 | } 165 | 166 | func getNullTerminatedString(b []byte) (v string, n int) { 167 | for { 168 | if n > len(b) || b[n] == 0 { 169 | break 170 | } else { 171 | n++ 172 | } 173 | } 174 | v = string(b[0:n]) 175 | n++ 176 | return 177 | } 178 | 179 | func putNullTerminatedString(b []byte, v string) (n int) { 180 | n = copy(b, v) 181 | b[n] = 0 // null terminator 182 | n++ 183 | return 184 | } 185 | 186 | // isNull returns whether the column at the given position is NULL; the first 187 | // column's position is 0. 188 | func isNull(bitmap []byte, pos, offset uint16) bool { 189 | // for binary protocol, result set row offset = 2 190 | pos += offset 191 | 192 | if (bitmap[pos/8] & (1 << (pos % 8))) != 0 { 193 | return true // null 194 | } 195 | return false // not null 196 | } 197 | 198 | // setBitCount returns the number of bits set in the given bitmap. 199 | func setBitCount(bitmap []byte) uint16 { 200 | var count, i, j uint16 201 | 202 | for i = 0; i < uint16(len(bitmap)); i++ { 203 | for j = 0; j < 8; j++ { 204 | if ((bitmap[i] >> j) & 0x01) == 1 { 205 | count++ 206 | } 207 | } 208 | } 209 | return count 210 | } 211 | 212 | // zerofy sets all bytes of the given slice to 0. 213 | func zerofy(b []byte) { 214 | for i := 0; i < len(b); i++ { 215 | b[i] = 0 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /uuid.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Nirbhay Choubey 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package mysql 26 | 27 | var ( 28 | uuidGroups = [...]byte{4, 2, 2, 2, 6} // UUID group size in bytes 29 | byteToHex = [...]byte{'0', '1', '2', '3', '4', '5', '6', 30 | '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'} 31 | ) 32 | 33 | type UUID struct { 34 | data [16]byte 35 | } 36 | 37 | // String converts UUID data to readable string. 38 | func (uuid *UUID) String() string { 39 | var ( 40 | res [36]byte 41 | cur byte 42 | resPos byte 43 | uuidPos byte 44 | ) 45 | 46 | for i := 0; i < len(uuidGroups); i++ { 47 | if i > 0 { 48 | res[resPos] = '-' 49 | resPos++ 50 | } 51 | 52 | for j := 0; j < int(uuidGroups[i]); j++ { 53 | cur = uuid.data[uuidPos] 54 | res[resPos] = byteToHex[cur>>4] 55 | resPos++ 56 | res[resPos] = byteToHex[cur&0xF] 57 | resPos++ 58 | 59 | uuidPos++ 60 | } 61 | } 62 | return string(res[:]) 63 | } 64 | --------------------------------------------------------------------------------