1 /// Internal - Helper functions for the communication protocol.
2 module mysql.protocol.packet_helpers;
3 
4 import std.algorithm;
5 import std.conv;
6 import std.datetime;
7 import std.exception;
8 import std.range;
9 import std.string;
10 import std.traits;
11 
12 import mysql.exceptions;
13 import mysql.protocol.constants;
14 import mysql.protocol.extra_types;
15 import mysql.protocol.sockets;
16 import mysql.types;
17 
18 @safe:
19 
20 /++
21 Function to extract a time difference from a binary encoded row.
22 
23 Time/date structures are packed by the server into a byte sub-packet
24 with a leading length byte, and a minimal number of bytes to embody the data.
25 
26 Params: a = slice of a protocol packet beginning at the length byte for a chunk of time data
27 
28 Returns: A populated or default initialized TimeDiff struct.
29 +/
30 TimeDiff toTimeDiff(in ubyte[] a) pure
31 {
32 	enforce!MYXProtocol(a.length, "Supplied byte array is zero length");
33 	TimeDiff td;
34 	uint l = a[0];
35 	enforce!MYXProtocol(l == 0 || l == 5 || l == 8 || l == 12, "Bad Time length in binary row.");
36 	if (l >= 5)
37 	{
38 		td.negative = (a[1]  != 0);
39 		td.days     = (a[5] << 24) + (a[4] << 16) + (a[3] << 8) + a[2];
40 	}
41 	if (l >= 8)
42 	{
43 		td.hours    = a[6];
44 		td.minutes  = a[7];
45 		td.seconds  = a[8];
46 	}
47 	// Note that the fractional seconds part is not stored by MySQL
48 	return td;
49 }
50 
51 /++
52 Function to extract a time difference from a text encoded column value.
53 
54 Text representations of a time difference are like -750:12:02 - 750 hours
55 12 minutes and two seconds ago.
56 
57 Params: s = A string representation of the time difference.
58 Returns: A populated or default initialized TimeDiff struct.
59 +/
60 TimeDiff toTimeDiff(string s)
61 {
62 	TimeDiff td;
63 	int t = parse!int(s);
64 	if (t < 0)
65 	{
66 		td.negative = true;
67 		t = -t;
68 	}
69 	td.hours    = cast(ubyte) t%24;
70 	td.days     = cast(ubyte) t/24;
71 	enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
72 	td.minutes  = parse!ubyte(s);
73 	enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
74 	td.seconds  = parse!ubyte(s);
75 	return td;
76 }
77 
78 /++
79 Function to extract a TimeOfDay from a binary encoded row.
80 
81 Time/date structures are packed by the server into a byte sub-packet
82 with a leading length byte, and a minimal number of bytes to embody the data.
83 
84 Params: a = slice of a protocol packet beginning at the length byte for a
85             chunk of time data.
86 Returns: A populated or default initialized std.datetime.TimeOfDay struct.
87 +/
88 TimeOfDay toTimeOfDay(in ubyte[] a) pure
89 {
90 	enforce!MYXProtocol(a.length, "Supplied byte array is zero length");
91 	uint l = a[0];
92 	enforce!MYXProtocol(l == 0 || l == 5 || l == 8 || l == 12, "Bad Time length in binary row.");
93 	enforce!MYXProtocol(l >= 8, "Time column value is not in a time-of-day format");
94 
95 	TimeOfDay tod;
96 	tod.hour    = a[6];
97 	tod.minute  = a[7];
98 	tod.second  = a[8];
99 	return tod;
100 }
101 
102 /++
103 Function to extract a TimeOfDay from a text encoded column value.
104 
105 Text representations of a time of day are as in 14:22:02
106 
107 Params: s = A string representation of the time.
108 Returns: A populated or default initialized std.datetime.TimeOfDay struct.
109 +/
110 TimeOfDay toTimeOfDay(const(char)[] s)
111 {
112 	TimeOfDay tod;
113 	tod.hour = parse!int(s);
114 	enforce!MYXProtocol(tod.hour <= 24 && tod.hour >= 0, "Time column value is in time difference form");
115 	enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
116 	tod.minute = parse!ubyte(s);
117 	enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
118 	tod.second = parse!ubyte(s);
119 	return tod;
120 }
121 
122 /++
123 Function to pack a TimeOfDay into a binary encoding for transmission to the server.
124 
125 Time/date structures are packed into a string of bytes with a leading length
126 byte, and a minimal number of bytes to embody the data.
127 
128 Params: tod = TimeOfDay struct.
129 Returns: Packed ubyte[].
130 +/
131 ubyte[] pack(in TimeOfDay tod) pure nothrow
132 {
133 	ubyte[] rv;
134 	if (tod == TimeOfDay.init)
135 	{
136 		rv.length = 1;
137 		rv[0] = 0;
138 	}
139 	else
140 	{
141 		rv.length = 9;
142 		rv[0] = 8;
143 		rv[6] = tod.hour;
144 		rv[7] = tod.minute;
145 		rv[8] = tod.second;
146 	}
147 	return rv;
148 }
149 
150 /++
151 Function to extract a Date from a binary encoded row.
152 
153 Time/date structures are packed by the server into a byte sub-packet
154 with a leading length byte, and a minimal number of bytes to embody the data.
155 
156 Params: a = slice of a protocol packet beginning at the length byte for a
157             chunk of Date data.
158 Returns: A populated or default initialized `std.datetime.Date` struct.
159 +/
160 Date toDate(in ubyte[] a) pure
161 {
162 	enforce!MYXProtocol(a.length, "Supplied byte array is zero length");
163 	if (a[0] == 0)
164 		return Date(0,0,0);
165 
166 	enforce!MYXProtocol(a[0] >= 4, "Binary date representation is too short");
167 	int year    = (a[2]  << 8) + a[1];
168 	int month   = cast(int) a[3];
169 	int day     = cast(int) a[4];
170 	return Date(year, month, day);
171 }
172 
173 /++
174 Function to extract a Date from a text encoded column value.
175 
176 Text representations of a Date are as in 2011-11-11
177 
178 Params: s = A string representation of the time difference.
179 Returns: A populated or default initialized `std.datetime.Date` struct.
180 +/
181 Date toDate(const(char)[] s)
182 {
183 	int year = parse!(ushort)(s);
184 	enforce!MYXProtocol(s.skipOver("-"), `Expected: "-"`);
185 	int month = parse!(ubyte)(s);
186 	enforce!MYXProtocol(s.skipOver("-"), `Expected: "-"`);
187 	int day = parse!(ubyte)(s);
188 	return Date(year, month, day);
189 }
190 
191 /++
192 Function to pack a Date into a binary encoding for transmission to the server.
193 
194 Time/date structures are packed into a string of bytes with a leading length
195 byte, and a minimal number of bytes to embody the data.
196 
197 Params: dt = `std.datetime.Date` struct.
198 Returns: Packed ubyte[].
199 +/
200 ubyte[] pack(in Date dt) pure nothrow
201 {
202 	ubyte[] rv;
203 	if (dt.year < 0)
204 	{
205 		rv.length = 1;
206 		rv[0] = 0;
207 	}
208 	else
209 	{
210 		rv.length = 5;
211 		rv[0] = 4;
212 		rv[1] = cast(ubyte) ( dt.year       & 0xff);
213 		rv[2] = cast(ubyte) ((dt.year >> 8) & 0xff);
214 		rv[3] = cast(ubyte)   dt.month;
215 		rv[4] = cast(ubyte)   dt.day;
216 	}
217 	return rv;
218 }
219 
220 /++
221 Function to extract a DateTime from a binary encoded row.
222 
223 Time/date structures are packed by the server into a byte sub-packet
224 with a leading length byte, and a minimal number of bytes to embody the data.
225 
226 Params: a = slice of a protocol packet beginning at the length byte for a
227             chunk of DateTime data
228 Returns: A populated or default initialized `std.datetime.DateTime` struct.
229 +/
230 DateTime toDateTime(in ubyte[] a) pure
231 {
232 	enforce!MYXProtocol(a.length, "Supplied byte array is zero length");
233 	if (a[0] == 0)
234 		return DateTime();
235 
236 	enforce!MYXProtocol(a[0] >= 4, "Supplied ubyte[] is not long enough");
237 	int year    = (a[2] << 8) + a[1];
238 	int month   =  a[3];
239 	int day     =  a[4];
240 	DateTime dt;
241 	if (a[0] == 4)
242 	{
243 		dt = DateTime(year, month, day);
244 	}
245 	else
246 	{
247 		enforce!MYXProtocol(a[0] >= 7, "Supplied ubyte[] is not long enough");
248 		int hour    = a[5];
249 		int minute  = a[6];
250 		int second  = a[7];
251 		dt = DateTime(year, month, day, hour, minute, second);
252 	}
253 	return dt;
254 }
255 
256 /++
257 Function to extract a DateTime from a text encoded column value.
258 
259 Text representations of a DateTime are as in 2011-11-11 12:20:02
260 
261 Params: s = A string representation of the time difference.
262 Returns: A populated or default initialized `std.datetime.DateTime` struct.
263 +/
264 DateTime toDateTime(const(char)[] s)
265 {
266 	int year = parse!(ushort)(s);
267 	enforce!MYXProtocol(s.skipOver("-"), `Expected: "-"`);
268 	int month = parse!(ubyte)(s);
269 	enforce!MYXProtocol(s.skipOver("-"), `Expected: "-"`);
270 	int day = parse!(ubyte)(s);
271 	enforce!MYXProtocol(s.skipOver(" "), `Expected: " "`);
272 	int hour = parse!(ubyte)(s);
273 	enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
274 	int minute = parse!(ubyte)(s);
275 	enforce!MYXProtocol(s.skipOver(":"), `Expected: ":"`);
276 	int second = parse!(ubyte)(s);
277 	return DateTime(year, month, day, hour, minute, second);
278 }
279 
280 /++
281 Function to extract a DateTime from a ulong.
282 
283 This is used to support the TimeStamp  struct.
284 
285 Params: x = A ulong e.g. 20111111122002UL.
286 Returns: A populated `std.datetime.DateTime` struct.
287 +/
288 DateTime toDateTime(ulong x)
289 {
290 	int second = cast(int) (x%100);
291 	x /= 100;
292 	int minute = cast(int) (x%100);
293 	x /= 100;
294 	int hour   = cast(int) (x%100);
295 	x /= 100;
296 	int day    = cast(int) (x%100);
297 	x /= 100;
298 	int month  = cast(int) (x%100);
299 	x /= 100;
300 	int year   = cast(int) (x%10000);
301 	// 2038-01-19 03:14:07
302 	enforce!MYXProtocol(year >= 1970 &&  year < 2039, "Date/time out of range for 2 bit timestamp");
303 	enforce!MYXProtocol(year != 2038 || (month < 1 && day < 19 && hour < 3 && minute < 14 && second < 7),
304 			"Date/time out of range for 2 bit timestamp");
305 	return DateTime(year, month, day, hour, minute, second);
306 }
307 
308 /++
309 Function to pack a DateTime into a binary encoding for transmission to the server.
310 
311 Time/date structures are packed into a string of bytes with a leading length byte,
312 and a minimal number of bytes to embody the data.
313 
314 Params: dt = `std.datetime.DateTime` struct.
315 Returns: Packed ubyte[].
316 +/
317 ubyte[] pack(in DateTime dt) pure nothrow
318 {
319 	uint len = 1;
320 	if (dt.year || dt.month || dt.day) len = 5;
321 	if (dt.hour || dt.minute|| dt.second) len = 8;
322 	ubyte[] rv;
323 	rv.length = len;
324 	rv[0] =  cast(ubyte)(rv.length - 1); // num bytes
325 	if(len >= 5)
326 	{
327 		rv[1] = cast(ubyte) ( dt.year       & 0xff);
328 		rv[2] = cast(ubyte) ((dt.year >> 8) & 0xff);
329 		rv[3] = cast(ubyte)   dt.month;
330 		rv[4] = cast(ubyte)   dt.day;
331 	}
332 	if(len == 8)
333 	{
334 		rv[5] = cast(ubyte) dt.hour;
335 		rv[6] = cast(ubyte) dt.minute;
336 		rv[7] = cast(ubyte) dt.second;
337 	}
338 	return rv;
339 }
340 
341 
342 T consume(T)(MySQLSocket conn) pure {
343 	ubyte[T.sizeof] buffer;
344 	conn.read(buffer);
345 	ubyte[] rng = buffer;
346 	return consume!T(rng);
347 }
348 
349 string consume(T:string, ubyte N=T.sizeof)(ref ubyte[] packet) pure
350 {
351 	return packet.consume!string(N);
352 }
353 
354 string consume(T:string)(ref ubyte[] packet, size_t N) pure
355 in
356 {
357 	assert(packet.length >= N);
358 }
359 do
360 {
361 	auto result = packet.consume(N);
362 	return (() @trusted => cast(string)result)();
363 }
364 
365 /// Returns N number of bytes from the packet and advances the array
366 ubyte[] consume()(ref ubyte[] packet, size_t N) pure nothrow
367 in
368 {
369 	assert(packet.length >= N);
370 }
371 do
372 {
373 	auto result = packet[0..N];
374 	packet = packet[N..$];
375 	return result;
376 }
377 
378 T decode(T:ulong)(in ubyte[] packet, size_t n) pure nothrow
379 {
380 	switch(n)
381 	{
382 		case 8: return packet.decode!(T, 8)();
383 		case 4: return packet.decode!(T, 4)();
384 		case 3: return packet.decode!(T, 3)();
385 		case 2: return packet.decode!(T, 2)();
386 		case 1: return packet.decode!(T, 1)();
387 		default: assert(0);
388 	}
389 }
390 
391 T consume(T)(ref ubyte[] packet, int n) pure nothrow
392 if(isIntegral!T)
393 {
394 	switch(n)
395 	{
396 		case 8: return packet.consume!(T, 8)();
397 		case 4: return packet.consume!(T, 4)();
398 		case 3: return packet.consume!(T, 3)();
399 		case 2: return packet.consume!(T, 2)();
400 		case 1: return packet.consume!(T, 1)();
401 		default: assert(0);
402 	}
403 }
404 
405 TimeOfDay consume(T:TimeOfDay, ubyte N=T.sizeof)(ref ubyte[] packet) pure
406 in
407 {
408 	static assert(N == T.sizeof);
409 }
410 do
411 {
412 	enforce!MYXProtocol(packet.length, "Supplied byte array is zero length");
413 	uint length = packet.front;
414 	enforce!MYXProtocol(length == 0 || length == 5 || length == 8 || length == 12, "Bad Time length in binary row.");
415 	enforce!MYXProtocol(length >= 8, "Time column value is not in a time-of-day format");
416 
417 	packet.popFront(); // length
418 	auto bytes = packet.consume(length);
419 
420 	// TODO: What are the fields in between!?! Blank Date?
421 	TimeOfDay tod;
422 	tod.hour    = bytes[5];
423 	tod.minute  = bytes[6];
424 	tod.second  = bytes[7];
425 	return tod;
426 }
427 
428 Date consume(T:Date, ubyte N=T.sizeof)(ref ubyte[] packet) pure
429 in
430 {
431 	static assert(N == T.sizeof);
432 }
433 do
434 {
435 	return toDate(packet.consume(5));
436 }
437 
438 DateTime consume(T:DateTime, ubyte N=T.sizeof)(ref ubyte[] packet) pure
439 in
440 {
441 	assert(packet.length);
442 	assert(N == T.sizeof);
443 }
444 do
445 {
446 	auto numBytes = packet.consume!ubyte();
447 	if(numBytes == 0)
448 		return DateTime();
449 
450 	enforce!MYXProtocol(numBytes >= 4, "Supplied packet is not large enough to store DateTime");
451 
452 	int year    = packet.consume!ushort();
453 	int month   = packet.consume!ubyte();
454 	int day     = packet.consume!ubyte();
455 	int hour    = 0;
456 	int minute  = 0;
457 	int second  = 0;
458 	if(numBytes > 4)
459 	{
460 		enforce!MYXProtocol(numBytes >= 7, "Supplied packet is not large enough to store a DateTime with TimeOfDay");
461 		hour    = packet.consume!ubyte();
462 		minute  = packet.consume!ubyte();
463 		second  = packet.consume!ubyte();
464 	}
465 	return DateTime(year, month, day, hour, minute, second);
466 }
467 
468 
469 @property bool hasEnoughBytes(T, ubyte N=T.sizeof)(in ubyte[] packet) pure
470 in
471 {
472 	static assert(T.sizeof >= N, T.stringof~" not large enough to store "~to!string(N)~" bytes");
473 }
474 do
475 {
476 	return packet.length >= N;
477 }
478 
479 T decode(T, ubyte N=T.sizeof)(in ubyte[] packet) pure nothrow
480 if(isIntegral!T)
481 in
482 {
483 	static assert(N == 1 || N == 2 || N == 3 || N == 4 || N == 8, "Cannot decode integral value. Invalid size: "~N.stringof);
484 	static assert(T.sizeof >= N, T.stringof~" not large enough to store "~to!string(N)~" bytes");
485 	assert(packet.hasEnoughBytes!(T,N), "packet not long enough to contain all bytes needed for "~T.stringof);
486 }
487 do
488 {
489 	T value = 0;
490 	static if(N == 8) // 64 bit
491 	{
492 		value |= cast(T)(packet[7]) << (8*7);
493 		value |= cast(T)(packet[6]) << (8*6);
494 		value |= cast(T)(packet[5]) << (8*5);
495 		value |= cast(T)(packet[4]) << (8*4);
496 	}
497 	static if(N >= 4) // 32 bit
498 	{
499 		value |= cast(T)(packet[3]) << (8*3);
500 	}
501 	static if(N >= 3) // 24 bit
502 	{
503 		value |= cast(T)(packet[2]) << (8*2);
504 	}
505 	static if(N >= 2) // 16 bit
506 	{
507 		value |= cast(T)(packet[1]) << (8*1);
508 	}
509 	static if(N >= 1) // 8 bit
510 	{
511 		value |= cast(T)(packet[0]) << (8*0);
512 	}
513 	return value;
514 }
515 
516 T consume(T, ubyte N=T.sizeof)(ref ubyte[] packet) pure nothrow
517 if(isIntegral!T)
518 in
519 {
520 	static assert(N == 1 || N == 2 || N == 3 || N == 4 || N == 8, "Cannot consume integral value. Invalid size: "~N.stringof);
521 	static assert(T.sizeof >= N, T.stringof~" not large enough to store "~to!string(N)~" bytes");
522 	assert(packet.hasEnoughBytes!(T,N), "packet not long enough to contain all bytes needed for "~T.stringof);
523 }
524 do
525 {
526 	// The uncommented line triggers a template deduction error,
527 	// so we need to store a temporary first
528 	// could the problem be method chaining?
529 	//return packet.consume(N).decode!(T, N)();
530 	auto bytes = packet.consume(N);
531 	return bytes.decode!(T, N)();
532 }
533 
534 
535 T myto(T)(const(char)[] value)
536 {
537 	static if(is(T == DateTime))
538 		return toDateTime(value);
539 	else static if(is(T == Date))
540 		return toDate(value);
541 	else static if(is(T == TimeOfDay))
542 		return toTimeOfDay(value);
543 	else
544 		return to!T(value);
545 }
546 
547 T decode(T, ubyte N=T.sizeof)(in ubyte[] packet) pure nothrow @trusted
548 if(isFloatingPoint!T)
549 in
550 {
551 	static assert((is(T == float) && N == float.sizeof)
552 			|| is(T == double) && N == double.sizeof);
553 }
554 do
555 {
556 	T result = 0;
557 	(cast(ubyte*)&result)[0..T.sizeof] = packet[0..T.sizeof];
558 	return result;
559 }
560 
561 T consume(T, ubyte N=T.sizeof)(ref ubyte[] packet) pure nothrow
562 if(isFloatingPoint!T)
563 in
564 {
565 	static assert((is(T == float) && N == float.sizeof)
566 			|| is(T == double) && N == double.sizeof);
567 }
568 do
569 {
570 	return packet.consume(T.sizeof).decode!T();
571 }
572 
573 
574 SQLValue consumeBinaryValueIfComplete(T, int N=T.sizeof)(ref ubyte[] packet, bool unsigned)
575 {
576 	SQLValue result;
577 
578 	// Length of DateTime packet is NOT DateTime.sizeof, it can be 1, 5 or 8 bytes
579 	static if(is(T==DateTime))
580 		result.isIncomplete = packet.length < 1;
581 	else
582 		result.isIncomplete = packet.length < N;
583 
584 	// isNull should have been handled by the caller as the binary format uses a
585 	// null bitmap, and we don't have access to that information at this point
586 	assert(!result.isNull);
587 	if(!result.isIncomplete)
588 	{
589 		// only integral types is unsigned
590 		static if(isIntegral!T)
591 		{
592 			if(unsigned)
593 				result.value = packet.consume!(Unsigned!T)();
594 			else
595 				result.value = packet.consume!(Signed!T)();
596 		}
597 		else
598 		{
599 			// TODO: DateTime values etc might be incomplete!
600 			result.value = packet.consume!(T, N)();
601 		}
602 	}
603 	return result;
604 }
605 
606 SQLValue consumeNonBinaryValueIfComplete(T)(ref ubyte[] packet, bool unsigned)
607 {
608 	SQLValue result;
609 	auto lcb = packet.decode!LCB();
610 	result.isIncomplete = lcb.isIncomplete || packet.length < (lcb.value+lcb.totalBytes);
611 	result.isNull = lcb.isNull;
612 	if(!result.isIncomplete)
613 	{
614 		// The packet has all the data we need, so we'll remove the LCB
615 		// and convert the data
616 		packet.skip(lcb.totalBytes);
617 		assert(packet.length >= lcb.value);
618 		auto value = cast(char[]) packet.consume(cast(size_t)lcb.value);
619 
620 		if(!result.isNull)
621 		{
622 			assert(!result.isIncomplete);
623 			assert(!result.isNull);
624 			static if(isIntegral!T)
625 			{
626 				if(unsigned)
627 					result.value = to!(Unsigned!T)(value);
628 				else
629 					result.value = to!(Signed!T)(value);
630 			}
631 			else
632 			{
633 				result.value = myto!T(value);
634 			}
635 		}
636 	}
637 	return result;
638 }
639 
640 SQLValue consumeIfComplete(T, int N=T.sizeof)(ref ubyte[] packet, bool binary, bool unsigned)
641 {
642 	return binary
643 		? packet.consumeBinaryValueIfComplete!(T, N)(unsigned)
644 		: packet.consumeNonBinaryValueIfComplete!T(unsigned);
645 }
646 
647 SQLValue consumeIfComplete()(ref ubyte[] packet, SQLType sqlType, bool binary, bool unsigned, ushort charSet)
648 {
649 	switch(sqlType)
650 	{
651 		default: assert(false, "Unsupported SQL type "~to!string(sqlType));
652 		case SQLType.NULL:
653 			SQLValue result;
654 			result.isIncomplete = false;
655 			result.isNull = true;
656 			return result;
657 		case SQLType.TINY:
658 			return packet.consumeIfComplete!byte(binary, unsigned);
659 		case SQLType.SHORT:
660 			return packet.consumeIfComplete!short(binary, unsigned);
661 		case SQLType.INT24:
662 			return packet.consumeIfComplete!(int, 3)(binary, unsigned);
663 		case SQLType.INT:
664 			return packet.consumeIfComplete!int(binary, unsigned);
665 		case SQLType.LONGLONG:
666 			return packet.consumeIfComplete!long(binary, unsigned);
667 		case SQLType.FLOAT:
668 			return packet.consumeIfComplete!float(binary, unsigned);
669 		case SQLType.DOUBLE:
670 			return packet.consumeIfComplete!double(binary, unsigned);
671 		case SQLType.TIMESTAMP:
672 			return packet.consumeIfComplete!DateTime(binary, unsigned);
673 		case SQLType.TIME:
674 			return packet.consumeIfComplete!TimeOfDay(binary, unsigned);
675 		case SQLType.YEAR:
676 			return packet.consumeIfComplete!ushort(binary, unsigned);
677 		case SQLType.DATE:
678 			return packet.consumeIfComplete!Date(binary, unsigned);
679 		case SQLType.DATETIME:
680 			return packet.consumeIfComplete!DateTime(binary, unsigned);
681 		case SQLType.VARCHAR:
682 		case SQLType.ENUM:
683 		case SQLType.SET:
684 		case SQLType.VARSTRING:
685 		case SQLType.STRING:
686 		case SQLType.NEWDECIMAL:
687 			return packet.consumeIfComplete!string(false, unsigned);
688 		case SQLType.TINYBLOB:
689 		case SQLType.MEDIUMBLOB:
690 		case SQLType.BLOB:
691 		case SQLType.LONGBLOB:
692 		case SQLType.BIT:  // Yes, BIT. See note below.
693 
694 			// TODO: This line should work. Why doesn't it?
695 			//return packet.consumeIfComplete!(ubyte[])(binary, unsigned);
696 
697 			auto lcb = packet.consumeIfComplete!LCB();
698 			assert(!lcb.isIncomplete);
699 			SQLValue result;
700 			result.isIncomplete = false;
701 			result.isNull = lcb.isNull;
702 			if(result.isNull)
703 			{
704 				// TODO: consumeIfComplete!LCB should be adjusted to do
705 				//       this itself, but not until I'm certain that nothing
706 				//       is reliant on the current behavior.
707 				packet.popFront(); // LCB length
708 			}
709 			else
710 			{
711 				auto data = packet.consume(cast(size_t)lcb.value);
712 				if(charSet == 0x3F) // CharacterSet == binary
713 					result.value = data; // BLOB-ish
714 				else
715 					result.value = (() @trusted => cast(string)data)(); // TEXT-ish
716 			}
717 
718 			// Type BIT is treated as a length coded binary (like a BLOB or VARCHAR),
719 			// not like an integral type. So convert the binary data to a bool.
720 			// See: http://dev.mysql.com/doc/internals/en/binary-protocol-value.html
721 			if(sqlType == SQLType.BIT)
722 			{
723 				enforce!MYXProtocol(result.value.length == 1,
724 					"Expected BIT to arrive as an LCB with length 1, but got length "~to!string(result.value.length));
725 
726 				result.value = result.value[0] == 1;
727 			}
728 
729 			return result;
730 	}
731 }
732 
733 /++
734 Extract number of bytes used for this LCB
735 
736 Returns the number of bytes required to store this LCB
737 
738 See_Also: $(LINK http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Elements)
739 
740 Returns: 0 if it's a null value, or number of bytes in other cases
741 +/
742 byte getNumLCBBytes(in ubyte lcbHeader) pure nothrow
743 {
744 	switch(lcbHeader)
745 	{
746 		case 251: return 0; // null
747 		case 0: .. case 250: return 1; // 8-bit
748 		case 252: return 2;  // 16-bit
749 		case 253: return 3;  // 24-bit
750 		case 254: return 8;  // 64-bit
751 
752 		case 255:
753 		default:
754 			assert(0);
755 	}
756 	assert(0);
757 }
758 
759 
760 /++
761 Decodes a Length Coded Binary from a packet
762 
763 See_Also: struct `mysql.protocol.extra_types.LCB`
764 
765 Params:
766 packet = A packet that starts with a LCB. The bytes is popped off
767          iff the packet is complete. See `mysql.protocol.extra_types.LCB`.
768 
769 Returns: A decoded LCB value
770 +/
771 T consumeIfComplete(T:LCB)(ref ubyte[] packet) pure nothrow
772 in
773 {
774 	assert(packet.length >= 1, "packet has to include at least the LCB length byte");
775 }
776 do
777 {
778 	auto lcb = packet.decodeLCBHeader();
779 	if(lcb.isNull || lcb.isIncomplete)
780 		return lcb;
781 
782 	if(lcb.numBytes > 1)
783 	{
784 		// We know it's complete, so we have to start consuming the LCB
785 		// Single byte values doesn't have a length
786 		packet.popFront(); // LCB length
787 	}
788 
789 	assert(packet.length >= lcb.numBytes);
790 
791 	lcb.value = packet.consume!ulong(lcb.numBytes);
792 	return lcb;
793 }
794 
795 LCB decodeLCBHeader(in ubyte[] packet) pure nothrow
796 in
797 {
798 	assert(packet.length >= 1, "packet has to include at least the LCB length byte");
799 }
800 do
801 {
802 	LCB lcb;
803 	lcb.numBytes = getNumLCBBytes(packet.front);
804 	if(lcb.numBytes == 0)
805 	{
806 		lcb.isNull = true;
807 		return lcb;
808 	}
809 
810 	assert(!lcb.isNull);
811 	// -1 for LCB length as we haven't popped it off yet
812 	lcb.isIncomplete = (lcb.numBytes > 1) && (packet.length-1 < lcb.numBytes);
813 	if(lcb.isIncomplete)
814 	{
815 		// Not enough bytes to store data. We don't remove any data, and expect
816 		// the caller to check isIncomplete and take action to fetch more data
817 		// and call this method at a later time
818 		return lcb;
819 	}
820 
821 	assert(!lcb.isIncomplete);
822 	return lcb;
823 }
824 
825 /++
826 Decodes a Length Coded Binary from a packet
827 
828 See_Also: struct `mysql.protocol.extra_types.LCB`
829 
830 Params:
831 packet = A packet that starts with a LCB. See `mysql.protocol.extra_types.LCB`.
832 
833 Returns: A decoded LCB value
834 +/
835 LCB decode(T:LCB)(in ubyte[] packet) pure nothrow
836 in
837 {
838 	assert(packet.length >= 1, "packet has to include at least the LCB length byte");
839 }
840 do
841 {
842 	auto lcb = packet.decodeLCBHeader();
843 	if(lcb.isNull || lcb.isIncomplete)
844 		return lcb;
845 	assert(packet.length >= lcb.totalBytes);
846 
847 	if(lcb.numBytes == 0)
848 		lcb.value = 0;
849 	else if(lcb.numBytes == 1)
850 		lcb.value = packet.decode!ulong(lcb.numBytes);
851 	else
852 	{
853 		// Skip the throwaway byte that indicated "at least 2 more bytes coming"
854 		lcb.value = packet[1..$].decode!ulong(lcb.numBytes);
855 	}
856 
857 	return lcb;
858 }
859 
860 /++
861 Parse Length Coded String
862 
863 See_Also: $(LINK http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Elements)
864 +/
865 string consume(T:LCS)(ref ubyte[] packet) pure
866 in
867 {
868 	assert(packet.length >= 1, "LCS packet needs to store at least the LCB header");
869 }
870 do
871 {
872 	auto lcb = packet.consumeIfComplete!LCB();
873 	assert(!lcb.isIncomplete);
874 	if(lcb.isNull)
875 		return null;
876 	enforce!MYXProtocol(lcb.value <= uint.max, "Protocol Length Coded String is too long");
877 	return cast(string)packet.consume(cast(size_t)lcb.value).idup;
878 }
879 
880 /++
881 Skips over n items, advances the array, and return the newly advanced array
882 to allow method chaining.
883 +/
884 T[] skip(T)(ref T[] array, size_t n) pure nothrow
885 in
886 {
887 	assert(n <= array.length);
888 }
889 do
890 {
891 	array = array[n..$];
892 	return array;
893 }
894 
895 /++
896 Converts a value into a sequence of bytes and fills the supplied array.
897 
898 Params:
899 IsInt24 = If only the most significant 3 bytes from the value should be used.
900 value = The value to add to array.
901 array = The array we should add the values for. It has to be large enough,
902         and the values are packed starting index 0.
903 +/
904 void packInto(T, bool IsInt24 = false)(T value, ubyte[] array) pure nothrow
905 in
906 {
907 	static if(IsInt24)
908 		assert(array.length >= 3);
909 	else
910 		assert(array.length >= T.sizeof, "Not enough space to unpack "~T.stringof);
911 }
912 do
913 {
914 	static if(T.sizeof >= 1)
915 	{
916 		array[0] = cast(ubyte) (value >> 8*0) & 0xff;
917 	}
918 	static if(T.sizeof >= 2)
919 	{
920 		array[1] = cast(ubyte) (value >> 8*1) & 0xff;
921 	}
922 	static if(!IsInt24)
923 	{
924 		static if(T.sizeof >= 4)
925 		{
926 			array[2] = cast(ubyte) (value >> 8*2) & 0xff;
927 			array[3] = cast(ubyte) (value >> 8*3) & 0xff;
928 		}
929 		static if(T.sizeof >= 8)
930 		{
931 			array[4] = cast(ubyte) (value >> 8*4) & 0xff;
932 			array[5] = cast(ubyte) (value >> 8*5) & 0xff;
933 			array[6] = cast(ubyte) (value >> 8*6) & 0xff;
934 			array[7] = cast(ubyte) (value >> 8*7) & 0xff;
935 		}
936 	}
937 	else
938 	{
939 		array[2] = cast(ubyte) (value >> 8*2) & 0xff;
940 	}
941 }
942 
943 ubyte[] packLength(size_t l, out size_t offset) pure nothrow
944 out(result)
945 {
946 	assert(result.length >= 1);
947 }
948 do
949 {
950 	ubyte[] t;
951 	if (!l)
952 	{
953 		t.length = 1;
954 		t[0] = 0;
955 	}
956 	else if (l <= 250)
957 	{
958 		t.length = 1+l;
959 		t[0] = cast(ubyte) l;
960 		offset = 1;
961 	}
962 	else if (l <= 0xffff) // 16-bit
963 	{
964 		t.length = 3+l;
965 		t[0] = 252;
966 		packInto(cast(ushort)l, t[1..3]);
967 		offset = 3;
968 	}
969 	else if (l < 0xffffff) // 24-bit
970 	{
971 		t.length = 4+l;
972 		t[0] = 253;
973 		packInto!(uint, true)(cast(uint)l, t[1..4]);
974 		offset = 4;
975 	}
976 	else // 64-bit
977 	{
978 		ulong u = cast(ulong) l;
979 		t.length = 9+l;
980 		t[0] = 254;
981 		u.packInto(t[1..9]);
982 		offset = 9;
983 	}
984 	return t;
985 }
986 
987 ubyte[] packLCS(const(void)[] a) pure nothrow
988 {
989 	size_t offset;
990 	ubyte[] t = packLength(a.length, offset);
991 	if (t[0])
992 		t[offset..$] = (cast(const(ubyte)[]) a)[0..$];
993 	return t;
994 }
995 
996 @("testLCB")
997 debug(MYSQLN_TESTS)
998 unittest
999 {
1000 	static void testLCB(string parseLCBFunc)(bool shouldConsume)
1001 	{
1002 		ubyte[] buf = [ 0xde, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x01, 0x00 ];
1003 		ubyte[] bufCopy;
1004 
1005 		bufCopy = buf;
1006 		LCB lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1007 		assert(lcb.value == 0xde && !lcb.isNull && lcb.totalBytes == 1);
1008 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1009 
1010 		buf[0] = 251;
1011 		bufCopy = buf;
1012 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1013 		assert(lcb.value == 0 && lcb.isNull && lcb.totalBytes == 1);
1014 		//TODO: This test seems to fail for consumeIfComplete, need to investigate.
1015 		//      Don't know if fixing it might cause a problem, or if I simple misunderstood
1016 		//      the function's intent.
1017 		if(parseLCBFunc != "consumeIfComplete")
1018 			assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1019 
1020 		buf[0] = 252;
1021 		bufCopy = buf;
1022 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1023 		assert(lcb.value == 0xbbcc && !lcb.isNull && lcb.totalBytes == 3);
1024 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1025 
1026 		buf[0] = 253;
1027 		bufCopy = buf;
1028 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1029 		assert(lcb.value == 0xaabbcc && !lcb.isNull && lcb.totalBytes == 4);
1030 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1031 
1032 		buf[0] = 254;
1033 		bufCopy = buf;
1034 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1035 		assert(lcb.value == 0x5566778899aabbcc && !lcb.isNull && lcb.totalBytes == 9);
1036 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1037 	}
1038 
1039 	//TODO: Merge 'consumeIfComplete(T:LCB)' and 'decode(T:LCB)', they do
1040 	//      basically the same thing, only one consumes input and the other
1041 	//      doesn't. Just want a better idea of where/how/why they're both
1042 	//      used, and maybe more tests, before I go messing with them.
1043 	testLCB!"consumeIfComplete"(true);
1044 	testLCB!"decode"(false);
1045 }
1046 
1047 @("consume!LCS")
1048 debug(MYSQLN_TESTS)
1049 unittest
1050 {
1051 	ubyte[] buf;
1052 	ubyte[] bufCopy;
1053 
1054 	buf.length = 0x2000200;
1055 	buf[] = '\x01';
1056 	buf[0] = 250;
1057 	buf[1] = '<';
1058 	buf[249] = '!';
1059 	buf[250] = '>';
1060 	bufCopy = buf;
1061 	string x = consume!LCS(bufCopy);
1062 	assert(x.length == 250 && x[0] == '<' && x[249] == '>');
1063 
1064 	buf[] = '\x01';
1065 	buf[0] = 252;
1066 	buf[1] = 0xff;
1067 	buf[2] = 0xff;
1068 	buf[3] = '<';
1069 	buf[0x10000] = '*';
1070 	buf[0x10001] = '>';
1071 	bufCopy = buf;
1072 	x = consume!LCS(bufCopy);
1073 	assert(x.length == 0xffff && x[0] == '<' && x[0xfffe] == '>');
1074 
1075 	buf[] = '\x01';
1076 	buf[0] = 253;
1077 	buf[1] = 0xff;
1078 	buf[2] = 0xff;
1079 	buf[3] = 0xff;
1080 	buf[4] = '<';
1081 	buf[0x1000001] = '*';
1082 	buf[0x1000002] = '>';
1083 	bufCopy = buf;
1084 	x = consume!LCS(bufCopy);
1085 	assert(x.length == 0xffffff && x[0] == '<' && x[0xfffffe] == '>');
1086 
1087 	buf[] = '\x01';
1088 	buf[0] = 254;
1089 	buf[1] = 0xff;
1090 	buf[2] = 0x00;
1091 	buf[3] = 0x00;
1092 	buf[4] = 0x02;
1093 	buf[5] = 0x00;
1094 	buf[6] = 0x00;
1095 	buf[7] = 0x00;
1096 	buf[8] = 0x00;
1097 	buf[9] = '<';
1098 	buf[0x2000106] = '!';
1099 	buf[0x2000107] = '>';
1100 	bufCopy = buf;
1101 	x = consume!LCS(bufCopy);
1102 	assert(x.length == 0x20000ff && x[0] == '<' && x[0x20000fe] == '>');
1103 }
1104 
1105 /// Set packet length and number. It's important that the length of packet has
1106 /// already been set to the final state as its length is used.
1107 void setPacketHeader(ref ubyte[] packet, ubyte packetNumber) pure nothrow
1108 in
1109 {
1110 	// packet should include header, and possibly data
1111 	assert(packet.length >= 4);
1112 }
1113 do
1114 {
1115 	auto dataLength = packet.length - 4; // don't include header in calculated size
1116 	assert(dataLength <= uint.max);
1117 	packet.setPacketHeader(packetNumber, cast(uint)dataLength);
1118 }
1119 
1120 void setPacketHeader(ref ubyte[] packet, ubyte packetNumber, uint dataLength) pure nothrow
1121 in
1122 {
1123 	// packet should include header
1124 	assert(packet.length >= 4);
1125 	// Length is always a 24-bit int
1126 	assert(dataLength <= 0xffff_ffff_ffff);
1127 }
1128 do
1129 {
1130 	dataLength.packInto!(uint, true)(packet);
1131 	packet[3] = packetNumber;
1132 }