1 module database.mysql.type; 2 3 import std.algorithm; 4 import std.array : appender; 5 import std.conv : parse, to; 6 import std.datetime; 7 import std.format: format, formattedWrite; 8 import std.traits; 9 import std.typecons; 10 import std.variant; 11 12 import database.mysql.protocol; 13 import database.mysql.packet; 14 import database.mysql.exception; 15 import database.mysql.row; 16 17 struct IgnoreAttribute {} 18 struct OptionalAttribute {} 19 struct NameAttribute { const(char)[] name; } 20 struct UnCamelCaseAttribute {} 21 struct TableNameAttribute { const(char)[] name; } 22 23 @property TableNameAttribute tableName(const(char)[] name) 24 { 25 return TableNameAttribute(name); 26 } 27 28 @property IgnoreAttribute ignore() 29 { 30 return IgnoreAttribute(); 31 } 32 33 @property OptionalAttribute optional() 34 { 35 return OptionalAttribute(); 36 } 37 38 @property NameAttribute as(const(char)[] name) 39 { 40 return NameAttribute(name); 41 } 42 43 @property UnCamelCaseAttribute uncamel() 44 { 45 return UnCamelCaseAttribute(); 46 } 47 48 template Unnull(U) 49 { 50 alias impl(N : Nullable!T, T) = T; 51 alias impl(T) = T; 52 alias Unnull = impl!U; 53 } 54 55 alias Unboth(T) = Unqual!(Unnull!T); 56 enum isSomeDuration(T) = is(Unboth!T == Date) || is(Unboth!T == DateTime) || is(Unboth!T == SysTime) || is(Unboth!T == Duration) || is(Unboth!T == TimeOfDay); 57 enum isValueType(T) = isSomeDuration!(Unboth!T) || is(Unboth!T == MySQLValue) || (!is(Unboth!T == struct) && !is(Unboth!T == class)); 58 59 template isWritableDataMember(T, string Member) 60 { 61 static if (is(TypeTuple!(__traits(getMember, T, Member)))) 62 { 63 enum isWritableDataMember = false; 64 } 65 else static if (!is(typeof(__traits(getMember, T, Member)))) 66 { 67 enum isWritableDataMember = false; 68 } 69 else static if (is(typeof(__traits(getMember, T, Member)) == void)) 70 { 71 enum isWritableDataMember = false; 72 } 73 else static if (is(typeof(__traits(getMember, T, Member)) == enum)) 74 { 75 enum isWritableDataMember = true; 76 } 77 else static if (hasUDA!(__traits(getMember, T, Member), IgnoreAttribute)) 78 { 79 enum isWritableDataMember = false; 80 } 81 else static if (isArray!(typeof(__traits(getMember, T, Member))) && !is(typeof(typeof(__traits(getMember, T, Member)).init[0]) == ubyte) && !is(typeof(__traits(getMember, T, Member)) == string)) 82 { 83 enum isWritableDataMember = false; 84 } 85 else static if (isAssociativeArray!(typeof(__traits(getMember, T, Member)))) 86 { 87 enum isWritableDataMember = false; 88 } 89 else static if (isSomeFunction!(typeof(__traits(getMember, T, Member)))) 90 { 91 enum isWritableDataMember = false; 92 } 93 else static if (!is(typeof((){ T x = void; __traits(getMember, x, Member) = __traits(getMember, x, Member); }()))) 94 { 95 enum isWritableDataMember = false; 96 } 97 else static if ((__traits(getProtection, __traits(getMember, T, Member)) != "public") && (__traits(getProtection, __traits(getMember, T, Member)) != "export")) 98 { 99 enum isWritableDataMember = false; 100 } 101 else 102 { 103 enum isWritableDataMember = true; 104 } 105 } 106 107 template isReadableDataMember(T, string Member) 108 { 109 static if (is(TypeTuple!(__traits(getMember, T, Member)))) 110 { 111 enum isReadableDataMember = false; 112 } 113 else static if (!is(typeof(__traits(getMember, T, Member)))) 114 { 115 enum isReadableDataMember = false; 116 } 117 else static if (is(typeof(__traits(getMember, T, Member)) == void)) 118 { 119 enum isReadableDataMember = false; 120 } 121 else static if (is(typeof(__traits(getMember, T, Member)) == enum)) 122 { 123 enum isReadableDataMember = true; 124 } 125 else static if (hasUDA!(__traits(getMember, T, Member), IgnoreAttribute)) 126 { 127 enum isReadableDataMember = false; 128 } 129 else static if (isArray!(typeof(__traits(getMember, T, Member))) && !is(typeof(typeof(__traits(getMember, T, Member)).init[0]) == ubyte) && !is(typeof(__traits(getMember, T, Member)) == string)) 130 { 131 enum isReadableDataMember = false; 132 } 133 else static if (isAssociativeArray!(typeof(__traits(getMember, T, Member)))) 134 { 135 enum isReadableDataMember = false; 136 } 137 else static if (isSomeFunction!(typeof(__traits(getMember, T, Member))) /* && return type is valueType*/ ) 138 { 139 enum isReadableDataMember = true; 140 } 141 else static if (!is(typeof((){ T x = void; __traits(getMember, x, Member) = __traits(getMember, x, Member); }()))) 142 { 143 enum isReadableDataMember = false; 144 } 145 else static if ((__traits(getProtection, __traits(getMember, T, Member)) != "public") && (__traits(getProtection, __traits(getMember, T, Member)) != "export")) 146 { 147 enum isReadableDataMember = false; 148 } 149 else 150 { 151 enum isReadableDataMember = true; 152 } 153 } 154 155 struct MySQLRawString 156 { 157 @disable this(); 158 159 this(const(char)[] data) 160 { 161 data_ = data; 162 } 163 164 @property auto length() const 165 { 166 return data_.length; 167 } 168 169 @property auto data() const 170 { 171 return data_; 172 } 173 174 private const(char)[] data_; 175 } 176 177 struct MySQLFragment 178 { 179 @disable this(); 180 181 this(const(char)[] data) 182 { 183 data_ = data; 184 } 185 186 @property auto length() const 187 { 188 return data_.length; 189 } 190 191 @property auto data() const 192 { 193 return data_; 194 } 195 196 private const(char)[] data_; 197 } 198 199 struct MySQLBinary 200 { 201 this(T)(T[] data) 202 { 203 data_ = (cast(ubyte*)data.ptr)[0..typeof(T[].init[0]).sizeof * data.length]; 204 } 205 206 @property auto length() const 207 { 208 return data_.length; 209 } 210 211 @property auto data() const 212 { 213 return data_; 214 } 215 216 private const(ubyte)[] data_; 217 } 218 219 struct MySQLValue 220 { 221 package enum BufferSize = max(ulong.sizeof, (ulong[]).sizeof, MySQLDateTime.sizeof, MySQLTime.sizeof); 222 223 package this(const(char)[] name, ColumnTypes type, bool signed, void* ptr, size_t size) 224 { 225 assert(size <= BufferSize); 226 type_ = type; 227 sign_ = signed ? 0x00 : 0x80; 228 if (type != ColumnTypes.MYSQL_TYPE_NULL) 229 buffer_[0..size] = (cast(ubyte*)ptr)[0..size]; 230 name_ = name; 231 } 232 233 this(T)(T) if (is(Unqual!T == typeof(null))) 234 { 235 type_ = ColumnTypes.MYSQL_TYPE_NULL; 236 sign_ = 0x00; 237 } 238 239 this(T)(T value) if (is(Unqual!T == MySQLValue)) 240 { 241 this = value; 242 } 243 244 this(T)(T value) if (std.traits.isFloatingPoint!T) 245 { 246 alias UT = Unqual!T; 247 248 sign_ = 0x00; 249 static if (is(UT == float)) 250 { 251 type_ = ColumnTypes.MYSQL_TYPE_FLOAT; 252 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 253 } 254 else static if (is(UT == double)) 255 { 256 type_ = ColumnTypes.MYSQL_TYPE_DOUBLE; 257 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 258 } 259 else 260 { 261 type_ = ColumnTypes.MYSQL_TYPE_DOUBLE; 262 auto data = cast(double)value; 263 buffer_[0..typeof(data).sizeof] = (cast(ubyte*)&data)[0..typeof(data).sizeof]; 264 } 265 } 266 267 this(T)(T value) if (isIntegral!T || isBoolean!T) 268 { 269 alias UT = Unqual!T; 270 271 static if (is(UT == long) || is(UT == ulong)) 272 { 273 type_ = ColumnTypes.MYSQL_TYPE_LONGLONG; 274 } 275 else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) 276 { 277 type_ = ColumnTypes.MYSQL_TYPE_LONG; 278 } 279 else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) 280 { 281 type_ = ColumnTypes.MYSQL_TYPE_SHORT; 282 } 283 else 284 { 285 type_ = ColumnTypes.MYSQL_TYPE_TINY; 286 } 287 288 sign_ = isUnsigned!UT ? 0x80 : 0x00; 289 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 290 } 291 292 this(T)(T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) 293 { 294 type_ = ColumnTypes.MYSQL_TYPE_TIMESTAMP; 295 sign_ = 0x00; 296 (*cast(MySQLDateTime*)buffer_) = MySQLDateTime.from(value); 297 } 298 299 this(T)(T value) if (is(Unqual!T == Duration) || is(Unqual!T == TimeOfDay)) 300 { 301 type_ = ColumnTypes.MYSQL_TYPE_TIME; 302 sign_ = 0x00; 303 (*cast(MySQLTime*)buffer_) = MySQLTime.from(value); 304 } 305 306 this(T)(T value) if (isSomeString!(OriginalType!T)) 307 { 308 static assert(typeof(T.init[0]).sizeof == 1, format("Unsupported string type: %s", T.stringof)); 309 310 type_ = ColumnTypes.MYSQL_TYPE_STRING; 311 sign_ = 0x80; 312 313 auto slice = value[0..$]; 314 buffer_.ptr[0..typeof(slice).sizeof] = (cast(ubyte*)&slice)[0..typeof(slice).sizeof]; 315 } 316 317 this(T)(T value) if (is(Unqual!T == MySQLBinary)) 318 { 319 type_ = ColumnTypes.MYSQL_TYPE_BLOB; 320 sign_ = 0x80; 321 buffer_.ptr[0..(ubyte[]).sizeof] = (cast(ubyte*)&value.data_)[0..(ubyte[]).sizeof]; 322 } 323 324 void toString(Appender)(ref Appender app) const 325 { 326 final switch(type_) with (ColumnTypes) 327 { 328 case MYSQL_TYPE_NULL: 329 break; 330 case MYSQL_TYPE_TINY: 331 if (isSigned) formattedWrite(&app, "%d", *cast(ubyte*)buffer_.ptr); 332 else formattedWrite(&app, "%d", *cast(byte*)buffer_.ptr); 333 break; 334 case MYSQL_TYPE_YEAR: 335 case MYSQL_TYPE_SHORT: 336 if (isSigned) formattedWrite(&app, "%d", *cast(short*)buffer_.ptr); 337 else formattedWrite(&app, "%d", *cast(ushort*)buffer_.ptr); 338 break; 339 case MYSQL_TYPE_INT24: 340 case MYSQL_TYPE_LONG: 341 if (isSigned) formattedWrite(&app, "%d", *cast(int*)buffer_.ptr); 342 else formattedWrite(&app, "%d", *cast(uint*)buffer_.ptr); 343 break; 344 case MYSQL_TYPE_LONGLONG: 345 if (isSigned) formattedWrite(&app, "%d", *cast(long*)buffer_.ptr); 346 else formattedWrite(&app, "%d", *cast(ulong*)buffer_.ptr); 347 break; 348 case MYSQL_TYPE_FLOAT: 349 float f = *cast(float*)buffer_.ptr; 350 long l = cast(long)f; 351 f -= l; 352 string str = to!string(l) ~ ((f > 0) ? f.to!string[1 .. $] : ""); 353 app.put(str); 354 //formattedWrite(&app, "%g", *cast(float*)buffer_.ptr); 355 break; 356 case MYSQL_TYPE_DOUBLE: 357 double d = *cast(double*)buffer_.ptr; 358 long l = cast(long)d; 359 d -= l; 360 string str = to!string(l) ~ ((d > 0) ? d.to!string[1 .. $] : ""); 361 app.put(str); 362 //formattedWrite(&app, "%g", *cast(double*)buffer_.ptr); 363 break; 364 case MYSQL_TYPE_SET: 365 case MYSQL_TYPE_ENUM: 366 case MYSQL_TYPE_VARCHAR: 367 case MYSQL_TYPE_VAR_STRING: 368 case MYSQL_TYPE_STRING: 369 case MYSQL_TYPE_JSON: 370 case MYSQL_TYPE_NEWDECIMAL: 371 case MYSQL_TYPE_DECIMAL: 372 case MYSQL_TYPE_TINY_BLOB: 373 case MYSQL_TYPE_MEDIUM_BLOB: 374 case MYSQL_TYPE_LONG_BLOB: 375 case MYSQL_TYPE_BLOB: 376 app.put(*cast(string*)buffer_.ptr); 377 break; 378 case MYSQL_TYPE_BIT: 379 case MYSQL_TYPE_GEOMETRY: 380 formattedWrite(&app, "%s", *cast(ubyte[]*)buffer_.ptr); 381 break; 382 case MYSQL_TYPE_TIME: 383 case MYSQL_TYPE_TIME2: 384 formattedWrite(&app, "%s", (*cast(MySQLTime*)buffer_.ptr).to!Duration()); 385 break; 386 case MYSQL_TYPE_DATE: 387 case MYSQL_TYPE_NEWDATE: 388 case MYSQL_TYPE_DATETIME: 389 case MYSQL_TYPE_DATETIME2: 390 case MYSQL_TYPE_TIMESTAMP: 391 case MYSQL_TYPE_TIMESTAMP2: 392 DateTime dt = (*cast(MySQLDateTime*)buffer_.ptr).to!DateTime(); 393 app.put(dt.date().toISOExtString() ~ " " ~ dt.timeOfDay().toISOExtString()); 394 //formattedWrite(&app, "%s", (*cast(MySQLDateTime*)buffer_.ptr).to!DateTime()); 395 break; 396 } 397 } 398 399 string toString() const 400 { 401 auto app = appender!string; 402 toString(app); 403 return app.data; 404 } 405 406 bool opEquals(MySQLValue other) const 407 { 408 if (isString && other.isString) 409 { 410 return peek!string == other.peek!string; 411 } 412 else if (isScalar && other.isScalar) 413 { 414 if (isFloatingPoint || other.isFloatingPoint) 415 return get!double == other.get!double; 416 if (isSigned || other.isSigned) 417 return get!long == other.get!long; 418 return get!ulong == other.get!ulong; 419 } 420 else if (isTime && other.isTime) 421 { 422 return get!Duration == other.get!Duration; 423 } 424 else if (isTimestamp && other.isTimestamp) 425 { 426 return get!SysTime == other.get!SysTime; 427 } 428 else if (isNull && other.isNull) 429 { 430 return true; 431 } 432 433 return false; 434 } 435 436 T get(T)(lazy T def) const 437 { 438 return !isNull ? get!T : def; 439 } 440 441 T get(T)() const if (isScalarType!T && !is(T == enum)) 442 { 443 switch(type_) with (ColumnTypes) 444 { 445 case MYSQL_TYPE_TINY: 446 return cast(T)(*cast(ubyte*)buffer_.ptr); 447 case MYSQL_TYPE_YEAR: 448 case MYSQL_TYPE_SHORT: 449 return cast(T)(*cast(ushort*)buffer_.ptr); 450 case MYSQL_TYPE_INT24: 451 case MYSQL_TYPE_LONG: 452 return cast(T)(*cast(uint*)buffer_.ptr); 453 case MYSQL_TYPE_LONGLONG: 454 return cast(T)(*cast(ulong*)buffer_.ptr); 455 case MYSQL_TYPE_FLOAT: 456 return cast(T)(*cast(float*)buffer_.ptr); 457 case MYSQL_TYPE_DOUBLE: 458 return cast(T)(*cast(double*)buffer_.ptr); 459 default: 460 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 461 } 462 } 463 464 T get(T)() const if (is(Unqual!T == SysTime) || is(Unqual!T == DateTime) || is(Unqual!T == Date)) 465 { 466 switch(type_) with (ColumnTypes) 467 { 468 case MYSQL_TYPE_DATE: 469 case MYSQL_TYPE_NEWDATE: 470 case MYSQL_TYPE_DATETIME: 471 case MYSQL_TYPE_DATETIME2: 472 case MYSQL_TYPE_TIMESTAMP: 473 case MYSQL_TYPE_TIMESTAMP2: 474 return (*cast(MySQLDateTime*)buffer_.ptr).to!T; 475 default: 476 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 477 } 478 } 479 480 T get(T)() const if (is(Unqual!T == TimeOfDay)) 481 { 482 switch(type_) with (ColumnTypes) 483 { 484 case MYSQL_TYPE_DATE: 485 case MYSQL_TYPE_NEWDATE: 486 case MYSQL_TYPE_DATETIME: 487 case MYSQL_TYPE_DATETIME2: 488 case MYSQL_TYPE_TIMESTAMP: 489 case MYSQL_TYPE_TIMESTAMP2: 490 return (*cast(MySQLDateTime*)buffer_.ptr).to!T; 491 case MYSQL_TYPE_TIME: 492 case MYSQL_TYPE_TIME2: 493 return (*cast(MySQLTime*)buffer_.ptr).to!T; 494 default: 495 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 496 } 497 } 498 499 T get(T)() const if (is(Unqual!T == Duration)) 500 { 501 switch(type_) with (ColumnTypes) 502 { 503 case MYSQL_TYPE_TIME: 504 case MYSQL_TYPE_TIME2: 505 return (*cast(MySQLTime*)buffer_.ptr).to!T; 506 default: 507 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 508 } 509 } 510 511 T get(T)() const if (is(Unqual!T == enum)) 512 { 513 return cast(T)get!(OriginalType!T); 514 } 515 516 T get(T)() const if (isArray!T && !is(T == enum)) 517 { 518 switch(type_) with (ColumnTypes) 519 { 520 case MYSQL_TYPE_SET: 521 case MYSQL_TYPE_ENUM: 522 case MYSQL_TYPE_VARCHAR: 523 case MYSQL_TYPE_VAR_STRING: 524 case MYSQL_TYPE_STRING: 525 case MYSQL_TYPE_JSON: 526 case MYSQL_TYPE_NEWDECIMAL: 527 case MYSQL_TYPE_DECIMAL: 528 return (*cast(T*)buffer_.ptr).dup; 529 case MYSQL_TYPE_BIT: 530 case MYSQL_TYPE_TINY_BLOB: 531 case MYSQL_TYPE_MEDIUM_BLOB: 532 case MYSQL_TYPE_LONG_BLOB: 533 case MYSQL_TYPE_BLOB: 534 case MYSQL_TYPE_GEOMETRY: 535 return (*cast(T*)buffer_.ptr).dup; 536 default: 537 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 538 } 539 } 540 541 T get(T)() const if(isInstanceOf!(Nullable, T)) 542 { 543 if (type_ == ColumnTypes.MYSQL_TYPE_NULL) 544 return T.init; 545 return T(get!(typeof(T.init.get))); 546 } 547 548 T peek(T)(lazy T def) const 549 { 550 return !isNull ? peek!(T) : def; 551 } 552 553 T peek(T)() const if (isScalarType!T) 554 { 555 return get!(T); 556 } 557 558 T peek(T)() const if (is(Unqual!T == SysTime) || is(Unqual!T == DateTime) || is(Unqual!T == Date) || is(Unqual!T == TimeOfDay)) 559 { 560 return get!(T); 561 } 562 563 T peek(T)() const if (is(Unqual!T == Duration)) 564 { 565 return get!(T); 566 } 567 568 T peek(T)() const if (isArray!T) 569 { 570 switch(type_) with (ColumnTypes) 571 { 572 case MYSQL_TYPE_SET: 573 case MYSQL_TYPE_ENUM: 574 case MYSQL_TYPE_VARCHAR: 575 case MYSQL_TYPE_VAR_STRING: 576 case MYSQL_TYPE_STRING: 577 case MYSQL_TYPE_JSON: 578 case MYSQL_TYPE_NEWDECIMAL: 579 case MYSQL_TYPE_DECIMAL: 580 return (*cast(T*)buffer_.ptr); 581 case MYSQL_TYPE_BIT: 582 case MYSQL_TYPE_TINY_BLOB: 583 case MYSQL_TYPE_MEDIUM_BLOB: 584 case MYSQL_TYPE_LONG_BLOB: 585 case MYSQL_TYPE_BLOB: 586 case MYSQL_TYPE_GEOMETRY: 587 return (*cast(T*)buffer_.ptr); 588 default: 589 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 590 } 591 } 592 593 bool isNull() const 594 { 595 return type_ == ColumnTypes.MYSQL_TYPE_NULL; 596 } 597 598 ColumnTypes type() const 599 { 600 return type_; 601 } 602 603 bool isSigned() const 604 { 605 return sign_ == 0x00; 606 } 607 608 bool isString() const 609 { 610 final switch(type_) with (ColumnTypes) 611 { 612 case MYSQL_TYPE_NULL: 613 return false; 614 case MYSQL_TYPE_TINY: 615 case MYSQL_TYPE_YEAR: 616 case MYSQL_TYPE_SHORT: 617 case MYSQL_TYPE_INT24: 618 case MYSQL_TYPE_LONG: 619 case MYSQL_TYPE_LONGLONG: 620 case MYSQL_TYPE_FLOAT: 621 case MYSQL_TYPE_DOUBLE: 622 return false; 623 case MYSQL_TYPE_SET: 624 case MYSQL_TYPE_ENUM: 625 case MYSQL_TYPE_VARCHAR: 626 case MYSQL_TYPE_VAR_STRING: 627 case MYSQL_TYPE_STRING: 628 case MYSQL_TYPE_JSON: 629 case MYSQL_TYPE_NEWDECIMAL: 630 case MYSQL_TYPE_DECIMAL: 631 case MYSQL_TYPE_TINY_BLOB: 632 case MYSQL_TYPE_MEDIUM_BLOB: 633 case MYSQL_TYPE_LONG_BLOB: 634 case MYSQL_TYPE_BLOB: 635 return true; 636 case MYSQL_TYPE_BIT: 637 case MYSQL_TYPE_GEOMETRY: 638 return false; 639 case MYSQL_TYPE_TIME: 640 case MYSQL_TYPE_TIME2: 641 return false; 642 case MYSQL_TYPE_DATE: 643 case MYSQL_TYPE_NEWDATE: 644 case MYSQL_TYPE_DATETIME: 645 case MYSQL_TYPE_DATETIME2: 646 case MYSQL_TYPE_TIMESTAMP: 647 case MYSQL_TYPE_TIMESTAMP2: 648 return false; 649 } 650 } 651 652 bool isScalar() const 653 { 654 final switch(type_) with (ColumnTypes) 655 { 656 case MYSQL_TYPE_NULL: 657 return false; 658 case MYSQL_TYPE_TINY: 659 case MYSQL_TYPE_YEAR: 660 case MYSQL_TYPE_SHORT: 661 case MYSQL_TYPE_INT24: 662 case MYSQL_TYPE_LONG: 663 case MYSQL_TYPE_LONGLONG: 664 case MYSQL_TYPE_FLOAT: 665 case MYSQL_TYPE_DOUBLE: 666 return true; 667 case MYSQL_TYPE_SET: 668 case MYSQL_TYPE_ENUM: 669 case MYSQL_TYPE_VARCHAR: 670 case MYSQL_TYPE_VAR_STRING: 671 case MYSQL_TYPE_STRING: 672 case MYSQL_TYPE_JSON: 673 case MYSQL_TYPE_NEWDECIMAL: 674 case MYSQL_TYPE_DECIMAL: 675 case MYSQL_TYPE_TINY_BLOB: 676 case MYSQL_TYPE_MEDIUM_BLOB: 677 case MYSQL_TYPE_LONG_BLOB: 678 case MYSQL_TYPE_BLOB: 679 return false; 680 case MYSQL_TYPE_BIT: 681 case MYSQL_TYPE_GEOMETRY: 682 return false; 683 case MYSQL_TYPE_TIME: 684 case MYSQL_TYPE_TIME2: 685 return false; 686 case MYSQL_TYPE_DATE: 687 case MYSQL_TYPE_NEWDATE: 688 case MYSQL_TYPE_DATETIME: 689 case MYSQL_TYPE_DATETIME2: 690 case MYSQL_TYPE_TIMESTAMP: 691 case MYSQL_TYPE_TIMESTAMP2: 692 return false; 693 } 694 } 695 696 bool isFloatingPoint() const 697 { 698 final switch(type_) with (ColumnTypes) 699 { 700 case MYSQL_TYPE_NULL: 701 return false; 702 case MYSQL_TYPE_TINY: 703 case MYSQL_TYPE_YEAR: 704 case MYSQL_TYPE_SHORT: 705 case MYSQL_TYPE_INT24: 706 case MYSQL_TYPE_LONG: 707 case MYSQL_TYPE_LONGLONG: 708 return false; 709 case MYSQL_TYPE_FLOAT: 710 case MYSQL_TYPE_DOUBLE: 711 return true; 712 case MYSQL_TYPE_SET: 713 case MYSQL_TYPE_ENUM: 714 case MYSQL_TYPE_VARCHAR: 715 case MYSQL_TYPE_VAR_STRING: 716 case MYSQL_TYPE_STRING: 717 case MYSQL_TYPE_JSON: 718 case MYSQL_TYPE_NEWDECIMAL: 719 case MYSQL_TYPE_DECIMAL: 720 case MYSQL_TYPE_TINY_BLOB: 721 case MYSQL_TYPE_MEDIUM_BLOB: 722 case MYSQL_TYPE_LONG_BLOB: 723 case MYSQL_TYPE_BLOB: 724 return false; 725 case MYSQL_TYPE_BIT: 726 case MYSQL_TYPE_GEOMETRY: 727 return false; 728 case MYSQL_TYPE_TIME: 729 case MYSQL_TYPE_TIME2: 730 return false; 731 case MYSQL_TYPE_DATE: 732 case MYSQL_TYPE_NEWDATE: 733 case MYSQL_TYPE_DATETIME: 734 case MYSQL_TYPE_DATETIME2: 735 case MYSQL_TYPE_TIMESTAMP: 736 case MYSQL_TYPE_TIMESTAMP2: 737 return false; 738 } 739 } 740 741 bool isTime() const 742 { 743 final switch(type_) with (ColumnTypes) 744 { 745 case MYSQL_TYPE_NULL: 746 return false; 747 case MYSQL_TYPE_TINY: 748 case MYSQL_TYPE_YEAR: 749 case MYSQL_TYPE_SHORT: 750 case MYSQL_TYPE_INT24: 751 case MYSQL_TYPE_LONG: 752 case MYSQL_TYPE_LONGLONG: 753 case MYSQL_TYPE_FLOAT: 754 case MYSQL_TYPE_DOUBLE: 755 return false; 756 case MYSQL_TYPE_SET: 757 case MYSQL_TYPE_ENUM: 758 case MYSQL_TYPE_VARCHAR: 759 case MYSQL_TYPE_VAR_STRING: 760 case MYSQL_TYPE_STRING: 761 case MYSQL_TYPE_JSON: 762 case MYSQL_TYPE_NEWDECIMAL: 763 case MYSQL_TYPE_DECIMAL: 764 case MYSQL_TYPE_TINY_BLOB: 765 case MYSQL_TYPE_MEDIUM_BLOB: 766 case MYSQL_TYPE_LONG_BLOB: 767 case MYSQL_TYPE_BLOB: 768 return false; 769 case MYSQL_TYPE_BIT: 770 case MYSQL_TYPE_GEOMETRY: 771 return false; 772 case MYSQL_TYPE_TIME: 773 case MYSQL_TYPE_TIME2: 774 return true; 775 case MYSQL_TYPE_DATE: 776 case MYSQL_TYPE_NEWDATE: 777 case MYSQL_TYPE_DATETIME: 778 case MYSQL_TYPE_DATETIME2: 779 case MYSQL_TYPE_TIMESTAMP: 780 case MYSQL_TYPE_TIMESTAMP2: 781 return false; 782 } 783 } 784 785 alias isDuration = isTime; 786 787 bool isDateTime() const { 788 final switch(type_) with (ColumnTypes) 789 { 790 case MYSQL_TYPE_NULL: 791 return false; 792 case MYSQL_TYPE_TINY: 793 case MYSQL_TYPE_YEAR: 794 case MYSQL_TYPE_SHORT: 795 case MYSQL_TYPE_INT24: 796 case MYSQL_TYPE_LONG: 797 case MYSQL_TYPE_LONGLONG: 798 case MYSQL_TYPE_FLOAT: 799 case MYSQL_TYPE_DOUBLE: 800 return false; 801 case MYSQL_TYPE_SET: 802 case MYSQL_TYPE_ENUM: 803 case MYSQL_TYPE_VARCHAR: 804 case MYSQL_TYPE_VAR_STRING: 805 case MYSQL_TYPE_STRING: 806 case MYSQL_TYPE_JSON: 807 case MYSQL_TYPE_NEWDECIMAL: 808 case MYSQL_TYPE_DECIMAL: 809 case MYSQL_TYPE_TINY_BLOB: 810 case MYSQL_TYPE_MEDIUM_BLOB: 811 case MYSQL_TYPE_LONG_BLOB: 812 case MYSQL_TYPE_BLOB: 813 return false; 814 case MYSQL_TYPE_BIT: 815 case MYSQL_TYPE_GEOMETRY: 816 return false; 817 case MYSQL_TYPE_TIME: 818 case MYSQL_TYPE_TIME2: 819 return false; 820 case MYSQL_TYPE_DATE: 821 case MYSQL_TYPE_NEWDATE: 822 case MYSQL_TYPE_DATETIME: 823 case MYSQL_TYPE_DATETIME2: 824 case MYSQL_TYPE_TIMESTAMP: 825 case MYSQL_TYPE_TIMESTAMP2: 826 return true; 827 } 828 } 829 830 alias isTimestamp = isDateTime; 831 832 private: 833 834 ColumnTypes type_ = ColumnTypes.MYSQL_TYPE_NULL; 835 ubyte sign_; 836 ubyte[6] pad_; 837 ubyte[BufferSize] buffer_; 838 const(char)[] name_; 839 } 840 841 struct MySQLColumn 842 { 843 uint length; 844 ushort flags; 845 ubyte decimals; 846 ColumnTypes type; 847 const(char)[] name; 848 } 849 850 alias MySQLHeader = MySQLColumn[]; 851 852 struct MySQLTime 853 { 854 uint days; 855 ubyte negative; 856 ubyte hours; 857 ubyte mins; 858 ubyte secs; 859 uint usecs; 860 861 auto to(T)() const if (is(Unqual!T == Duration)) 862 { 863 auto total = days * 86400_000_000L + 864 hours * 3600_000_000L + 865 mins * 60_000_000L + 866 secs * 1_000_000L + 867 usecs; 868 return cast(T)dur!"usecs"(negative ? -total : total); 869 } 870 871 auto to(T)() const if (is(Unqual!T == TimeOfDay)) 872 { 873 return cast(T)TimeOfDay(hours, mins, secs); 874 } 875 876 static MySQLTime from(Duration duration) 877 { 878 MySQLTime time; 879 duration.abs.split!("days", "hours", "minutes", "seconds", "usecs")(time.days, time.hours, time.mins, time.secs, time.usecs); 880 time.negative = duration.isNegative ? 1 : 0; 881 return time; 882 } 883 884 static MySQLTime from(TimeOfDay tod) 885 { 886 MySQLTime time; 887 time.hours = tod.hour; 888 time.mins = tod.minute; 889 time.secs = tod.second; 890 return time; 891 } 892 } 893 894 void putMySQLTime(ref OutputPacket packet, in MySQLTime time) 895 { 896 if (time.days || time.hours || time.mins || time.mins || time.usecs) 897 { 898 auto usecs = time.usecs != 0; 899 packet.put!ubyte(usecs ? 12 : 8); 900 packet.put!ubyte(time.negative); 901 packet.put!uint(time.days); 902 packet.put!ubyte(time.hours); 903 packet.put!ubyte(time.mins); 904 packet.put!ubyte(time.secs); 905 if (usecs) 906 packet.put!uint(time.usecs); 907 } 908 else 909 { 910 packet.put!ubyte(0); 911 } 912 } 913 914 auto eatMySQLTime(ref InputPacket packet) 915 { 916 MySQLTime time; 917 switch(packet.eat!ubyte) 918 { 919 case 12: 920 time.negative = packet.eat!ubyte; 921 time.days = packet.eat!uint; 922 time.hours = packet.eat!ubyte; 923 time.mins = packet.eat!ubyte; 924 time.secs = packet.eat!ubyte; 925 time.usecs = packet.eat!uint; 926 break; 927 case 8: 928 time.negative = packet.eat!ubyte; 929 time.days = packet.eat!uint; 930 time.hours = packet.eat!ubyte; 931 time.mins = packet.eat!ubyte; 932 time.secs = packet.eat!ubyte; 933 break; 934 case 0: 935 break; 936 default: 937 throw new MySQLProtocolException("Bad time struct format"); 938 } 939 940 return time; 941 } 942 943 struct MySQLDateTime 944 { 945 ushort year; 946 ubyte month; 947 ubyte day; 948 ubyte hour; 949 ubyte min; 950 ubyte sec; 951 uint usec; 952 953 bool valid() const 954 { 955 return month != 0; 956 } 957 958 T to(T)() const if (is(Unqual!T == SysTime)) 959 { 960 assert(valid()); 961 return cast(T)SysTime(DateTime(year, month, day, hour, min, sec), usec.dur!"usecs", UTC()); 962 } 963 964 T to(T)() const if (is(Unqual!T == DateTime)) 965 { 966 assert(valid()); 967 return cast(T)DateTime(year, month, day, hour, min, sec); 968 } 969 970 T to(T)() const if (is(T == Date)) 971 { 972 assert(valid()); 973 return cast(T)Date(year, month, day); 974 } 975 976 T to(T)() const if (is(Unqual!T == TimeOfDay)) 977 { 978 return cast(T)TimeOfDay(hour, min, sec); 979 } 980 981 static MySQLDateTime from(SysTime sysTime) 982 { 983 MySQLDateTime time; 984 985 auto dateTime = cast(DateTime)sysTime; 986 time.year = dateTime.year; 987 time.month = dateTime.month; 988 time.day = dateTime.day; 989 time.hour = dateTime.hour; 990 time.min = dateTime.minute; 991 time.sec = dateTime.second; 992 time.usec = cast(int)sysTime.fracSecs.total!"usecs"; 993 994 return time; 995 } 996 997 static MySQLDateTime from(DateTime dateTime) 998 { 999 MySQLDateTime time; 1000 1001 time.year = dateTime.year; 1002 time.month = dateTime.month; 1003 time.day = dateTime.day; 1004 time.hour = dateTime.hour; 1005 time.min = dateTime.minute; 1006 time.sec = dateTime.second; 1007 1008 return time; 1009 } 1010 1011 static MySQLDateTime from(Date date) 1012 { 1013 MySQLDateTime time; 1014 1015 time.year = date.year; 1016 time.month = date.month; 1017 time.day = date.day; 1018 1019 return time; 1020 } 1021 } 1022 1023 void putMySQLDateTime(ref OutputPacket packet, in MySQLDateTime time) 1024 { 1025 auto marker = packet.marker!ubyte; 1026 ubyte length; 1027 1028 if (time.year || time.month || time.day) 1029 { 1030 length = 4; 1031 packet.put!ushort(time.year); 1032 packet.put!ubyte(time.month); 1033 packet.put!ubyte(time.day); 1034 1035 if (time.hour || time.min || time.sec || time.usec) 1036 { 1037 length = 7; 1038 packet.put!ubyte(time.hour); 1039 packet.put!ubyte(time.min); 1040 packet.put!ubyte(time.sec); 1041 1042 if (time.usec) 1043 { 1044 length = 11; 1045 packet.put!uint(time.usec); 1046 } 1047 } 1048 } 1049 1050 packet.put!ubyte(marker, length); 1051 } 1052 1053 auto eatMySQLDateTime(ref InputPacket packet) 1054 { 1055 MySQLDateTime time; 1056 switch(packet.eat!ubyte) 1057 { 1058 case 11: 1059 time.year = packet.eat!ushort; 1060 time.month = packet.eat!ubyte; 1061 time.day = packet.eat!ubyte; 1062 time.hour = packet.eat!ubyte; 1063 time.min = packet.eat!ubyte; 1064 time.sec = packet.eat!ubyte; 1065 time.usec = packet.eat!uint; 1066 break; 1067 case 7: 1068 time.year = packet.eat!ushort; 1069 time.month = packet.eat!ubyte; 1070 time.day = packet.eat!ubyte; 1071 time.hour = packet.eat!ubyte; 1072 time.min = packet.eat!ubyte; 1073 time.sec = packet.eat!ubyte; 1074 break; 1075 case 4: 1076 time.year = packet.eat!ushort; 1077 time.month = packet.eat!ubyte; 1078 time.day = packet.eat!ubyte; 1079 break; 1080 case 0: 1081 break; 1082 default: 1083 throw new MySQLProtocolException("Bad datetime struct format"); 1084 } 1085 1086 return time; 1087 } 1088 1089 private void skip(ref const(char)[] x, char ch) 1090 { 1091 if (x.length && (x.ptr[0] == ch)) 1092 { 1093 x = x[1..$]; 1094 } 1095 else 1096 { 1097 throw new MySQLProtocolException("Bad datetime string format"); 1098 } 1099 } 1100 1101 auto parseMySQLTime(const(char)[] x) 1102 { 1103 MySQLTime time; 1104 1105 auto hours = x.parse!int; 1106 if (hours < 0) 1107 { 1108 time.negative = 1; 1109 hours = -hours; 1110 } 1111 time.days = hours / 24; 1112 time.hours = cast(ubyte)(hours % 24); 1113 x.skip(':'); 1114 time.mins = x.parse!ubyte; 1115 x.skip(':'); 1116 time.secs = x.parse!ubyte; 1117 if (x.length) 1118 { 1119 x.skip('.'); 1120 time.usecs = x.parse!uint; 1121 switch (6 - max(6, x.length)) 1122 { 1123 case 0: break; 1124 case 1: time.usecs *= 10; break; 1125 case 2: time.usecs *= 100; break; 1126 case 3: time.usecs *= 1_000; break; 1127 case 4: time.usecs *= 10_000; break; 1128 case 5: time.usecs *= 100_000; break; 1129 default: assert("Bad datetime string format"); break; 1130 } 1131 } 1132 1133 return time; 1134 } 1135 1136 auto parseMySQLDateTime(const(char)[] x) 1137 { 1138 MySQLDateTime time; 1139 1140 time.year = x.parse!ushort; 1141 x.skip('-'); 1142 time.month = x.parse!ubyte; 1143 x.skip('-'); 1144 time.day = x.parse!ubyte; 1145 if (x.length) 1146 { 1147 x.skip(' '); 1148 time.hour = x.parse!ubyte; 1149 x.skip(':'); 1150 time.min = x.parse!ubyte; 1151 x.skip(':'); 1152 time.sec = x.parse!ubyte; 1153 1154 if (x.length) 1155 { 1156 x.skip('.'); 1157 time.usec = x.parse!uint; 1158 switch (6 - max(6, x.length)) 1159 { 1160 case 0: break; 1161 case 1: time.usec *= 10; break; 1162 case 2: time.usec *= 100; break; 1163 case 3: time.usec *= 1_000; break; 1164 case 4: time.usec *= 10_000; break; 1165 case 5: time.usec *= 100_000; break; 1166 default: assert("Bad datetime string format"); break; 1167 } 1168 } 1169 } 1170 1171 return time; 1172 } 1173 1174 void eatValue(ref InputPacket packet, ref const MySQLColumn column, ref MySQLValue value) 1175 { 1176 auto signed = (column.flags & FieldFlags.UNSIGNED_FLAG) == 0; 1177 final switch(column.type) with (ColumnTypes) 1178 { 1179 case MYSQL_TYPE_NULL: 1180 value = MySQLValue(column.name, column.type, signed, null, 0); 1181 break; 1182 case MYSQL_TYPE_TINY: 1183 auto x = packet.eat!ubyte; 1184 value = MySQLValue(column.name, column.type, signed, &x, 1); 1185 break; 1186 case MYSQL_TYPE_YEAR: 1187 case MYSQL_TYPE_SHORT: 1188 auto x = packet.eat!ushort; 1189 value = MySQLValue(column.name, column.type, signed, &x, 2); 1190 break; 1191 case MYSQL_TYPE_INT24: 1192 case MYSQL_TYPE_LONG: 1193 auto x = packet.eat!uint; 1194 value = MySQLValue(column.name, column.type, signed, &x, 4); 1195 break; 1196 case MYSQL_TYPE_DOUBLE: 1197 case MYSQL_TYPE_LONGLONG: 1198 auto x = packet.eat!ulong; 1199 value = MySQLValue(column.name, column.type, signed, &x, 8); 1200 break; 1201 case MYSQL_TYPE_FLOAT: 1202 auto x = packet.eat!float; 1203 value = MySQLValue(column.name, column.type, signed, &x, 4); 1204 break; 1205 case MYSQL_TYPE_SET: 1206 case MYSQL_TYPE_ENUM: 1207 case MYSQL_TYPE_VARCHAR: 1208 case MYSQL_TYPE_VAR_STRING: 1209 case MYSQL_TYPE_STRING: 1210 case MYSQL_TYPE_JSON: 1211 case MYSQL_TYPE_NEWDECIMAL: 1212 case MYSQL_TYPE_DECIMAL: 1213 auto x = packet.eat!(const(char)[])(cast(size_t)packet.eatLenEnc()); 1214 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1215 break; 1216 case MYSQL_TYPE_BIT: 1217 case MYSQL_TYPE_TINY_BLOB: 1218 case MYSQL_TYPE_MEDIUM_BLOB: 1219 case MYSQL_TYPE_LONG_BLOB: 1220 case MYSQL_TYPE_BLOB: 1221 case MYSQL_TYPE_GEOMETRY: 1222 auto x = packet.eat!(const(ubyte)[])(cast(size_t)packet.eatLenEnc()); 1223 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1224 break; 1225 case MYSQL_TYPE_TIME: 1226 case MYSQL_TYPE_TIME2: 1227 auto x = eatMySQLTime(packet); 1228 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1229 break; 1230 case MYSQL_TYPE_DATE: 1231 case MYSQL_TYPE_NEWDATE: 1232 case MYSQL_TYPE_DATETIME: 1233 case MYSQL_TYPE_DATETIME2: 1234 case MYSQL_TYPE_TIMESTAMP: 1235 case MYSQL_TYPE_TIMESTAMP2: 1236 auto x = eatMySQLDateTime(packet); 1237 value = x.valid() ? MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof) : MySQLValue(column.name, ColumnTypes.MYSQL_TYPE_NULL, signed, null, 0); 1238 break; 1239 } 1240 } 1241 1242 void eatValueText(ref InputPacket packet, ref const MySQLColumn column, ref MySQLValue value) 1243 { 1244 auto signed = (column.flags & FieldFlags.UNSIGNED_FLAG) == 0; 1245 auto svalue = (column.type != ColumnTypes.MYSQL_TYPE_NULL) ? cast(string)(packet.eat!(const(char)[])(cast(size_t)packet.eatLenEnc())) : string.init; 1246 final switch(column.type) with (ColumnTypes) 1247 { 1248 case MYSQL_TYPE_NULL: 1249 value = MySQLValue(column.name, column.type, signed, null, 0); 1250 break; 1251 case MYSQL_TYPE_TINY: 1252 auto x = (svalue.ptr[0] == '-') ? cast(ubyte)(-1 * svalue[1..$].to!byte) : svalue.to!ubyte; 1253 value = MySQLValue(column.name, column.type, signed, &x, 1); 1254 break; 1255 case MYSQL_TYPE_YEAR: 1256 case MYSQL_TYPE_SHORT: 1257 auto x = (svalue.ptr[0] == '-') ? cast(ushort)(-1 * svalue[1..$].to!short) : svalue.to!ushort; 1258 value = MySQLValue(column.name, column.type, signed, &x, 2); 1259 break; 1260 case MYSQL_TYPE_INT24: 1261 case MYSQL_TYPE_LONG: 1262 auto x = (svalue.ptr[0] == '-') ? cast(uint)(-svalue[1..$].to!int) : svalue.to!uint; 1263 value = MySQLValue(column.name, column.type, signed, &x, 4); 1264 break; 1265 case MYSQL_TYPE_LONGLONG: 1266 auto x = (svalue.ptr[0] == '-') ? cast(ulong)(-svalue[1..$].to!long) : svalue.to!ulong; 1267 value = MySQLValue(column.name, column.type, signed, &x, 8); 1268 break; 1269 case MYSQL_TYPE_DOUBLE: 1270 auto x = svalue.to!double; 1271 value = MySQLValue(column.name, column.type, signed, &x, 8); 1272 break; 1273 case MYSQL_TYPE_FLOAT: 1274 auto x = svalue.to!float; 1275 value = MySQLValue(column.name, column.type, signed, &x, 4); 1276 break; 1277 case MYSQL_TYPE_SET: 1278 case MYSQL_TYPE_ENUM: 1279 case MYSQL_TYPE_VARCHAR: 1280 case MYSQL_TYPE_VAR_STRING: 1281 case MYSQL_TYPE_STRING: 1282 case MYSQL_TYPE_JSON: 1283 case MYSQL_TYPE_NEWDECIMAL: 1284 case MYSQL_TYPE_DECIMAL: 1285 value = MySQLValue(column.name, column.type, signed, &svalue, typeof(svalue).sizeof); 1286 break; 1287 case MYSQL_TYPE_BIT: 1288 case MYSQL_TYPE_TINY_BLOB: 1289 case MYSQL_TYPE_MEDIUM_BLOB: 1290 case MYSQL_TYPE_LONG_BLOB: 1291 case MYSQL_TYPE_BLOB: 1292 case MYSQL_TYPE_GEOMETRY: 1293 value = MySQLValue(column.name, column.type, signed, &svalue, typeof(svalue).sizeof); 1294 break; 1295 case MYSQL_TYPE_TIME: 1296 case MYSQL_TYPE_TIME2: 1297 auto x = parseMySQLTime(svalue); 1298 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1299 break; 1300 case MYSQL_TYPE_DATE: 1301 case MYSQL_TYPE_NEWDATE: 1302 case MYSQL_TYPE_DATETIME: 1303 case MYSQL_TYPE_DATETIME2: 1304 case MYSQL_TYPE_TIMESTAMP: 1305 case MYSQL_TYPE_TIMESTAMP2: 1306 auto x = parseMySQLDateTime(svalue); 1307 value = x.valid() ? MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof) : MySQLValue(column.name, ColumnTypes.MYSQL_TYPE_NULL, signed, null, 0); 1308 break; 1309 } 1310 } 1311 1312 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Variant)) 1313 { 1314 if (!value.hasValue) 1315 { 1316 putValueType(packet, MySQLValue(null)); 1317 } 1318 else if (value.type == typeid(string)) 1319 { 1320 putValueType(packet, value.get!string); 1321 } 1322 else if (value.type == typeid(dstring)) 1323 { 1324 putValueType(packet, value.get!dstring); 1325 } 1326 else if (value.type == typeid(wstring)) 1327 { 1328 putValueType(packet, value.get!wstring); 1329 } 1330 else if (value.type == typeid(short)) 1331 { 1332 putValueType(packet, value.get!short); 1333 } 1334 else if (value.type == typeid(int)) 1335 { 1336 putValueType(packet, value.get!int); 1337 } 1338 else if (value.type == typeid(long)) 1339 { 1340 putValueType(packet, value.get!long); 1341 } 1342 else if (value.type == typeid(ushort)) 1343 { 1344 putValueType(packet, value.get!ushort); 1345 } 1346 else if (value.type == typeid(uint)) 1347 { 1348 putValueType(packet, value.get!uint); 1349 } 1350 else if (value.type == typeid(ulong)) 1351 { 1352 putValueType(packet, value.get!ulong); 1353 } 1354 else if (value.type == typeid(float)) 1355 { 1356 putValueType(packet, value.get!float); 1357 } 1358 else if (value.type == typeid(double)) 1359 { 1360 putValueType(packet, value.get!double); 1361 } 1362 else if (value.type == typeid(byte)) 1363 { 1364 putValueType(packet, value.get!byte); 1365 } 1366 else if (value.type == typeid(ubyte)) 1367 { 1368 putValueType(packet, value.get!ubyte); 1369 } 1370 else if (value.type == typeid(bool)) 1371 { 1372 putValueType(packet, value.get!bool); 1373 } 1374 else if (value.type == typeid(Date)) 1375 { 1376 putValueType(packet, value.get!Date); 1377 } 1378 else if (value.type == typeid(DateTime)) 1379 { 1380 putValueType(packet, value.get!DateTime); 1381 } 1382 else if (value.type == typeid(SysTime)) 1383 { 1384 putValueType(packet, value.get!SysTime); 1385 } 1386 else if (value.type == typeid(Duration)) 1387 { 1388 putValueType(packet, value.get!Duration); 1389 } 1390 else if (value.type == typeid(MySQLBinary)) 1391 { 1392 putValueType(packet, value.get!MySQLBinary); 1393 } 1394 else if (value.type == typeid(MySQLValue)) 1395 { 1396 putValueType(packet, value.get!MySQLValue); 1397 } 1398 else 1399 { 1400 throw new Exception("exists unkown type at Variant[]: " ~ value.type.toString()); 1401 } 1402 } 1403 1404 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Variant)) 1405 { 1406 if (!value.hasValue) 1407 { 1408 putValue(packet, MySQLValue(null)); 1409 } 1410 else if (value.type == typeid(string)) 1411 { 1412 putValue(packet, value.get!string); 1413 } 1414 else if (value.type == typeid(dstring)) 1415 { 1416 putValue(packet, value.get!dstring); 1417 } 1418 else if (value.type == typeid(wstring)) 1419 { 1420 putValue(packet, value.get!wstring); 1421 } 1422 else if (value.type == typeid(short)) 1423 { 1424 putValue(packet, value.get!short); 1425 } 1426 else if (value.type == typeid(int)) 1427 { 1428 putValue(packet, value.get!int); 1429 } 1430 else if (value.type == typeid(long)) 1431 { 1432 putValue(packet, value.get!long); 1433 } 1434 else if (value.type == typeid(ushort)) 1435 { 1436 putValue(packet, value.get!ushort); 1437 } 1438 else if (value.type == typeid(uint)){ 1439 putValue(packet, value.get!uint); 1440 } 1441 else if (value.type == typeid(ulong)) 1442 { 1443 putValue(packet, value.get!ulong); 1444 } 1445 else if (value.type == typeid(float)) 1446 { 1447 putValue(packet, value.get!float); 1448 } 1449 else if (value.type == typeid(double)) 1450 { 1451 putValue(packet, value.get!double); 1452 } 1453 else if (value.type == typeid(byte)) 1454 { 1455 putValue(packet, value.get!byte); 1456 } 1457 else if (value.type == typeid(ubyte)) 1458 { 1459 putValue(packet, value.get!ubyte); 1460 } 1461 else if (value.type == typeid(bool)) 1462 { 1463 putValue(packet, value.get!bool); 1464 } 1465 else if (value.type == typeid(Date)) 1466 { 1467 putValue(packet, value.get!Date); 1468 } 1469 else if (value.type == typeid(DateTime)) 1470 { 1471 putValue(packet, value.get!DateTime); 1472 } 1473 else if (value.type == typeid(SysTime)) 1474 { 1475 putValue(packet, value.get!SysTime); 1476 } 1477 else if (value.type == typeid(Duration)) 1478 { 1479 putValue(packet, value.get!Duration); 1480 } 1481 else if (value.type == typeid(MySQLBinary)) 1482 { 1483 putValue(packet, value.get!MySQLBinary); 1484 } 1485 else if (value.type == typeid(MySQLValue)) 1486 { 1487 putValue(packet, value.get!MySQLValue); 1488 } 1489 else 1490 { 1491 throw new Exception("exists unkown type at Variant[]: " ~ value.type.toString()); 1492 } 1493 } 1494 1495 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) 1496 { 1497 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TIMESTAMP); 1498 packet.put!ubyte(0x80); 1499 } 1500 1501 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) 1502 { 1503 putMySQLDateTime(packet, MySQLDateTime.from(value)); 1504 } 1505 1506 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Duration)) 1507 { 1508 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TIME); 1509 packet.put!ubyte(0x00); 1510 } 1511 1512 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Duration)) 1513 { 1514 putMySQLTime(packet, MySQLTime.from(value)); 1515 } 1516 1517 void putValueType(T)(ref OutputPacket packet, T value) if (isIntegral!T || isBoolean!T) 1518 { 1519 alias UT = Unqual!T; 1520 1521 enum ubyte sign = isUnsigned!UT ? 0x80 : 0x00; 1522 1523 static if (is(UT == long) || is(UT == ulong)) 1524 { 1525 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_LONGLONG); 1526 packet.put!ubyte(sign); 1527 } 1528 else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) 1529 { 1530 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_LONG); 1531 packet.put!ubyte(sign); 1532 } 1533 else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) 1534 { 1535 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_SHORT); 1536 packet.put!ubyte(sign); 1537 } 1538 else { 1539 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TINY); 1540 packet.put!ubyte(sign); 1541 } 1542 } 1543 1544 void putValue(T)(ref OutputPacket packet, T value) if (isIntegral!T || isBoolean!T) 1545 { 1546 alias UT = Unqual!T; 1547 1548 static if (is(UT == long) || is(UT == ulong)) 1549 { 1550 packet.put!ulong(value); 1551 } 1552 else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) 1553 { 1554 packet.put!uint(value); 1555 } 1556 else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) 1557 { 1558 packet.put!ushort(value); 1559 } 1560 else 1561 { 1562 packet.put!ubyte(value); 1563 } 1564 } 1565 1566 void putValueType(T)(ref OutputPacket packet, T value) if (isFloatingPoint!T) 1567 { 1568 alias UT = Unqual!T; 1569 1570 enum ubyte sign = 0x00; 1571 1572 static if (is(UT == float)) 1573 { 1574 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_FLOAT); 1575 packet.put!ubyte(sign); 1576 } 1577 else 1578 { 1579 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_DOUBLE); 1580 packet.put!ubyte(sign); 1581 } 1582 } 1583 1584 void putValue(T)(ref OutputPacket packet, T value) if (isFloatingPoint!T) 1585 { 1586 alias UT = Unqual!T; 1587 1588 static if (is(UT == float)) 1589 { 1590 packet.put!float(value); 1591 } 1592 else 1593 { 1594 packet.put!double(cast(double)value); 1595 } 1596 } 1597 1598 void putValueType(T)(ref OutputPacket packet, T value) if (isSomeString!(OriginalType!T)) 1599 { 1600 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_STRING); 1601 packet.put!ubyte(0x80); 1602 } 1603 1604 void putValue(T)(ref OutputPacket packet, T value) if (isSomeString!(OriginalType!T)) 1605 { 1606 ulong size = value.length * T.init[0].sizeof; 1607 packet.putLenEnc(size); 1608 packet.put(value); 1609 } 1610 1611 void putValueType(T)(ref OutputPacket packet, T value) if (isArray!T && !isSomeString!(OriginalType!T)) 1612 { 1613 foreach(ref item; value) 1614 putValueType(packet, item); 1615 } 1616 1617 void putValue(T)(ref OutputPacket packet, T value) if (isArray!T && !isSomeString!(OriginalType!T)) 1618 { 1619 foreach(ref item; value) 1620 putValue(packet, item); 1621 } 1622 1623 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLBinary)) 1624 { 1625 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_BLOB); 1626 packet.put!ubyte(0x80); 1627 } 1628 1629 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLBinary)) 1630 { 1631 ulong size = value.length; 1632 packet.putLenEnc(size); 1633 packet.put(value.data); 1634 } 1635 1636 void putValueType(T)(ref OutputPacket packet, T value) if(is(Unqual!T == MySQLValue)) 1637 { 1638 packet.put!ubyte(value.type_); 1639 packet.put!ubyte(value.sign_); 1640 } 1641 1642 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLValue)) 1643 { 1644 final switch(value.type) with (ColumnTypes) 1645 { 1646 case MYSQL_TYPE_NULL: 1647 break; 1648 case MYSQL_TYPE_TINY: 1649 packet.put!ubyte(*cast(ubyte*)value.buffer_.ptr); 1650 break; 1651 case MYSQL_TYPE_YEAR: 1652 case MYSQL_TYPE_SHORT: 1653 packet.put!ushort(*cast(ushort*)value.buffer_.ptr); 1654 break; 1655 case MYSQL_TYPE_INT24: 1656 case MYSQL_TYPE_LONG: 1657 packet.put!uint(*cast(uint*)value.buffer_.ptr); 1658 break; 1659 case MYSQL_TYPE_LONGLONG: 1660 packet.put!ulong(*cast(ulong*)value.buffer_.ptr); 1661 break; 1662 case MYSQL_TYPE_DOUBLE: 1663 packet.put!double(*cast(double*)value.buffer_.ptr); 1664 break; 1665 case MYSQL_TYPE_FLOAT: 1666 packet.put!float(*cast(float*)value.buffer_.ptr); 1667 break; 1668 case MYSQL_TYPE_SET: 1669 case MYSQL_TYPE_ENUM: 1670 case MYSQL_TYPE_VARCHAR: 1671 case MYSQL_TYPE_VAR_STRING: 1672 case MYSQL_TYPE_STRING: 1673 case MYSQL_TYPE_JSON: 1674 case MYSQL_TYPE_NEWDECIMAL: 1675 case MYSQL_TYPE_DECIMAL: 1676 case MYSQL_TYPE_BIT: 1677 case MYSQL_TYPE_TINY_BLOB: 1678 case MYSQL_TYPE_MEDIUM_BLOB: 1679 case MYSQL_TYPE_LONG_BLOB: 1680 case MYSQL_TYPE_BLOB: 1681 case MYSQL_TYPE_GEOMETRY: 1682 packet.putLenEnc((*cast(ubyte[]*)value.buffer_.ptr).length); 1683 packet.put(*cast(ubyte[]*)value.buffer_.ptr); 1684 break; 1685 case MYSQL_TYPE_TIME: 1686 case MYSQL_TYPE_TIME2: 1687 packet.putMySQLTime(*cast(MySQLTime*)value.buffer_.ptr); 1688 break; 1689 case MYSQL_TYPE_DATE: 1690 case MYSQL_TYPE_NEWDATE: 1691 case MYSQL_TYPE_DATETIME: 1692 case MYSQL_TYPE_DATETIME2: 1693 case MYSQL_TYPE_TIMESTAMP: 1694 case MYSQL_TYPE_TIMESTAMP2: 1695 packet.putMySQLDateTime(*cast(MySQLDateTime*)value.buffer_.ptr); 1696 break; 1697 } 1698 } 1699 1700 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == typeof(null))) 1701 { 1702 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_NULL); 1703 packet.put!ubyte(0x00); 1704 } 1705 1706 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == typeof(null))) 1707 { 1708 } 1709 1710 void putValueType(T)(ref OutputPacket packet, T value) if (isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T)) 1711 { 1712 if (value.isNull) 1713 { 1714 putValueType(packet, null); 1715 } 1716 else 1717 { 1718 putValueType(packet, value.get); 1719 } 1720 } 1721 1722 void putValue(T)(ref OutputPacket packet, T value) if (isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T)) 1723 { 1724 if (value.isNull) 1725 { 1726 putValue(packet, null); 1727 } 1728 else 1729 { 1730 putValue(packet, value.get); 1731 } 1732 }