1 module mysql.type; 2 3 4 import std.algorithm; 5 import std.array : appender; 6 import std.conv : parse, to; 7 import std.datetime; 8 import std.format: format, formattedWrite; 9 import std.traits; 10 11 import mysql.protocol; 12 import mysql.packet; 13 import mysql.exception; 14 public import mysql.row; 15 16 17 struct MySQLRawString { 18 @disable this(); 19 20 this(const(char)[] data) { 21 data_ = data; 22 } 23 24 @property auto length() const { 25 return data_.length; 26 } 27 28 @property auto data() const { 29 return data_; 30 } 31 32 private const(char)[] data_; 33 } 34 35 36 37 struct MySQLFragment { 38 @disable this(); 39 40 this(const(char)[] data) { 41 data_ = data; 42 } 43 44 @property auto length() const { 45 return data_.length; 46 } 47 48 @property auto data() const { 49 return data_; 50 } 51 52 private const(char)[] data_; 53 } 54 55 56 struct MySQLBinary { 57 this(T)(T[] data) { 58 data_ = (cast(ubyte*)data.ptr)[0..typeof(T[].init[0]).sizeof * data.length]; 59 } 60 61 @property auto length() const { 62 return data_.length; 63 } 64 65 @property auto data() const { 66 return data_; 67 } 68 69 private const(ubyte)[] data_; 70 } 71 72 73 struct MySQLValue { 74 package enum BufferSize = max(ulong.sizeof, (ulong[]).sizeof, MySQLDateTime.sizeof, MySQLTime.sizeof); 75 package this(const(char)[] name, ColumnTypes type, bool signed, void* ptr, size_t size) { 76 assert(size <= BufferSize); 77 type_ = type; 78 sign_ = signed ? 0x00 : 0x80; 79 if (type != ColumnTypes.MYSQL_TYPE_NULL) 80 buffer_[0..size] = (cast(ubyte*)ptr)[0..size]; 81 name_ = name; 82 } 83 84 this(T)(T) if (is(Unqual!T == typeof(null))) { 85 type_ = ColumnTypes.MYSQL_TYPE_NULL; 86 sign_ = 0x00; 87 } 88 89 this(T)(T value) if (isIntegral!T || isBoolean!T) { 90 alias UT = Unqual!T; 91 92 static if (is(UT == long) || is(UT == ulong)) { 93 type_ = ColumnTypes.MYSQL_TYPE_LONGLONG; 94 } else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) { 95 type_ = ColumnTypes.MYSQL_TYPE_LONG; 96 } else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) { 97 type_ = ColumnTypes.MYSQL_TYPE_SHORT; 98 } else { 99 type_ = ColumnTypes.MYSQL_TYPE_TINY; 100 } 101 102 sign_ = isUnsigned!UT ? 0x80 : 0x00; 103 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 104 } 105 106 this(T)(T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) { 107 type_ = ColumnTypes.MYSQL_TYPE_TIMESTAMP; 108 sign_ = 0x00; 109 (*cast(MySQLDateTime*)buffer_.ptr).from(value); 110 } 111 112 this(T)(T value) if (is(Unqual!T == Duration)) { 113 type_ = ColumnTypes.MYSQL_TYPE_TIME; 114 sign_ = 0x00; 115 (*cast(MySQLTime*)buffer_.ptr).from(value); 116 } 117 118 this(T)(T value) if (isSomeString!T) { 119 static assert(typeof(T.init[0]).sizeof == 1, "Unsupported string type: " ~ T); 120 121 type_ = ColumnTypes.MYSQL_TYPE_STRING; 122 sign_ = 0x80; 123 124 auto slice = value[0..$]; 125 buffer_.ptr[0..typeof(slice).sizeof] = (cast(ubyte*)&slice)[0..typeof(slice).sizeof]; 126 } 127 128 this(T)(T value) if (is(Unqual!T == MySQLBinary)) { 129 type_ = ColumnTypes.MYSQL_TYPE_BLOB; 130 sign_ = 0x80; 131 buffer_.ptr[0..(ubyte[]).sizeof] = (cast(ubyte*)&value.data_)[0..(ubyte[]).sizeof]; 132 } 133 134 void toString(Appender)(ref Appender app) const { 135 final switch(type_) with (ColumnTypes) { 136 case MYSQL_TYPE_NULL: 137 break; 138 case MYSQL_TYPE_TINY: 139 if (isSigned) formattedWrite(&app, "%d", *cast(ubyte*)buffer_.ptr); 140 else formattedWrite(&app, "%d", *cast(byte*)buffer_.ptr); 141 break; 142 case MYSQL_TYPE_YEAR: 143 case MYSQL_TYPE_SHORT: 144 if (isSigned) formattedWrite(&app, "%d", *cast(short*)buffer_.ptr); 145 else formattedWrite(&app, "%d", *cast(ushort*)buffer_.ptr); 146 break; 147 case MYSQL_TYPE_INT24: 148 case MYSQL_TYPE_LONG: 149 if (isSigned) formattedWrite(&app, "%d", *cast(int*)buffer_.ptr); 150 else formattedWrite(&app, "%d", *cast(uint*)buffer_.ptr); 151 break; 152 case MYSQL_TYPE_LONGLONG: 153 if (isSigned) formattedWrite(&app, "%d", *cast(long*)buffer_.ptr); 154 else formattedWrite(&app, "%d", *cast(ulong*)buffer_.ptr); 155 break; 156 case MYSQL_TYPE_FLOAT: 157 formattedWrite(&app, "%g", *cast(float*)buffer_.ptr); 158 break; 159 case MYSQL_TYPE_DOUBLE: 160 formattedWrite(&app, "%g", *cast(double*)buffer_.ptr); 161 break; 162 case MYSQL_TYPE_SET: 163 case MYSQL_TYPE_ENUM: 164 case MYSQL_TYPE_VARCHAR: 165 case MYSQL_TYPE_VAR_STRING: 166 case MYSQL_TYPE_STRING: 167 case MYSQL_TYPE_JSON: 168 case MYSQL_TYPE_NEWDECIMAL: 169 case MYSQL_TYPE_DECIMAL: 170 case MYSQL_TYPE_TINY_BLOB: 171 case MYSQL_TYPE_MEDIUM_BLOB: 172 case MYSQL_TYPE_LONG_BLOB: 173 case MYSQL_TYPE_BLOB: 174 app.put(*cast(string*)buffer_.ptr); 175 break; 176 case MYSQL_TYPE_BIT: 177 case MYSQL_TYPE_GEOMETRY: 178 formattedWrite(&app, "%s", *cast(ubyte[]*)buffer_.ptr); 179 break; 180 case MYSQL_TYPE_TIME: 181 case MYSQL_TYPE_TIME2: 182 formattedWrite(&app, "%s", (*cast(MySQLTime*)buffer_.ptr).toDuration()); 183 break; 184 case MYSQL_TYPE_DATE: 185 case MYSQL_TYPE_NEWDATE: 186 case MYSQL_TYPE_DATETIME: 187 case MYSQL_TYPE_DATETIME2: 188 case MYSQL_TYPE_TIMESTAMP: 189 case MYSQL_TYPE_TIMESTAMP2: 190 formattedWrite(&app, "%s", (*cast(MySQLDateTime*)buffer_.ptr).to!DateTime()); 191 break; 192 } 193 } 194 195 string toString() const { 196 auto app = appender!string; 197 toString(app); 198 return app.data; 199 } 200 201 T get(T)(lazy T def) const { 202 return !isNull ? get!T : def; 203 } 204 205 T get(T)() const if (isScalarType!T) { 206 switch(type_) with (ColumnTypes) { 207 case MYSQL_TYPE_TINY: 208 return cast(T)(*cast(ubyte*)buffer_.ptr); 209 case MYSQL_TYPE_YEAR: 210 case MYSQL_TYPE_SHORT: 211 return cast(T)(*cast(ushort*)buffer_.ptr); 212 case MYSQL_TYPE_INT24: 213 case MYSQL_TYPE_LONG: 214 return cast(T)(*cast(uint*)buffer_.ptr); 215 case MYSQL_TYPE_LONGLONG: 216 return cast(T)(*cast(ulong*)buffer_.ptr); 217 case MYSQL_TYPE_FLOAT: 218 return cast(T)(*cast(float*)buffer_.ptr); 219 case MYSQL_TYPE_DOUBLE: 220 return cast(T)(*cast(double*)buffer_.ptr); 221 default: 222 throw new MySQLErrorException(format("Cannot convert '%s' from %s to scalar", name_, columnTypeName(type_))); 223 } 224 } 225 226 T get(T)() const if (is(Unqual!T == SysTime) || is(Unqual!T == DateTime) || is(Unqual!T == Date) || is(Unqual!T == TimeOfDay)) { 227 switch(type_) with (ColumnTypes) { 228 case MYSQL_TYPE_DATE: 229 case MYSQL_TYPE_NEWDATE: 230 case MYSQL_TYPE_DATETIME: 231 case MYSQL_TYPE_DATETIME2: 232 case MYSQL_TYPE_TIMESTAMP: 233 case MYSQL_TYPE_TIMESTAMP2: 234 return (*cast(MySQLDateTime*)buffer_.ptr).to!T; 235 default: 236 throw new MySQLErrorException(format("Cannot convert '%s' from %s to timestamp", name_, columnTypeName(type_))); 237 } 238 } 239 240 T get(T)() const if (is(Unqual!T == Duration)) { 241 switch(type_) with (ColumnTypes) { 242 case MYSQL_TYPE_TIME: 243 case MYSQL_TYPE_TIME2: 244 return (*cast(MySQLTime*)buffer_.ptr).toDuration; 245 default: 246 throw new MySQLErrorException(format("Cannot convert '%s' from %s to duration", name_, columnTypeName(type_))); 247 } 248 } 249 250 T get(T)() const if (isArray!T) { 251 switch(type_) with (ColumnTypes) { 252 case MYSQL_TYPE_SET: 253 case MYSQL_TYPE_ENUM: 254 case MYSQL_TYPE_VARCHAR: 255 case MYSQL_TYPE_VAR_STRING: 256 case MYSQL_TYPE_STRING: 257 case MYSQL_TYPE_JSON: 258 case MYSQL_TYPE_NEWDECIMAL: 259 case MYSQL_TYPE_DECIMAL: 260 return (*cast(T*)buffer_.ptr).dup; 261 case MYSQL_TYPE_BIT: 262 case MYSQL_TYPE_TINY_BLOB: 263 case MYSQL_TYPE_MEDIUM_BLOB: 264 case MYSQL_TYPE_LONG_BLOB: 265 case MYSQL_TYPE_BLOB: 266 case MYSQL_TYPE_GEOMETRY: 267 return (*cast(T*)buffer_.ptr).dup; 268 default: 269 throw new MySQLErrorException(format("Cannot convert '%s' from %s to array", name_, columnTypeName(type_))); 270 } 271 } 272 273 T peek(T)(lazy T def) const { 274 return !isNull ? peek!T : def; 275 } 276 277 T peek(T)() const if (isScalarType!T) { 278 return get!T; 279 } 280 281 T peek(T)() const if (is(Unqual!T == SysTime) || is(Unqual!T == DateTime) || is(Unqual!T == Date) || is(Unqual!T == TimeOfDay)) { 282 return get!T; 283 } 284 285 T peek(T)() const if (is(Unqual!T == Duration)) { 286 return get!T; 287 } 288 289 T peek(T)() const if (isArray!T) { 290 switch(type_) with (ColumnTypes) { 291 case MYSQL_TYPE_SET: 292 case MYSQL_TYPE_ENUM: 293 case MYSQL_TYPE_VARCHAR: 294 case MYSQL_TYPE_VAR_STRING: 295 case MYSQL_TYPE_STRING: 296 case MYSQL_TYPE_JSON: 297 case MYSQL_TYPE_NEWDECIMAL: 298 case MYSQL_TYPE_DECIMAL: 299 return (*cast(T*)buffer_.ptr); 300 case MYSQL_TYPE_BIT: 301 case MYSQL_TYPE_TINY_BLOB: 302 case MYSQL_TYPE_MEDIUM_BLOB: 303 case MYSQL_TYPE_LONG_BLOB: 304 case MYSQL_TYPE_BLOB: 305 case MYSQL_TYPE_GEOMETRY: 306 return (*cast(T*)buffer_.ptr); 307 default: 308 throw new MySQLErrorException(format("Cannot convert '%s' from %s to array", name_, columnTypeName(type_))); 309 } 310 } 311 312 bool isNull() const { 313 return type_ == ColumnTypes.MYSQL_TYPE_NULL; 314 } 315 316 ColumnTypes type() const { 317 return type_; 318 } 319 320 bool isSigned() const { 321 return sign_ == 0x00; 322 } 323 324 private: 325 ColumnTypes type_ = ColumnTypes.MYSQL_TYPE_NULL; 326 ubyte sign_; 327 ubyte[6] pad_; 328 ubyte[BufferSize] buffer_; 329 const(char)[] name_; 330 } 331 332 333 struct MySQLColumn { 334 uint length; 335 ushort flags; 336 ubyte decimals; 337 ColumnTypes type; 338 const(char)[] name; 339 } 340 341 342 alias MySQLHeader = MySQLColumn[]; 343 344 345 struct MySQLTime { 346 uint days; 347 ubyte negative; 348 ubyte hours; 349 ubyte mins; 350 ubyte secs; 351 uint usecs; 352 353 Duration toDuration() { 354 auto total = days * 86400_000_000L + 355 hours * 3600_000_000L + 356 mins * 60_000_000L + 357 secs * 1_000_000L + 358 usecs; 359 return dur!"usecs"(negative ? -total : total); 360 } 361 362 static MySQLTime from(Duration duration) { 363 MySQLTime time; 364 duration.abs.split!("days", "hours", "minutes", "seconds", "usecs")(time.days, time.hours, time.mins, time.secs, time.usecs); 365 time.negative = duration.isNegative ? 1 : 0; 366 return time; 367 } 368 } 369 370 void putMySQLTime(ref OutputPacket packet, in MySQLTime time) { 371 if (time.days || time.hours || time.mins || time.mins || time.usecs) { 372 auto usecs = time.usecs != 0; 373 packet.put!ubyte(usecs ? 12 : 8); 374 packet.put!ubyte(time.negative); 375 packet.put!uint(time.days); 376 packet.put!ubyte(time.hours); 377 packet.put!ubyte(time.mins); 378 packet.put!ubyte(time.secs); 379 if (usecs) 380 packet.put!uint(time.usecs); 381 } else { 382 packet.put!ubyte(0); 383 } 384 } 385 386 auto eatMySQLTime(ref InputPacket packet) { 387 MySQLTime time; 388 switch(packet.eat!ubyte) { 389 case 12: 390 time.negative = packet.eat!ubyte; 391 time.days = packet.eat!uint; 392 time.hours = packet.eat!ubyte; 393 time.mins = packet.eat!ubyte; 394 time.secs = packet.eat!ubyte; 395 time.usecs = packet.eat!uint; 396 break; 397 case 8: 398 time.negative = packet.eat!ubyte; 399 time.days = packet.eat!uint; 400 time.hours = packet.eat!ubyte; 401 time.mins = packet.eat!ubyte; 402 time.secs = packet.eat!ubyte; 403 break; 404 case 0: 405 break; 406 default: 407 throw new MySQLProtocolException("Bad time struct format"); 408 } 409 410 return time; 411 } 412 413 414 struct MySQLDateTime { 415 ushort year; 416 ubyte month; 417 ubyte day; 418 ubyte hour; 419 ubyte min; 420 ubyte sec; 421 uint usec; 422 423 bool valid() const { 424 return month != 0; 425 } 426 427 T to(T)() if (is(T == SysTime)) { 428 assert(valid()); 429 return SysTime(DateTime(year, month, day, hour, min, sec), usec.dur!"usecs", UTC()); 430 } 431 432 T to(T)() if (is(T == DateTime)) { 433 assert(valid()); 434 return DateTime(year, month, day, hour, min, sec); 435 } 436 437 T to(T)() if (is(T == Date)) { 438 assert(valid()); 439 return Date(year, month, day); 440 } 441 442 T to(T)() if (is(T == TimeOfDay)) { 443 return TimeOfDay(hour, min, sec); 444 } 445 446 static MySQLDateTime from(SysTime sysTime) { 447 MySQLDateTime time; 448 449 auto dateTime = cast(DateTime)sysTime; 450 time.year = dateTime.year; 451 time.month = dateTime.month; 452 time.day = dateTime.day; 453 time.hour = dateTime.hour; 454 time.min = dateTime.minute; 455 time.sec = dateTime.second; 456 time.usec = cast(int)sysTime.fracSecs.total!"usecs"; 457 458 return time; 459 } 460 461 static MySQLDateTime from(DateTime dateTime) { 462 MySQLDateTime time; 463 464 time.year = dateTime.year; 465 time.month = dateTime.month; 466 time.day = dateTime.day; 467 time.hour = dateTime.hour; 468 time.min = dateTime.minute; 469 time.sec = dateTime.second; 470 471 return time; 472 } 473 474 static MySQLDateTime from(Date date) { 475 MySQLDateTime time; 476 477 time.year = date.year; 478 time.month = date.month; 479 time.day = date.day; 480 481 return time; 482 } 483 } 484 485 void putMySQLDateTime(ref OutputPacket packet, in MySQLDateTime time) { 486 auto marker = packet.marker!ubyte; 487 ubyte length; 488 489 if (time.year || time.month || time.day) { 490 length = 4; 491 packet.put!ushort(time.year); 492 packet.put!ubyte(time.month); 493 packet.put!ubyte(time.day); 494 495 if (time.hour || time.min || time.sec || time.usec) { 496 length = 7; 497 packet.put!ubyte(time.hour); 498 packet.put!ubyte(time.min); 499 packet.put!ubyte(time.sec); 500 501 if (time.usec) { 502 length = 11; 503 packet.put!uint(time.usec); 504 } 505 } 506 } 507 508 packet.put!ubyte(marker, length); 509 } 510 511 auto eatMySQLDateTime(ref InputPacket packet) { 512 MySQLDateTime time; 513 switch(packet.eat!ubyte) { 514 case 11: 515 time.year = packet.eat!ushort; 516 time.month = packet.eat!ubyte; 517 time.day = packet.eat!ubyte; 518 time.hour = packet.eat!ubyte; 519 time.min = packet.eat!ubyte; 520 time.sec = packet.eat!ubyte; 521 time.usec = packet.eat!uint; 522 break; 523 case 7: 524 time.year = packet.eat!ushort; 525 time.month = packet.eat!ubyte; 526 time.day = packet.eat!ubyte; 527 time.hour = packet.eat!ubyte; 528 time.min = packet.eat!ubyte; 529 time.sec = packet.eat!ubyte; 530 break; 531 case 4: 532 time.year = packet.eat!ushort; 533 time.month = packet.eat!ubyte; 534 time.day = packet.eat!ubyte; 535 break; 536 case 0: 537 break; 538 default: 539 throw new MySQLProtocolException("Bad datetime struct format"); 540 } 541 542 return time; 543 } 544 545 private void skip(ref const(char)[] x, char ch) { 546 if (x.length && (x.ptr[0] == ch)) { 547 x = x[1..$]; 548 } else { 549 throw new MySQLProtocolException("Bad datetime string format"); 550 } 551 } 552 553 auto parseMySQLTime(const(char)[] x) { 554 MySQLTime time; 555 556 auto hours = x.parse!int; 557 if (hours < 0) { 558 time.negative = 1; 559 hours = -hours; 560 } 561 time.days = hours / 24; 562 time.hours = cast(ubyte)(hours % 24); 563 x.skip(':'); 564 time.mins = x.parse!ubyte; 565 x.skip(':'); 566 time.secs = x.parse!ubyte; 567 if (x.length) { 568 x.skip('.'); 569 time.usecs = x.parse!uint; 570 switch (6 - max(6, x.length)) { 571 case 0: break; 572 case 1: time.usecs *= 10; break; 573 case 2: time.usecs *= 100; break; 574 case 3: time.usecs *= 1_000; break; 575 case 4: time.usecs *= 10_000; break; 576 case 5: time.usecs *= 100_000; break; 577 default: assert("Bad datetime string format"); break; 578 } 579 } 580 581 return time; 582 } 583 584 auto parseMySQLDateTime(const(char)[] x) { 585 MySQLDateTime time; 586 587 time.year = x.parse!ushort; 588 x.skip('-'); 589 time.month = x.parse!ubyte; 590 x.skip('-'); 591 time.day = x.parse!ubyte; 592 if (x.length) { 593 x.skip(' '); 594 time.hour = x.parse!ubyte; 595 x.skip(':'); 596 time.min = x.parse!ubyte; 597 x.skip(':'); 598 time.sec = x.parse!ubyte; 599 600 if (x.length) { 601 x.skip('.'); 602 time.usec = x.parse!uint; 603 switch (6 - max(6, x.length)) { 604 case 0: break; 605 case 1: time.usec *= 10; break; 606 case 2: time.usec *= 100; break; 607 case 3: time.usec *= 1_000; break; 608 case 4: time.usec *= 10_000; break; 609 case 5: time.usec *= 100_000; break; 610 default: assert("Bad datetime string format"); break; 611 } 612 } 613 } 614 615 return time; 616 } 617 618 void eatValue(ref InputPacket packet, ref const MySQLColumn column, ref MySQLValue value) { 619 auto signed = (column.flags & FieldFlags.UNSIGNED_FLAG) == 0; 620 final switch(column.type) with (ColumnTypes) { 621 case MYSQL_TYPE_NULL: 622 value = MySQLValue(column.name, column.type, signed, null, 0); 623 break; 624 case MYSQL_TYPE_TINY: 625 auto x = packet.eat!ubyte; 626 value = MySQLValue(column.name, column.type, signed, &x, 1); 627 break; 628 case MYSQL_TYPE_YEAR: 629 case MYSQL_TYPE_SHORT: 630 auto x = packet.eat!ushort; 631 value = MySQLValue(column.name, column.type, signed, &x, 2); 632 break; 633 case MYSQL_TYPE_INT24: 634 case MYSQL_TYPE_LONG: 635 auto x = packet.eat!uint; 636 value = MySQLValue(column.name, column.type, signed, &x, 4); 637 break; 638 case MYSQL_TYPE_DOUBLE: 639 case MYSQL_TYPE_LONGLONG: 640 auto x = packet.eat!ulong; 641 value = MySQLValue(column.name, column.type, signed, &x, 8); 642 break; 643 case MYSQL_TYPE_FLOAT: 644 auto x = packet.eat!float; 645 value = MySQLValue(column.name, column.type, signed, &x, 4); 646 break; 647 case MYSQL_TYPE_SET: 648 case MYSQL_TYPE_ENUM: 649 case MYSQL_TYPE_VARCHAR: 650 case MYSQL_TYPE_VAR_STRING: 651 case MYSQL_TYPE_STRING: 652 case MYSQL_TYPE_JSON: 653 case MYSQL_TYPE_NEWDECIMAL: 654 case MYSQL_TYPE_DECIMAL: 655 auto x = packet.eat!(const(char)[])(cast(size_t)packet.eatLenEnc()); 656 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 657 break; 658 case MYSQL_TYPE_BIT: 659 case MYSQL_TYPE_TINY_BLOB: 660 case MYSQL_TYPE_MEDIUM_BLOB: 661 case MYSQL_TYPE_LONG_BLOB: 662 case MYSQL_TYPE_BLOB: 663 case MYSQL_TYPE_GEOMETRY: 664 auto x = packet.eat!(const(ubyte)[])(cast(size_t)packet.eatLenEnc()); 665 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 666 break; 667 case MYSQL_TYPE_TIME: 668 case MYSQL_TYPE_TIME2: 669 auto x = eatMySQLTime(packet); 670 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 671 break; 672 case MYSQL_TYPE_DATE: 673 case MYSQL_TYPE_NEWDATE: 674 case MYSQL_TYPE_DATETIME: 675 case MYSQL_TYPE_DATETIME2: 676 case MYSQL_TYPE_TIMESTAMP: 677 case MYSQL_TYPE_TIMESTAMP2: 678 auto x = eatMySQLDateTime(packet); 679 value = x.valid() ? MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof) : MySQLValue(column.name, ColumnTypes.MYSQL_TYPE_NULL, signed, null, 0); 680 break; 681 } 682 } 683 684 void eatValueText(ref InputPacket packet, ref const MySQLColumn column, ref MySQLValue value) { 685 auto signed = (column.flags & FieldFlags.UNSIGNED_FLAG) == 0; 686 auto svalue = (column.type != ColumnTypes.MYSQL_TYPE_NULL) ? cast(string)(packet.eat!(const(char)[])(cast(size_t)packet.eatLenEnc())) : string.init; 687 final switch(column.type) with (ColumnTypes) { 688 case MYSQL_TYPE_NULL: 689 value = MySQLValue(column.name, column.type, signed, null, 0); 690 break; 691 case MYSQL_TYPE_TINY: 692 auto x = (svalue.ptr[0] == '-') ? cast(ubyte)(-svalue[1..$].to!byte) : svalue.to!ubyte; 693 value = MySQLValue(column.name, column.type, signed, &x, 1); 694 break; 695 case MYSQL_TYPE_YEAR: 696 case MYSQL_TYPE_SHORT: 697 auto x = (svalue.ptr[0] == '-') ? cast(ushort)(-svalue[1..$].to!short) : svalue.to!ushort; 698 value = MySQLValue(column.name, column.type, signed, &x, 2); 699 break; 700 case MYSQL_TYPE_INT24: 701 case MYSQL_TYPE_LONG: 702 auto x = (svalue.ptr[0] == '-') ? cast(uint)(-svalue[1..$].to!int) : svalue.to!uint; 703 value = MySQLValue(column.name, column.type, signed, &x, 4); 704 break; 705 case MYSQL_TYPE_LONGLONG: 706 auto x = (svalue.ptr[0] == '-') ? cast(ulong)(-svalue[1..$].to!long) : svalue.to!ulong; 707 value = MySQLValue(column.name, column.type, signed, &x, 8); 708 break; 709 case MYSQL_TYPE_DOUBLE: 710 auto x = svalue.to!double; 711 value = MySQLValue(column.name, column.type, signed, &x, 8); 712 break; 713 case MYSQL_TYPE_FLOAT: 714 auto x = svalue.to!float; 715 value = MySQLValue(column.name, column.type, signed, &x, 4); 716 break; 717 case MYSQL_TYPE_SET: 718 case MYSQL_TYPE_ENUM: 719 case MYSQL_TYPE_VARCHAR: 720 case MYSQL_TYPE_VAR_STRING: 721 case MYSQL_TYPE_STRING: 722 case MYSQL_TYPE_JSON: 723 case MYSQL_TYPE_NEWDECIMAL: 724 case MYSQL_TYPE_DECIMAL: 725 value = MySQLValue(column.name, column.type, signed, &svalue, typeof(svalue).sizeof); 726 break; 727 case MYSQL_TYPE_BIT: 728 case MYSQL_TYPE_TINY_BLOB: 729 case MYSQL_TYPE_MEDIUM_BLOB: 730 case MYSQL_TYPE_LONG_BLOB: 731 case MYSQL_TYPE_BLOB: 732 case MYSQL_TYPE_GEOMETRY: 733 value = MySQLValue(column.name, column.type, signed, &svalue, typeof(svalue).sizeof); 734 break; 735 case MYSQL_TYPE_TIME: 736 case MYSQL_TYPE_TIME2: 737 auto x = parseMySQLTime(svalue); 738 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 739 break; 740 case MYSQL_TYPE_DATE: 741 case MYSQL_TYPE_NEWDATE: 742 case MYSQL_TYPE_DATETIME: 743 case MYSQL_TYPE_DATETIME2: 744 case MYSQL_TYPE_TIMESTAMP: 745 case MYSQL_TYPE_TIMESTAMP2: 746 auto x = parseMySQLDateTime(svalue); 747 value = x.valid() ? MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof) : MySQLValue(column.name, ColumnTypes.MYSQL_TYPE_NULL, signed, null, 0); 748 break; 749 } 750 } 751 752 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) { 753 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TIMESTAMP); 754 packet.put!ubyte(0x80); 755 } 756 757 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) { 758 putMySQLDateTime(packet, MySQLDateTime.from(value)); 759 } 760 761 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Duration)) { 762 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TIME); 763 packet.put!ubyte(0x00); 764 } 765 766 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Duration)) { 767 putMySQLTime(packet, MySQLTime.from(value)); 768 } 769 770 void putValueType(T)(ref OutputPacket packet, T value) if (isIntegral!T || isBoolean!T) { 771 alias UT = Unqual!T; 772 773 enum ubyte sign = isUnsigned!UT ? 0x80 : 0x00; 774 775 static if (is(UT == long) || is(UT == ulong)) { 776 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_LONGLONG); 777 packet.put!ubyte(sign); 778 } else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) { 779 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_LONG); 780 packet.put!ubyte(sign); 781 } else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) { 782 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_SHORT); 783 packet.put!ubyte(sign); 784 } else { 785 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TINY); 786 packet.put!ubyte(sign); 787 } 788 } 789 790 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == typeof(null))) { 791 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_NULL); 792 packet.put!ubyte(0x00); 793 } 794 795 void putValue(T)(ref OutputPacket packet, T value) if (isIntegral!T || isBoolean!T) { 796 alias UT = Unqual!T; 797 798 static if (is(UT == long) || is(UT == ulong)) { 799 packet.put!ulong(value); 800 } else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) { 801 packet.put!uint(value); 802 } else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) { 803 packet.put!ushort(value); 804 } else { 805 packet.put!ubyte(value); 806 } 807 } 808 809 void putValueType(T)(ref OutputPacket packet, T value) if (isSomeString!T) { 810 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_STRING); 811 packet.put!ubyte(0x80); 812 } 813 814 void putValue(T)(ref OutputPacket packet, T value) if (isSomeString!T) { 815 ulong size = value.length * T.init[0].sizeof; 816 packet.putLenEnc(size); 817 packet.put(value); 818 } 819 820 void putValueType(T)(ref OutputPacket packet, T value) if (isArray!T && !isSomeString!T) { 821 foreach(ref item; value) 822 putValueType(packet, item); 823 } 824 825 void putValue(T)(ref OutputPacket packet, T value) if (isArray!T && !isSomeString!T) { 826 foreach(ref item; value) 827 putValue(packet, item); 828 } 829 830 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLBinary)) { 831 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_BLOB); 832 packet.put!ubyte(0x80); 833 } 834 835 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLBinary)) { 836 ulong size = value.length; 837 packet.putLenEnc(size); 838 packet.put(value.data); 839 } 840 841 void putValueType(T)(ref OutputPacket packet, T value) if(is(Unqual!T == MySQLValue)) { 842 packet.put!ubyte(value.type_); 843 packet.put!ubyte(value.sign_); 844 } 845 846 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLValue)) { 847 final switch(value.type) with (ColumnTypes) { 848 case MYSQL_TYPE_NULL: 849 break; 850 case MYSQL_TYPE_TINY: 851 packet.put!ubyte(*cast(ubyte*)value.buffer_.ptr); 852 break; 853 case MYSQL_TYPE_YEAR: 854 case MYSQL_TYPE_SHORT: 855 packet.put!ushort(*cast(ushort*)value.buffer_.ptr); 856 break; 857 case MYSQL_TYPE_INT24: 858 case MYSQL_TYPE_LONG: 859 packet.put!uint(*cast(uint*)value.buffer_.ptr); 860 break; 861 case MYSQL_TYPE_LONGLONG: 862 packet.put!ulong(*cast(ulong*)value.buffer_.ptr); 863 break; 864 case MYSQL_TYPE_DOUBLE: 865 packet.put!double(*cast(double*)value.buffer_.ptr); 866 break; 867 case MYSQL_TYPE_FLOAT: 868 packet.put!double(*cast(float*)value.buffer_.ptr); 869 break; 870 case MYSQL_TYPE_SET: 871 case MYSQL_TYPE_ENUM: 872 case MYSQL_TYPE_VARCHAR: 873 case MYSQL_TYPE_VAR_STRING: 874 case MYSQL_TYPE_STRING: 875 case MYSQL_TYPE_JSON: 876 case MYSQL_TYPE_NEWDECIMAL: 877 case MYSQL_TYPE_DECIMAL: 878 case MYSQL_TYPE_BIT: 879 case MYSQL_TYPE_TINY_BLOB: 880 case MYSQL_TYPE_MEDIUM_BLOB: 881 case MYSQL_TYPE_LONG_BLOB: 882 case MYSQL_TYPE_BLOB: 883 case MYSQL_TYPE_GEOMETRY: 884 packet.putLenEnc((*cast(ubyte[]*)value.buffer_.ptr).length); 885 packet.put(*cast(ubyte[]*)value.buffer_.ptr); 886 break; 887 case MYSQL_TYPE_TIME: 888 case MYSQL_TYPE_TIME2: 889 packet.putMySQLTime(*cast(MySQLTime*)value.buffer_.ptr); 890 break; 891 case MYSQL_TYPE_DATE: 892 case MYSQL_TYPE_NEWDATE: 893 case MYSQL_TYPE_DATETIME: 894 case MYSQL_TYPE_DATETIME2: 895 case MYSQL_TYPE_TIMESTAMP: 896 case MYSQL_TYPE_TIMESTAMP2: 897 packet.putMySQLDateTime(*cast(MySQLDateTime*)value.buffer_.ptr); 898 break; 899 } 900 }