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