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