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 import std.typecons; 11 12 import mysql.protocol; 13 import mysql.packet; 14 import mysql.exception; 15 public import 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 return TableNameAttribute(name); 25 } 26 27 @property IgnoreAttribute ignore() { 28 return IgnoreAttribute(); 29 } 30 31 32 @property OptionalAttribute optional() { 33 return OptionalAttribute(); 34 } 35 36 37 @property NameAttribute as(const(char)[] name) { 38 return NameAttribute(name); 39 } 40 41 42 @property UnCamelCaseAttribute uncamel() { 43 return UnCamelCaseAttribute(); 44 } 45 46 47 template Unnull(U) { 48 alias impl(N : Nullable!T, T) = T; 49 alias impl(T) = T; 50 alias Unnull = impl!U; 51 } 52 53 alias Unboth(T) = Unqual!(Unnull!T); 54 enum isSomeDuration(T) = is(Unboth!T == Date) || is(Unboth!T == DateTime) || is(Unboth!T == SysTime) || is(Unboth!T == Duration) || is(Unboth!T == TimeOfDay); 55 enum isValueType(T) = isSomeDuration!(Unboth!T) || is(Unboth!T == MySQLValue) || (!is(Unboth!T == struct) && !is(Unboth!T == class)); 56 57 template isWritableDataMember(T, string Member) { 58 static if (is(TypeTuple!(__traits(getMember, T, Member)))) { 59 enum isWritableDataMember = false; 60 } else static if (!is(typeof(__traits(getMember, T, Member)))) { 61 enum isWritableDataMember = false; 62 } else static if (is(typeof(__traits(getMember, T, Member)) == void)) { 63 enum isWritableDataMember = false; 64 } else static if (is(typeof(__traits(getMember, T, Member)) == enum)) { 65 enum isWritableDataMember = true; 66 } else static if (hasUDA!(__traits(getMember, T, Member), IgnoreAttribute)) { 67 enum isWritableDataMember = false; 68 } 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)) { 69 enum isWritableDataMember = false; 70 } else static if (isAssociativeArray!(typeof(__traits(getMember, T, Member)))) { 71 enum isWritableDataMember = false; 72 } else static if (isSomeFunction!(typeof(__traits(getMember, T, Member)))) { 73 enum isWritableDataMember = false; 74 } else static if (!is(typeof((){ T x = void; __traits(getMember, x, Member) = __traits(getMember, x, Member); }()))) { 75 enum isWritableDataMember = false; 76 } else static if ((__traits(getProtection, __traits(getMember, T, Member)) != "public") && (__traits(getProtection, __traits(getMember, T, Member)) != "export")) { 77 enum isWritableDataMember = false; 78 } else { 79 enum isWritableDataMember = true; 80 } 81 } 82 83 template isReadableDataMember(T, string Member) { 84 static if (is(TypeTuple!(__traits(getMember, T, Member)))) { 85 enum isReadableDataMember = false; 86 } else static if (!is(typeof(__traits(getMember, T, Member)))) { 87 enum isReadableDataMember = false; 88 } else static if (is(typeof(__traits(getMember, T, Member)) == void)) { 89 enum isReadableDataMember = false; 90 } else static if (is(typeof(__traits(getMember, T, Member)) == enum)) { 91 enum isReadableDataMember = true; 92 } else static if (hasUDA!(__traits(getMember, T, Member), IgnoreAttribute)) { 93 enum isReadableDataMember = false; 94 } 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)) { 95 enum isReadableDataMember = false; 96 } else static if (isAssociativeArray!(typeof(__traits(getMember, T, Member)))) { 97 enum isReadableDataMember = false; 98 } else static if (isSomeFunction!(typeof(__traits(getMember, T, Member))) /* && return type is valueType*/ ) { 99 enum isReadableDataMember = true; 100 } else static if (!is(typeof((){ T x = void; __traits(getMember, x, Member) = __traits(getMember, x, Member); }()))) { 101 enum isReadableDataMember = false; 102 } else static if ((__traits(getProtection, __traits(getMember, T, Member)) != "public") && (__traits(getProtection, __traits(getMember, T, Member)) != "export")) { 103 enum isReadableDataMember = false; 104 } else { 105 enum isReadableDataMember = true; 106 } 107 } 108 109 struct MySQLRawString { 110 @disable this(); 111 112 this(const(char)[] data) { 113 data_ = data; 114 } 115 116 @property auto length() const { 117 return data_.length; 118 } 119 120 @property auto data() const { 121 return data_; 122 } 123 124 private const(char)[] data_; 125 } 126 127 128 129 struct MySQLFragment { 130 @disable this(); 131 132 this(const(char)[] data) { 133 data_ = data; 134 } 135 136 @property auto length() const { 137 return data_.length; 138 } 139 140 @property auto data() const { 141 return data_; 142 } 143 144 private const(char)[] data_; 145 } 146 147 148 struct MySQLBinary { 149 this(T)(T[] data) { 150 data_ = (cast(ubyte*)data.ptr)[0..typeof(T[].init[0]).sizeof * data.length]; 151 } 152 153 @property auto length() const { 154 return data_.length; 155 } 156 157 @property auto data() const { 158 return data_; 159 } 160 161 private const(ubyte)[] data_; 162 } 163 164 165 struct MySQLValue { 166 package enum BufferSize = max(ulong.sizeof, (ulong[]).sizeof, MySQLDateTime.sizeof, MySQLTime.sizeof); 167 package this(const(char)[] name, ColumnTypes type, bool signed, void* ptr, size_t size) { 168 assert(size <= BufferSize); 169 type_ = type; 170 sign_ = signed ? 0x00 : 0x80; 171 if (type != ColumnTypes.MYSQL_TYPE_NULL) 172 buffer_[0..size] = (cast(ubyte*)ptr)[0..size]; 173 name_ = name; 174 } 175 176 this(T)(T) if (is(Unqual!T == typeof(null))) { 177 type_ = ColumnTypes.MYSQL_TYPE_NULL; 178 sign_ = 0x00; 179 } 180 181 this(T)(T value) if (is(Unqual!T == MySQLValue)) { 182 this = value; 183 } 184 185 this(T)(T value) if (std.traits.isFloatingPoint!T) { 186 alias UT = Unqual!T; 187 188 sign_ = 0x00; 189 static if (is(UT == float)) { 190 type_ = ColumnTypes.MYSQL_TYPE_FLOAT; 191 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 192 } else static if (is(UT == double)) { 193 type_ = ColumnTypes.MYSQL_TYPE_DOUBLE; 194 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 195 } else { 196 type_ = ColumnTypes.MYSQL_TYPE_DOUBLE; 197 auto data = cast(double)value; 198 buffer_[0..typeof(data).sizeof] = (cast(ubyte*)&data)[0..typeof(data).sizeof]; 199 } 200 } 201 202 this(T)(T value) if (isIntegral!T || isBoolean!T) { 203 alias UT = Unqual!T; 204 205 static if (is(UT == long) || is(UT == ulong)) { 206 type_ = ColumnTypes.MYSQL_TYPE_LONGLONG; 207 } else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) { 208 type_ = ColumnTypes.MYSQL_TYPE_LONG; 209 } else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) { 210 type_ = ColumnTypes.MYSQL_TYPE_SHORT; 211 } else { 212 type_ = ColumnTypes.MYSQL_TYPE_TINY; 213 } 214 215 sign_ = isUnsigned!UT ? 0x80 : 0x00; 216 buffer_[0..T.sizeof] = (cast(ubyte*)&value)[0..T.sizeof]; 217 } 218 219 this(T)(T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) { 220 type_ = ColumnTypes.MYSQL_TYPE_TIMESTAMP; 221 sign_ = 0x00; 222 (*cast(MySQLDateTime*)buffer_) = MySQLDateTime.from(value); 223 } 224 225 this(T)(T value) if (is(Unqual!T == Duration) || is(Unqual!T == TimeOfDay)) { 226 type_ = ColumnTypes.MYSQL_TYPE_TIME; 227 sign_ = 0x00; 228 (*cast(MySQLTime*)buffer_) = MySQLTime.from(value); 229 } 230 231 this(T)(T value) if (isSomeString!(OriginalType!T)) { 232 static assert(typeof(T.init[0]).sizeof == 1, format("Unsupported string type: %s", T.stringof)); 233 234 type_ = ColumnTypes.MYSQL_TYPE_STRING; 235 sign_ = 0x80; 236 237 auto slice = value[0..$]; 238 buffer_.ptr[0..typeof(slice).sizeof] = (cast(ubyte*)&slice)[0..typeof(slice).sizeof]; 239 } 240 241 this(T)(T value) if (is(Unqual!T == MySQLBinary)) { 242 type_ = ColumnTypes.MYSQL_TYPE_BLOB; 243 sign_ = 0x80; 244 buffer_.ptr[0..(ubyte[]).sizeof] = (cast(ubyte*)&value.data_)[0..(ubyte[]).sizeof]; 245 } 246 247 void toString(Appender)(ref Appender app) const { 248 final switch(type_) with (ColumnTypes) { 249 case MYSQL_TYPE_NULL: 250 break; 251 case MYSQL_TYPE_TINY: 252 if (isSigned) formattedWrite(&app, "%d", *cast(ubyte*)buffer_.ptr); 253 else formattedWrite(&app, "%d", *cast(byte*)buffer_.ptr); 254 break; 255 case MYSQL_TYPE_YEAR: 256 case MYSQL_TYPE_SHORT: 257 if (isSigned) formattedWrite(&app, "%d", *cast(short*)buffer_.ptr); 258 else formattedWrite(&app, "%d", *cast(ushort*)buffer_.ptr); 259 break; 260 case MYSQL_TYPE_INT24: 261 case MYSQL_TYPE_LONG: 262 if (isSigned) formattedWrite(&app, "%d", *cast(int*)buffer_.ptr); 263 else formattedWrite(&app, "%d", *cast(uint*)buffer_.ptr); 264 break; 265 case MYSQL_TYPE_LONGLONG: 266 if (isSigned) formattedWrite(&app, "%d", *cast(long*)buffer_.ptr); 267 else formattedWrite(&app, "%d", *cast(ulong*)buffer_.ptr); 268 break; 269 case MYSQL_TYPE_FLOAT: 270 formattedWrite(&app, "%g", *cast(float*)buffer_.ptr); 271 break; 272 case MYSQL_TYPE_DOUBLE: 273 formattedWrite(&app, "%g", *cast(double*)buffer_.ptr); 274 break; 275 case MYSQL_TYPE_SET: 276 case MYSQL_TYPE_ENUM: 277 case MYSQL_TYPE_VARCHAR: 278 case MYSQL_TYPE_VAR_STRING: 279 case MYSQL_TYPE_STRING: 280 case MYSQL_TYPE_JSON: 281 case MYSQL_TYPE_NEWDECIMAL: 282 case MYSQL_TYPE_DECIMAL: 283 case MYSQL_TYPE_TINY_BLOB: 284 case MYSQL_TYPE_MEDIUM_BLOB: 285 case MYSQL_TYPE_LONG_BLOB: 286 case MYSQL_TYPE_BLOB: 287 app.put(*cast(string*)buffer_.ptr); 288 break; 289 case MYSQL_TYPE_BIT: 290 case MYSQL_TYPE_GEOMETRY: 291 formattedWrite(&app, "%s", *cast(ubyte[]*)buffer_.ptr); 292 break; 293 case MYSQL_TYPE_TIME: 294 case MYSQL_TYPE_TIME2: 295 formattedWrite(&app, "%s", (*cast(MySQLTime*)buffer_.ptr).to!Duration()); 296 break; 297 case MYSQL_TYPE_DATE: 298 case MYSQL_TYPE_NEWDATE: 299 case MYSQL_TYPE_DATETIME: 300 case MYSQL_TYPE_DATETIME2: 301 case MYSQL_TYPE_TIMESTAMP: 302 case MYSQL_TYPE_TIMESTAMP2: 303 formattedWrite(&app, "%s", (*cast(MySQLDateTime*)buffer_.ptr).to!DateTime()); 304 break; 305 } 306 } 307 308 string toString() const { 309 auto app = appender!string; 310 toString(app); 311 return app.data; 312 } 313 314 bool opEquals(MySQLValue other) const { 315 if (isString && other.isString) { 316 return peek!string == other.peek!string; 317 } else if (isScalar && other.isScalar) { 318 if (isFloatingPoint || other.isFloatingPoint) 319 return get!double == other.get!double; 320 if (isSigned || other.isSigned) 321 return get!long == other.get!long; 322 return get!ulong == other.get!ulong; 323 } else if (isTime && other.isTime) { 324 return get!Duration == other.get!Duration; 325 } else if (isTimestamp && other.isTimestamp) { 326 return get!SysTime == other.get!SysTime; 327 } else if (isNull && other.isNull) { 328 return true; 329 } 330 return false; 331 } 332 333 T get(T)(lazy T def) const { 334 return !isNull ? get!T : def; 335 } 336 337 T get(T)() const if (isScalarType!T && !is(T == enum)) { 338 switch(type_) with (ColumnTypes) { 339 case MYSQL_TYPE_TINY: 340 return cast(T)(*cast(ubyte*)buffer_.ptr); 341 case MYSQL_TYPE_YEAR: 342 case MYSQL_TYPE_SHORT: 343 return cast(T)(*cast(ushort*)buffer_.ptr); 344 case MYSQL_TYPE_INT24: 345 case MYSQL_TYPE_LONG: 346 return cast(T)(*cast(uint*)buffer_.ptr); 347 case MYSQL_TYPE_LONGLONG: 348 return cast(T)(*cast(ulong*)buffer_.ptr); 349 case MYSQL_TYPE_FLOAT: 350 return cast(T)(*cast(float*)buffer_.ptr); 351 case MYSQL_TYPE_DOUBLE: 352 return cast(T)(*cast(double*)buffer_.ptr); 353 default: 354 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 355 } 356 } 357 358 T get(T)() const if (is(Unqual!T == SysTime) || is(Unqual!T == DateTime) || is(Unqual!T == Date)) { 359 switch(type_) with (ColumnTypes) { 360 case MYSQL_TYPE_DATE: 361 case MYSQL_TYPE_NEWDATE: 362 case MYSQL_TYPE_DATETIME: 363 case MYSQL_TYPE_DATETIME2: 364 case MYSQL_TYPE_TIMESTAMP: 365 case MYSQL_TYPE_TIMESTAMP2: 366 return (*cast(MySQLDateTime*)buffer_.ptr).to!T; 367 default: 368 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 369 } 370 } 371 372 T get(T)() const if (is(Unqual!T == TimeOfDay)) { 373 switch(type_) with (ColumnTypes) { 374 case MYSQL_TYPE_DATE: 375 case MYSQL_TYPE_NEWDATE: 376 case MYSQL_TYPE_DATETIME: 377 case MYSQL_TYPE_DATETIME2: 378 case MYSQL_TYPE_TIMESTAMP: 379 case MYSQL_TYPE_TIMESTAMP2: 380 return (*cast(MySQLDateTime*)buffer_.ptr).to!T; 381 case MYSQL_TYPE_TIME: 382 case MYSQL_TYPE_TIME2: 383 return (*cast(MySQLTime*)buffer_.ptr).to!T; 384 default: 385 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 386 } 387 } 388 389 T get(T)() const if (is(Unqual!T == Duration)) { 390 switch(type_) with (ColumnTypes) { 391 case MYSQL_TYPE_TIME: 392 case MYSQL_TYPE_TIME2: 393 return (*cast(MySQLTime*)buffer_.ptr).to!T; 394 default: 395 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 396 } 397 } 398 399 T get(T)() const if (is(Unqual!T == enum)) { 400 return cast(T)get!(OriginalType!T); 401 } 402 403 T get(T)() const if (isArray!T && !is(T == enum)) { 404 switch(type_) with (ColumnTypes) { 405 case MYSQL_TYPE_SET: 406 case MYSQL_TYPE_ENUM: 407 case MYSQL_TYPE_VARCHAR: 408 case MYSQL_TYPE_VAR_STRING: 409 case MYSQL_TYPE_STRING: 410 case MYSQL_TYPE_JSON: 411 case MYSQL_TYPE_NEWDECIMAL: 412 case MYSQL_TYPE_DECIMAL: 413 return (*cast(T*)buffer_.ptr).dup; 414 case MYSQL_TYPE_BIT: 415 case MYSQL_TYPE_TINY_BLOB: 416 case MYSQL_TYPE_MEDIUM_BLOB: 417 case MYSQL_TYPE_LONG_BLOB: 418 case MYSQL_TYPE_BLOB: 419 case MYSQL_TYPE_GEOMETRY: 420 return (*cast(T*)buffer_.ptr).dup; 421 default: 422 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 423 } 424 } 425 426 T get(T)() const if(isInstanceOf!(Nullable, T)) { 427 if (type_ == ColumnTypes.MYSQL_TYPE_NULL) 428 return T.init; 429 return T(get!(typeof(T.init.get))); 430 } 431 432 T peek(T)(lazy T def) const { 433 return !isNull ? peek!(T) : def; 434 } 435 436 T peek(T)() const if (isScalarType!T) { 437 return get!(T); 438 } 439 440 T peek(T)() const if (is(Unqual!T == SysTime) || is(Unqual!T == DateTime) || is(Unqual!T == Date) || is(Unqual!T == TimeOfDay)) { 441 return get!(T); 442 } 443 444 T peek(T)() const if (is(Unqual!T == Duration)) { 445 return get!(T); 446 } 447 448 T peek(T)() const if (isArray!T) { 449 switch(type_) with (ColumnTypes) { 450 case MYSQL_TYPE_SET: 451 case MYSQL_TYPE_ENUM: 452 case MYSQL_TYPE_VARCHAR: 453 case MYSQL_TYPE_VAR_STRING: 454 case MYSQL_TYPE_STRING: 455 case MYSQL_TYPE_JSON: 456 case MYSQL_TYPE_NEWDECIMAL: 457 case MYSQL_TYPE_DECIMAL: 458 return (*cast(T*)buffer_.ptr); 459 case MYSQL_TYPE_BIT: 460 case MYSQL_TYPE_TINY_BLOB: 461 case MYSQL_TYPE_MEDIUM_BLOB: 462 case MYSQL_TYPE_LONG_BLOB: 463 case MYSQL_TYPE_BLOB: 464 case MYSQL_TYPE_GEOMETRY: 465 return (*cast(T*)buffer_.ptr); 466 default: 467 throw new MySQLErrorException(format("Cannot convert '%s' from %s to %s", name_, columnTypeName(type_), T.stringof)); 468 } 469 } 470 471 bool isNull() const { 472 return type_ == ColumnTypes.MYSQL_TYPE_NULL; 473 } 474 475 ColumnTypes type() const { 476 return type_; 477 } 478 479 bool isSigned() const { 480 return sign_ == 0x00; 481 } 482 483 bool isString() const { 484 final switch(type_) with (ColumnTypes) { 485 case MYSQL_TYPE_NULL: 486 return false; 487 case MYSQL_TYPE_TINY: 488 case MYSQL_TYPE_YEAR: 489 case MYSQL_TYPE_SHORT: 490 case MYSQL_TYPE_INT24: 491 case MYSQL_TYPE_LONG: 492 case MYSQL_TYPE_LONGLONG: 493 case MYSQL_TYPE_FLOAT: 494 case MYSQL_TYPE_DOUBLE: 495 return false; 496 case MYSQL_TYPE_SET: 497 case MYSQL_TYPE_ENUM: 498 case MYSQL_TYPE_VARCHAR: 499 case MYSQL_TYPE_VAR_STRING: 500 case MYSQL_TYPE_STRING: 501 case MYSQL_TYPE_JSON: 502 case MYSQL_TYPE_NEWDECIMAL: 503 case MYSQL_TYPE_DECIMAL: 504 case MYSQL_TYPE_TINY_BLOB: 505 case MYSQL_TYPE_MEDIUM_BLOB: 506 case MYSQL_TYPE_LONG_BLOB: 507 case MYSQL_TYPE_BLOB: 508 return true; 509 case MYSQL_TYPE_BIT: 510 case MYSQL_TYPE_GEOMETRY: 511 return false; 512 case MYSQL_TYPE_TIME: 513 case MYSQL_TYPE_TIME2: 514 return false; 515 case MYSQL_TYPE_DATE: 516 case MYSQL_TYPE_NEWDATE: 517 case MYSQL_TYPE_DATETIME: 518 case MYSQL_TYPE_DATETIME2: 519 case MYSQL_TYPE_TIMESTAMP: 520 case MYSQL_TYPE_TIMESTAMP2: 521 return false; 522 } 523 } 524 525 bool isScalar() const { 526 final switch(type_) with (ColumnTypes) { 527 case MYSQL_TYPE_NULL: 528 return false; 529 case MYSQL_TYPE_TINY: 530 case MYSQL_TYPE_YEAR: 531 case MYSQL_TYPE_SHORT: 532 case MYSQL_TYPE_INT24: 533 case MYSQL_TYPE_LONG: 534 case MYSQL_TYPE_LONGLONG: 535 case MYSQL_TYPE_FLOAT: 536 case MYSQL_TYPE_DOUBLE: 537 return true; 538 case MYSQL_TYPE_SET: 539 case MYSQL_TYPE_ENUM: 540 case MYSQL_TYPE_VARCHAR: 541 case MYSQL_TYPE_VAR_STRING: 542 case MYSQL_TYPE_STRING: 543 case MYSQL_TYPE_JSON: 544 case MYSQL_TYPE_NEWDECIMAL: 545 case MYSQL_TYPE_DECIMAL: 546 case MYSQL_TYPE_TINY_BLOB: 547 case MYSQL_TYPE_MEDIUM_BLOB: 548 case MYSQL_TYPE_LONG_BLOB: 549 case MYSQL_TYPE_BLOB: 550 return false; 551 case MYSQL_TYPE_BIT: 552 case MYSQL_TYPE_GEOMETRY: 553 return false; 554 case MYSQL_TYPE_TIME: 555 case MYSQL_TYPE_TIME2: 556 return false; 557 case MYSQL_TYPE_DATE: 558 case MYSQL_TYPE_NEWDATE: 559 case MYSQL_TYPE_DATETIME: 560 case MYSQL_TYPE_DATETIME2: 561 case MYSQL_TYPE_TIMESTAMP: 562 case MYSQL_TYPE_TIMESTAMP2: 563 return false; 564 } 565 } 566 567 bool isFloatingPoint() const { 568 final switch(type_) with (ColumnTypes) { 569 case MYSQL_TYPE_NULL: 570 return false; 571 case MYSQL_TYPE_TINY: 572 case MYSQL_TYPE_YEAR: 573 case MYSQL_TYPE_SHORT: 574 case MYSQL_TYPE_INT24: 575 case MYSQL_TYPE_LONG: 576 case MYSQL_TYPE_LONGLONG: 577 return false; 578 case MYSQL_TYPE_FLOAT: 579 case MYSQL_TYPE_DOUBLE: 580 return true; 581 case MYSQL_TYPE_SET: 582 case MYSQL_TYPE_ENUM: 583 case MYSQL_TYPE_VARCHAR: 584 case MYSQL_TYPE_VAR_STRING: 585 case MYSQL_TYPE_STRING: 586 case MYSQL_TYPE_JSON: 587 case MYSQL_TYPE_NEWDECIMAL: 588 case MYSQL_TYPE_DECIMAL: 589 case MYSQL_TYPE_TINY_BLOB: 590 case MYSQL_TYPE_MEDIUM_BLOB: 591 case MYSQL_TYPE_LONG_BLOB: 592 case MYSQL_TYPE_BLOB: 593 return false; 594 case MYSQL_TYPE_BIT: 595 case MYSQL_TYPE_GEOMETRY: 596 return false; 597 case MYSQL_TYPE_TIME: 598 case MYSQL_TYPE_TIME2: 599 return false; 600 case MYSQL_TYPE_DATE: 601 case MYSQL_TYPE_NEWDATE: 602 case MYSQL_TYPE_DATETIME: 603 case MYSQL_TYPE_DATETIME2: 604 case MYSQL_TYPE_TIMESTAMP: 605 case MYSQL_TYPE_TIMESTAMP2: 606 return false; 607 } 608 } 609 610 bool isTime() const { 611 final switch(type_) with (ColumnTypes) { 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 false; 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 true; 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 alias isDuration = isTime; 653 654 bool isDateTime() const { 655 final switch(type_) with (ColumnTypes) { 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 false; 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 true; 693 } 694 } 695 696 alias isTimestamp = isDateTime; 697 698 private: 699 ColumnTypes type_ = ColumnTypes.MYSQL_TYPE_NULL; 700 ubyte sign_; 701 ubyte[6] pad_; 702 ubyte[BufferSize] buffer_; 703 const(char)[] name_; 704 } 705 706 707 struct MySQLColumn { 708 uint length; 709 ushort flags; 710 ubyte decimals; 711 ColumnTypes type; 712 const(char)[] name; 713 } 714 715 716 alias MySQLHeader = MySQLColumn[]; 717 718 719 struct MySQLTime { 720 uint days; 721 ubyte negative; 722 ubyte hours; 723 ubyte mins; 724 ubyte secs; 725 uint usecs; 726 727 auto to(T)() const if (is(Unqual!T == Duration)) { 728 auto total = days * 86400_000_000L + 729 hours * 3600_000_000L + 730 mins * 60_000_000L + 731 secs * 1_000_000L + 732 usecs; 733 return cast(T)dur!"usecs"(negative ? -total : total); 734 } 735 736 auto to(T)() const if (is(Unqual!T == TimeOfDay)) { 737 return cast(T)TimeOfDay(hours, mins, secs); 738 } 739 740 static MySQLTime from(Duration duration) { 741 MySQLTime time; 742 duration.abs.split!("days", "hours", "minutes", "seconds", "usecs")(time.days, time.hours, time.mins, time.secs, time.usecs); 743 time.negative = duration.isNegative ? 1 : 0; 744 return time; 745 } 746 747 static MySQLTime from(TimeOfDay tod) { 748 MySQLTime time; 749 time.hours = tod.hour; 750 time.mins = tod.minute; 751 time.secs = tod.second; 752 return time; 753 } 754 } 755 756 void putMySQLTime(ref OutputPacket packet, in MySQLTime time) { 757 if (time.days || time.hours || time.mins || time.mins || time.usecs) { 758 auto usecs = time.usecs != 0; 759 packet.put!ubyte(usecs ? 12 : 8); 760 packet.put!ubyte(time.negative); 761 packet.put!uint(time.days); 762 packet.put!ubyte(time.hours); 763 packet.put!ubyte(time.mins); 764 packet.put!ubyte(time.secs); 765 if (usecs) 766 packet.put!uint(time.usecs); 767 } else { 768 packet.put!ubyte(0); 769 } 770 } 771 772 auto eatMySQLTime(ref InputPacket packet) { 773 MySQLTime time; 774 switch(packet.eat!ubyte) { 775 case 12: 776 time.negative = packet.eat!ubyte; 777 time.days = packet.eat!uint; 778 time.hours = packet.eat!ubyte; 779 time.mins = packet.eat!ubyte; 780 time.secs = packet.eat!ubyte; 781 time.usecs = packet.eat!uint; 782 break; 783 case 8: 784 time.negative = packet.eat!ubyte; 785 time.days = packet.eat!uint; 786 time.hours = packet.eat!ubyte; 787 time.mins = packet.eat!ubyte; 788 time.secs = packet.eat!ubyte; 789 break; 790 case 0: 791 break; 792 default: 793 throw new MySQLProtocolException("Bad time struct format"); 794 } 795 796 return time; 797 } 798 799 800 struct MySQLDateTime { 801 ushort year; 802 ubyte month; 803 ubyte day; 804 ubyte hour; 805 ubyte min; 806 ubyte sec; 807 uint usec; 808 809 bool valid() const { 810 return month != 0; 811 } 812 813 T to(T)() const if (is(Unqual!T == SysTime)) { 814 assert(valid()); 815 return cast(T)SysTime(DateTime(year, month, day, hour, min, sec), usec.dur!"usecs", UTC()); 816 } 817 818 T to(T)() const if (is(Unqual!T == DateTime)) { 819 assert(valid()); 820 return cast(T)DateTime(year, month, day, hour, min, sec); 821 } 822 823 T to(T)() const if (is(T == Date)) { 824 assert(valid()); 825 return cast(T)Date(year, month, day); 826 } 827 828 T to(T)() const if (is(Unqual!T == TimeOfDay)) { 829 return cast(T)TimeOfDay(hour, min, sec); 830 } 831 832 static MySQLDateTime from(SysTime sysTime) { 833 MySQLDateTime time; 834 835 auto dateTime = cast(DateTime)sysTime; 836 time.year = dateTime.year; 837 time.month = dateTime.month; 838 time.day = dateTime.day; 839 time.hour = dateTime.hour; 840 time.min = dateTime.minute; 841 time.sec = dateTime.second; 842 time.usec = cast(int)sysTime.fracSecs.total!"usecs"; 843 844 return time; 845 } 846 847 static MySQLDateTime from(DateTime dateTime) { 848 MySQLDateTime time; 849 850 time.year = dateTime.year; 851 time.month = dateTime.month; 852 time.day = dateTime.day; 853 time.hour = dateTime.hour; 854 time.min = dateTime.minute; 855 time.sec = dateTime.second; 856 857 return time; 858 } 859 860 static MySQLDateTime from(Date date) { 861 MySQLDateTime time; 862 863 time.year = date.year; 864 time.month = date.month; 865 time.day = date.day; 866 867 return time; 868 } 869 } 870 871 void putMySQLDateTime(ref OutputPacket packet, in MySQLDateTime time) { 872 auto marker = packet.marker!ubyte; 873 ubyte length; 874 875 if (time.year || time.month || time.day) { 876 length = 4; 877 packet.put!ushort(time.year); 878 packet.put!ubyte(time.month); 879 packet.put!ubyte(time.day); 880 881 if (time.hour || time.min || time.sec || time.usec) { 882 length = 7; 883 packet.put!ubyte(time.hour); 884 packet.put!ubyte(time.min); 885 packet.put!ubyte(time.sec); 886 887 if (time.usec) { 888 length = 11; 889 packet.put!uint(time.usec); 890 } 891 } 892 } 893 894 packet.put!ubyte(marker, length); 895 } 896 897 auto eatMySQLDateTime(ref InputPacket packet) { 898 MySQLDateTime time; 899 switch(packet.eat!ubyte) { 900 case 11: 901 time.year = packet.eat!ushort; 902 time.month = packet.eat!ubyte; 903 time.day = packet.eat!ubyte; 904 time.hour = packet.eat!ubyte; 905 time.min = packet.eat!ubyte; 906 time.sec = packet.eat!ubyte; 907 time.usec = packet.eat!uint; 908 break; 909 case 7: 910 time.year = packet.eat!ushort; 911 time.month = packet.eat!ubyte; 912 time.day = packet.eat!ubyte; 913 time.hour = packet.eat!ubyte; 914 time.min = packet.eat!ubyte; 915 time.sec = packet.eat!ubyte; 916 break; 917 case 4: 918 time.year = packet.eat!ushort; 919 time.month = packet.eat!ubyte; 920 time.day = packet.eat!ubyte; 921 break; 922 case 0: 923 break; 924 default: 925 throw new MySQLProtocolException("Bad datetime struct format"); 926 } 927 928 return time; 929 } 930 931 private void skip(ref const(char)[] x, char ch) { 932 if (x.length && (x.ptr[0] == ch)) { 933 x = x[1..$]; 934 } else { 935 throw new MySQLProtocolException("Bad datetime string format"); 936 } 937 } 938 939 auto parseMySQLTime(const(char)[] x) { 940 MySQLTime time; 941 942 auto hours = x.parse!int; 943 if (hours < 0) { 944 time.negative = 1; 945 hours = -hours; 946 } 947 time.days = hours / 24; 948 time.hours = cast(ubyte)(hours % 24); 949 x.skip(':'); 950 time.mins = x.parse!ubyte; 951 x.skip(':'); 952 time.secs = x.parse!ubyte; 953 if (x.length) { 954 x.skip('.'); 955 time.usecs = x.parse!uint; 956 switch (6 - max(6, x.length)) { 957 case 0: break; 958 case 1: time.usecs *= 10; break; 959 case 2: time.usecs *= 100; break; 960 case 3: time.usecs *= 1_000; break; 961 case 4: time.usecs *= 10_000; break; 962 case 5: time.usecs *= 100_000; break; 963 default: assert("Bad datetime string format"); break; 964 } 965 } 966 967 return time; 968 } 969 970 auto parseMySQLDateTime(const(char)[] x) { 971 MySQLDateTime time; 972 973 time.year = x.parse!ushort; 974 x.skip('-'); 975 time.month = x.parse!ubyte; 976 x.skip('-'); 977 time.day = x.parse!ubyte; 978 if (x.length) { 979 x.skip(' '); 980 time.hour = x.parse!ubyte; 981 x.skip(':'); 982 time.min = x.parse!ubyte; 983 x.skip(':'); 984 time.sec = x.parse!ubyte; 985 986 if (x.length) { 987 x.skip('.'); 988 time.usec = x.parse!uint; 989 switch (6 - max(6, x.length)) { 990 case 0: break; 991 case 1: time.usec *= 10; break; 992 case 2: time.usec *= 100; break; 993 case 3: time.usec *= 1_000; break; 994 case 4: time.usec *= 10_000; break; 995 case 5: time.usec *= 100_000; break; 996 default: assert("Bad datetime string format"); break; 997 } 998 } 999 } 1000 1001 return time; 1002 } 1003 1004 void eatValue(ref InputPacket packet, ref const MySQLColumn column, ref MySQLValue value) { 1005 auto signed = (column.flags & FieldFlags.UNSIGNED_FLAG) == 0; 1006 final switch(column.type) with (ColumnTypes) { 1007 case MYSQL_TYPE_NULL: 1008 value = MySQLValue(column.name, column.type, signed, null, 0); 1009 break; 1010 case MYSQL_TYPE_TINY: 1011 auto x = packet.eat!ubyte; 1012 value = MySQLValue(column.name, column.type, signed, &x, 1); 1013 break; 1014 case MYSQL_TYPE_YEAR: 1015 case MYSQL_TYPE_SHORT: 1016 auto x = packet.eat!ushort; 1017 value = MySQLValue(column.name, column.type, signed, &x, 2); 1018 break; 1019 case MYSQL_TYPE_INT24: 1020 case MYSQL_TYPE_LONG: 1021 auto x = packet.eat!uint; 1022 value = MySQLValue(column.name, column.type, signed, &x, 4); 1023 break; 1024 case MYSQL_TYPE_DOUBLE: 1025 case MYSQL_TYPE_LONGLONG: 1026 auto x = packet.eat!ulong; 1027 value = MySQLValue(column.name, column.type, signed, &x, 8); 1028 break; 1029 case MYSQL_TYPE_FLOAT: 1030 auto x = packet.eat!float; 1031 value = MySQLValue(column.name, column.type, signed, &x, 4); 1032 break; 1033 case MYSQL_TYPE_SET: 1034 case MYSQL_TYPE_ENUM: 1035 case MYSQL_TYPE_VARCHAR: 1036 case MYSQL_TYPE_VAR_STRING: 1037 case MYSQL_TYPE_STRING: 1038 case MYSQL_TYPE_JSON: 1039 case MYSQL_TYPE_NEWDECIMAL: 1040 case MYSQL_TYPE_DECIMAL: 1041 auto x = packet.eat!(const(char)[])(cast(size_t)packet.eatLenEnc()); 1042 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1043 break; 1044 case MYSQL_TYPE_BIT: 1045 case MYSQL_TYPE_TINY_BLOB: 1046 case MYSQL_TYPE_MEDIUM_BLOB: 1047 case MYSQL_TYPE_LONG_BLOB: 1048 case MYSQL_TYPE_BLOB: 1049 case MYSQL_TYPE_GEOMETRY: 1050 auto x = packet.eat!(const(ubyte)[])(cast(size_t)packet.eatLenEnc()); 1051 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1052 break; 1053 case MYSQL_TYPE_TIME: 1054 case MYSQL_TYPE_TIME2: 1055 auto x = eatMySQLTime(packet); 1056 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1057 break; 1058 case MYSQL_TYPE_DATE: 1059 case MYSQL_TYPE_NEWDATE: 1060 case MYSQL_TYPE_DATETIME: 1061 case MYSQL_TYPE_DATETIME2: 1062 case MYSQL_TYPE_TIMESTAMP: 1063 case MYSQL_TYPE_TIMESTAMP2: 1064 auto x = eatMySQLDateTime(packet); 1065 value = x.valid() ? MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof) : MySQLValue(column.name, ColumnTypes.MYSQL_TYPE_NULL, signed, null, 0); 1066 break; 1067 } 1068 } 1069 1070 void eatValueText(ref InputPacket packet, ref const MySQLColumn column, ref MySQLValue value) { 1071 auto signed = (column.flags & FieldFlags.UNSIGNED_FLAG) == 0; 1072 auto svalue = (column.type != ColumnTypes.MYSQL_TYPE_NULL) ? cast(string)(packet.eat!(const(char)[])(cast(size_t)packet.eatLenEnc())) : string.init; 1073 final switch(column.type) with (ColumnTypes) { 1074 case MYSQL_TYPE_NULL: 1075 value = MySQLValue(column.name, column.type, signed, null, 0); 1076 break; 1077 case MYSQL_TYPE_TINY: 1078 auto x = (svalue.ptr[0] == '-') ? cast(ubyte)(-svalue[1..$].to!byte) : svalue.to!ubyte; 1079 value = MySQLValue(column.name, column.type, signed, &x, 1); 1080 break; 1081 case MYSQL_TYPE_YEAR: 1082 case MYSQL_TYPE_SHORT: 1083 auto x = (svalue.ptr[0] == '-') ? cast(ushort)(-svalue[1..$].to!short) : svalue.to!ushort; 1084 value = MySQLValue(column.name, column.type, signed, &x, 2); 1085 break; 1086 case MYSQL_TYPE_INT24: 1087 case MYSQL_TYPE_LONG: 1088 auto x = (svalue.ptr[0] == '-') ? cast(uint)(-svalue[1..$].to!int) : svalue.to!uint; 1089 value = MySQLValue(column.name, column.type, signed, &x, 4); 1090 break; 1091 case MYSQL_TYPE_LONGLONG: 1092 auto x = (svalue.ptr[0] == '-') ? cast(ulong)(-svalue[1..$].to!long) : svalue.to!ulong; 1093 value = MySQLValue(column.name, column.type, signed, &x, 8); 1094 break; 1095 case MYSQL_TYPE_DOUBLE: 1096 auto x = svalue.to!double; 1097 value = MySQLValue(column.name, column.type, signed, &x, 8); 1098 break; 1099 case MYSQL_TYPE_FLOAT: 1100 auto x = svalue.to!float; 1101 value = MySQLValue(column.name, column.type, signed, &x, 4); 1102 break; 1103 case MYSQL_TYPE_SET: 1104 case MYSQL_TYPE_ENUM: 1105 case MYSQL_TYPE_VARCHAR: 1106 case MYSQL_TYPE_VAR_STRING: 1107 case MYSQL_TYPE_STRING: 1108 case MYSQL_TYPE_JSON: 1109 case MYSQL_TYPE_NEWDECIMAL: 1110 case MYSQL_TYPE_DECIMAL: 1111 value = MySQLValue(column.name, column.type, signed, &svalue, typeof(svalue).sizeof); 1112 break; 1113 case MYSQL_TYPE_BIT: 1114 case MYSQL_TYPE_TINY_BLOB: 1115 case MYSQL_TYPE_MEDIUM_BLOB: 1116 case MYSQL_TYPE_LONG_BLOB: 1117 case MYSQL_TYPE_BLOB: 1118 case MYSQL_TYPE_GEOMETRY: 1119 value = MySQLValue(column.name, column.type, signed, &svalue, typeof(svalue).sizeof); 1120 break; 1121 case MYSQL_TYPE_TIME: 1122 case MYSQL_TYPE_TIME2: 1123 auto x = parseMySQLTime(svalue); 1124 value = MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof); 1125 break; 1126 case MYSQL_TYPE_DATE: 1127 case MYSQL_TYPE_NEWDATE: 1128 case MYSQL_TYPE_DATETIME: 1129 case MYSQL_TYPE_DATETIME2: 1130 case MYSQL_TYPE_TIMESTAMP: 1131 case MYSQL_TYPE_TIMESTAMP2: 1132 auto x = parseMySQLDateTime(svalue); 1133 value = x.valid() ? MySQLValue(column.name, column.type, signed, &x, typeof(x).sizeof) : MySQLValue(column.name, ColumnTypes.MYSQL_TYPE_NULL, signed, null, 0); 1134 break; 1135 } 1136 } 1137 1138 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) { 1139 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TIMESTAMP); 1140 packet.put!ubyte(0x80); 1141 } 1142 1143 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) { 1144 putMySQLDateTime(packet, MySQLDateTime.from(value)); 1145 } 1146 1147 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Duration)) { 1148 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TIME); 1149 packet.put!ubyte(0x00); 1150 } 1151 1152 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == Duration)) { 1153 putMySQLTime(packet, MySQLTime.from(value)); 1154 } 1155 1156 void putValueType(T)(ref OutputPacket packet, T value) if (isIntegral!T || isBoolean!T) { 1157 alias UT = Unqual!T; 1158 1159 enum ubyte sign = isUnsigned!UT ? 0x80 : 0x00; 1160 1161 static if (is(UT == long) || is(UT == ulong)) { 1162 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_LONGLONG); 1163 packet.put!ubyte(sign); 1164 } else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) { 1165 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_LONG); 1166 packet.put!ubyte(sign); 1167 } else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) { 1168 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_SHORT); 1169 packet.put!ubyte(sign); 1170 } else { 1171 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_TINY); 1172 packet.put!ubyte(sign); 1173 } 1174 } 1175 1176 void putValueType(T)(ref OutputPacket packet, T value) if (isFloatingPoint!T) { 1177 alias UT = Unqual!T; 1178 1179 enum ubyte sign = 0x00; 1180 1181 static if (is(UT == float)) { 1182 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_FLOAT); 1183 packet.put!ubyte(sign); 1184 } else { 1185 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_DOUBLE); 1186 packet.put!ubyte(sign); 1187 } 1188 } 1189 1190 void putValue(T)(ref OutputPacket packet, T value) if (isIntegral!T || isBoolean!T) { 1191 alias UT = Unqual!T; 1192 1193 static if (is(UT == long) || is(UT == ulong)) { 1194 packet.put!ulong(value); 1195 } else static if (is(UT == int) || is(UT == uint) || is(UT == dchar)) { 1196 packet.put!uint(value); 1197 } else static if (is(UT == short) || is(UT == ushort) || is(UT == wchar)) { 1198 packet.put!ushort(value); 1199 } else { 1200 packet.put!ubyte(value); 1201 } 1202 } 1203 1204 void putValue(T)(ref OutputPacket packet, T value) if (isFloatingPoint!T) { 1205 alias UT = Unqual!T; 1206 1207 static if (is(UT == float)) { 1208 packet.put!float(value); 1209 } else { 1210 packet.put!double(cast(double)value); 1211 } 1212 } 1213 1214 void putValueType(T)(ref OutputPacket packet, T value) if (isSomeString!(OriginalType!T)) { 1215 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_STRING); 1216 packet.put!ubyte(0x80); 1217 } 1218 1219 void putValue(T)(ref OutputPacket packet, T value) if (isSomeString!(OriginalType!T)) { 1220 ulong size = value.length * T.init[0].sizeof; 1221 packet.putLenEnc(size); 1222 packet.put(value); 1223 } 1224 1225 void putValueType(T)(ref OutputPacket packet, T value) if (isArray!T && !isSomeString!(OriginalType!T)) { 1226 foreach(ref item; value) 1227 putValueType(packet, item); 1228 } 1229 1230 void putValue(T)(ref OutputPacket packet, T value) if (isArray!T && !isSomeString!(OriginalType!T)) { 1231 foreach(ref item; value) 1232 putValue(packet, item); 1233 } 1234 1235 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLBinary)) { 1236 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_BLOB); 1237 packet.put!ubyte(0x80); 1238 } 1239 1240 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLBinary)) { 1241 ulong size = value.length; 1242 packet.putLenEnc(size); 1243 packet.put(value.data); 1244 } 1245 1246 void putValueType(T)(ref OutputPacket packet, T value) if(is(Unqual!T == MySQLValue)) { 1247 packet.put!ubyte(value.type_); 1248 packet.put!ubyte(value.sign_); 1249 } 1250 1251 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == MySQLValue)) { 1252 final switch(value.type) with (ColumnTypes) { 1253 case MYSQL_TYPE_NULL: 1254 break; 1255 case MYSQL_TYPE_TINY: 1256 packet.put!ubyte(*cast(ubyte*)value.buffer_.ptr); 1257 break; 1258 case MYSQL_TYPE_YEAR: 1259 case MYSQL_TYPE_SHORT: 1260 packet.put!ushort(*cast(ushort*)value.buffer_.ptr); 1261 break; 1262 case MYSQL_TYPE_INT24: 1263 case MYSQL_TYPE_LONG: 1264 packet.put!uint(*cast(uint*)value.buffer_.ptr); 1265 break; 1266 case MYSQL_TYPE_LONGLONG: 1267 packet.put!ulong(*cast(ulong*)value.buffer_.ptr); 1268 break; 1269 case MYSQL_TYPE_DOUBLE: 1270 packet.put!double(*cast(double*)value.buffer_.ptr); 1271 break; 1272 case MYSQL_TYPE_FLOAT: 1273 packet.put!float(*cast(float*)value.buffer_.ptr); 1274 break; 1275 case MYSQL_TYPE_SET: 1276 case MYSQL_TYPE_ENUM: 1277 case MYSQL_TYPE_VARCHAR: 1278 case MYSQL_TYPE_VAR_STRING: 1279 case MYSQL_TYPE_STRING: 1280 case MYSQL_TYPE_JSON: 1281 case MYSQL_TYPE_NEWDECIMAL: 1282 case MYSQL_TYPE_DECIMAL: 1283 case MYSQL_TYPE_BIT: 1284 case MYSQL_TYPE_TINY_BLOB: 1285 case MYSQL_TYPE_MEDIUM_BLOB: 1286 case MYSQL_TYPE_LONG_BLOB: 1287 case MYSQL_TYPE_BLOB: 1288 case MYSQL_TYPE_GEOMETRY: 1289 packet.putLenEnc((*cast(ubyte[]*)value.buffer_.ptr).length); 1290 packet.put(*cast(ubyte[]*)value.buffer_.ptr); 1291 break; 1292 case MYSQL_TYPE_TIME: 1293 case MYSQL_TYPE_TIME2: 1294 packet.putMySQLTime(*cast(MySQLTime*)value.buffer_.ptr); 1295 break; 1296 case MYSQL_TYPE_DATE: 1297 case MYSQL_TYPE_NEWDATE: 1298 case MYSQL_TYPE_DATETIME: 1299 case MYSQL_TYPE_DATETIME2: 1300 case MYSQL_TYPE_TIMESTAMP: 1301 case MYSQL_TYPE_TIMESTAMP2: 1302 packet.putMySQLDateTime(*cast(MySQLDateTime*)value.buffer_.ptr); 1303 break; 1304 } 1305 } 1306 1307 void putValueType(T)(ref OutputPacket packet, T value) if (is(Unqual!T == typeof(null))) { 1308 packet.put!ubyte(ColumnTypes.MYSQL_TYPE_NULL); 1309 packet.put!ubyte(0x00); 1310 } 1311 1312 void putValue(T)(ref OutputPacket packet, T value) if (is(Unqual!T == typeof(null))) { 1313 } 1314 1315 void putValueType(T)(ref OutputPacket packet, T value) if (isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T)) { 1316 if (value.isNull) { 1317 putValueType(packet, null); 1318 } else { 1319 putValueType(packet, value.get); 1320 } 1321 } 1322 1323 void putValue(T)(ref OutputPacket packet, T value) if (isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T)) { 1324 if (value.isNull) { 1325 putValue(packet, null); 1326 } else { 1327 putValue(packet, value.get); 1328 } 1329 }