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