1 module hunt.database.driver.mysql.impl.codec.DataTypeCodec;
2 
3 import hunt.database.driver.mysql.impl.codec.DataType;
4 import hunt.database.driver.mysql.impl.codec.ColumnDefinition;
5 
6 import hunt.database.driver.mysql.impl.util.BufferUtils;
7 import hunt.database.base.Numeric;
8 
9 import hunt.Exceptions;
10 import hunt.logging;
11 import hunt.net.buffer.ByteBuf;
12 import hunt.net.Exceptions;
13 import hunt.text.Charset;
14 
15 import std.conv;
16 import std.concurrency : initOnce;
17 import std.variant;
18 
19 /**
20  * 
21  */
22 class DataTypeCodec {
23     // binary codec protocol: https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html#sect_protocol_binary_resultset_row_value
24 
25     // Sentinel used when an object is refused by the data type
26     static Object REFUSED_SENTINEL() {
27         __gshared Object inst;
28         return initOnce!inst(new Object());
29     }
30 
31     // private static final java.time.format.DateTimeFormatter DATETIME_FORMAT = new DateTimeFormatterBuilder()
32     //     .parseCaseInsensitive()
33     //     .append(ISO_LOCAL_DATE)
34     //     .appendLiteral(' ')
35     //     .appendValue(HOUR_OF_DAY, 2)
36     //     .appendLiteral(':')
37     //     .appendValue(MINUTE_OF_HOUR, 2)
38     //     .appendLiteral(':')
39     //     .appendValue(SECOND_OF_MINUTE, 2)
40     //     .appendFraction(MICRO_OF_SECOND, 0, 6, true)
41     //     .toFormatter();
42 
43     static Variant decodeText(DataType dataType, Charset charset, int columnDefinitionFlags, ByteBuf buffer) {
44         int len = cast(int) BufferUtils.readLengthEncodedInteger(buffer);
45         int index = buffer.readerIndex();
46 
47         scope(exit) {
48             buffer.skipBytes(len);
49         }
50 
51         bool isBinaryField = isBinaryField(columnDefinitionFlags);
52 
53         version(HUNT_DB_DEBUG_MORE) {
54             tracef("dataType=%s, index=%d, len=%d, columnDefinitionFlags=%d, isBinaryField=%s", 
55                 dataType, index, len, columnDefinitionFlags, isBinaryField);
56         }
57 
58         string value = "";
59         if (isBinaryField) {
60             byte[] data = textDecodeBlob(index, len, buffer);
61             if(data is null) {
62                 return Variant("");
63             } else {
64                 return Variant(cast(string)data);
65             }
66         } else {
67             value = textDecodeText(charset, index, len, buffer);
68         }
69         
70         version(HUNT_DB_DEBUG_MORE) {
71             byte[] d = new byte[len];
72             for(int i=0; i< len; i++) {
73                 d[i] = buffer.getByte(index + i);
74             }
75             tracef("raw value: %s, bytes: [%(%02X  %)]", value, d);
76         }
77         // ByteBuf data = buffer.readSlice(length);
78         switch (dataType) {
79             case DataType.BIT:
80                 byte v = buffer.getByte(index);
81                 if(v == 1)  return Variant(true);
82                 return Variant(false);
83 
84             case DataType.INT1:
85                 return to!(byte)(value).Variant();
86 
87             case DataType.INT2:
88             case DataType.YEAR:
89                 return to!short(value).Variant();
90 
91             case DataType.INT3:
92             case DataType.INT4:
93                 return to!int(value).Variant();
94 
95             case DataType.INT8:
96                 return to!long(value).Variant();
97 
98             case DataType.FLOAT:
99                 return to!float(value).Variant();
100 
101             case DataType.DOUBLE:
102                 return to!double(value).Variant();
103 
104     //         case DataType.NUMERIC:
105     //             return textDecodeNUMERIC(charset, data).Variant();
106     //         case DataType.DATE:
107     //             return textDecodeDate(charset, data).Variant();
108     //         case DataType.TIME:
109     //             return textDecodeTime(charset, data).Variant();
110     //         case DataType.DATETIME:
111     //         case DataType.TIMESTAMP:
112     //             return textDecodeDateTime(charset, data).Variant();
113             case DataType.STRING:
114             case DataType.VARSTRING:
115             case DataType.BLOB:
116             default:
117                 // if (isBinaryField(columnDefinitionFlags)) {
118                 //     return textDecodeBlob(index, len, buffer).Variant();
119                 // } else {
120                 //     return textDecodeText(charset, index, len, buffer).Variant();
121                 // }
122 
123                 return textDecodeText(charset, index, len, buffer).Variant();
124         }
125     }
126 
127     // //TODO take care of unsigned numeric values here?
128     static void encodeBinary(DataType dataType, Charset charset, ref Variant value, ByteBuf buffer) {
129         switch (dataType) {
130             case DataType.INT1:
131                 byte b = 0;
132                 if (value.type == typeid(bool)) {
133                     if (value.get!bool()) {
134                         b = 1;
135                     } else {
136                         b = 0;
137                     }
138                 } else {
139                     assert(value.type == typeid(byte) || value.type == typeid(ubyte));
140                     b = value.get!byte();
141                 }
142                 buffer.writeByte(b);
143                 break;
144 
145             case DataType.INT2:
146                 assert(value.type == typeid(short) || value.type == typeid(short));
147                 buffer.writeShortLE(value.get!short());
148                 break;
149 
150             case DataType.INT3:
151                 assert(value.type == typeid(int) || value.type == typeid(uint));
152                 buffer.writeMediumLE(value.get!int());
153                 break;
154 
155             case DataType.INT4:
156                 assert(value.type == typeid(int) || value.type == typeid(uint));
157                 buffer.writeIntLE(value.get!int());
158                 break;
159 
160             case DataType.INT8:
161                 assert(value.type == typeid(long) || value.type == typeid(ulong));
162                 buffer.writeLongLE(value.get!int());
163                 break;
164 
165             case DataType.FLOAT:
166                 assert(value.type == typeid(float));
167                 buffer.writeFloatLE(value.get!float());
168                 break;
169 
170             case DataType.DOUBLE:
171                 assert(value.type == typeid(int) || value.type == typeid(int));
172                 buffer.writeDoubleLE(value.get!double());
173                 break;
174     //         case DataType.NUMERIC:
175     //             binaryEncodeNumeric(charset, (Numeric) value, buffer);
176     //             break;
177             case DataType.BLOB:
178                 assert(value.type == typeid(byte[]) || value.type == typeid(ubyte[]));
179                 byte[] data = value.get!(byte[])();
180                 BufferUtils.writeLengthEncodedInteger(buffer, cast(int)data.length);
181                 buffer.writeBytes(data);
182                 break;
183     //         case DataType.DATE:
184     //             binaryEncodeDate((LocalDate) value, buffer);
185     //             break;
186     //         case DataType.TIME:
187     //             binaryEncodeTime((Duration) value, buffer);
188     //             break;
189     //         case DataType.DATETIME:
190     //             binaryEncodeDatetime((LocalDateTime) value, buffer);
191     //             break;
192             case DataType.STRING:
193             case DataType.VARSTRING:
194             default:
195                 // binaryEncodeText(charset, string.valueOf(value), buffer);
196                 assert(value.type == typeid(string) || value.type == typeid(const(char)[])
197                     || value.type == typeid(immutable(char)[]));
198                 BufferUtils.writeLengthEncodedString(buffer, value.get!string(), charset);
199                 break;
200         }
201     }
202 
203     static Variant decodeBinary(DataType dataType, Charset charset, int columnDefinitionFlags, ByteBuf buffer) {
204         
205         switch (dataType) {
206             case DataType.INT1:
207                 return buffer.readByte().Variant();
208             case DataType.YEAR:
209             case DataType.INT2:
210                 return buffer.readShortLE().Variant();
211             case DataType.INT3:
212             case DataType.INT4:
213                 return buffer.readIntLE().Variant();
214             case DataType.INT8:
215                 return buffer.readLongLE().Variant();
216             case DataType.FLOAT:
217                 return buffer.readFloatLE().Variant();
218             case DataType.DOUBLE:
219                 return buffer.readDoubleLE().Variant();
220     //         case DataType.NUMERIC:
221     //             return binaryDecodeNumeric(charset, buffer).Variant();
222     //         case DataType.DATE:
223     //             return binaryDecodeDate(buffer).Variant();
224     //         case DataType.TIME:
225     //             return binaryDecodeTime(buffer).Variant();
226     //         case DataType.DATETIME:
227     //         case DataType.TIMESTAMP:
228     //             return binaryDecodeDatetime(buffer).Variant();
229             case DataType.STRING:
230             case DataType.VARSTRING:
231             case DataType.BLOB:
232             default:
233                 if (isBinaryField(columnDefinitionFlags)) {
234                     return binaryDecodeBlob(buffer).Variant();
235                 } else {
236                     return BufferUtils.readLengthEncodedString(buffer, charset).Variant();
237                 }
238         }
239     }
240 
241     // static Object prepare(DataType type, Object value) {
242     //     switch (type) {
243     //         //TODO handle json + unknown?
244     //         default:
245     //             Class<?> javaType = type.binaryType;
246     //             return value is null || javaType.isInstance(value) ? value : REFUSED_SENTINEL;
247     //     }
248     // }
249 
250     // private static void binaryEncodeInt1(Number value, ByteBuf buffer) {
251     //     buffer.writeByte(value.byteValue());
252     // }
253 
254     // private static void binaryEncodeInt2(Number value, ByteBuf buffer) {
255     //     buffer.writeShortLE(value.intValue());
256     // }
257 
258     // private static void binaryEncodeInt3(Number value, ByteBuf buffer) {
259     //     buffer.writeMediumLE(value.intValue());
260     // }
261 
262     // private static void binaryEncodeInt4(Number value, ByteBuf buffer) {
263     //     buffer.writeIntLE(value.intValue());
264     // }
265 
266     // private static void binaryEncodeInt8(Number value, ByteBuf buffer) {
267     //     buffer.writeLongLE(value.longValue());
268     // }
269 
270     // private static void binaryEncodeFloat(Number value, ByteBuf buffer) {
271     //     buffer.writeFloatLE(value.floatValue());
272     // }
273 
274     // private static void binaryEncodeDouble(Number value, ByteBuf buffer) {
275     //     buffer.writeDoubleLE(value.doubleValue());
276     // }
277 
278     // private static void binaryEncodeNumeric(Charset charset, Numeric value, ByteBuf buffer) {
279     //     BufferUtils.writeLengthEncodedString(buffer, value.toString(), charset);
280     // }
281 
282     // private static void binaryEncodeText(Charset charset, string value, ByteBuf buffer) {
283     //     BufferUtils.writeLengthEncodedString(buffer, value, charset);
284     // }
285 
286     // private static void binaryEncodeBlob(Buffer value, ByteBuf buffer) {
287     //     BufferUtils.writeLengthEncodedInteger(buffer, value.length());
288     //     buffer.writeBytes(value.getByteBuf());
289     // }
290 
291     // private static void binaryEncodeDate(LocalDate value, ByteBuf buffer) {
292     //     buffer.writeByte(4);
293     //     buffer.writeShortLE(value.getYear());
294     //     buffer.writeByte(value.getMonthValue());
295     //     buffer.writeByte(value.getDayOfMonth());
296     // }
297 
298     // private static void binaryEncodeTime(Duration value, ByteBuf buffer) {
299     //     long secondsOfDuration = value.getSeconds();
300     //     int nanosOfDuration = value.getNano();
301     //     if (secondsOfDuration == 0 && nanosOfDuration == 0) {
302     //         buffer.writeByte(0);
303     //         return;
304     //     }
305     //     byte isNegative = 0;
306     //     if (secondsOfDuration < 0) {
307     //         isNegative = 1;
308     //         secondsOfDuration = -secondsOfDuration;
309     //     }
310 
311     //     int days = (int) (secondsOfDuration / 86400);
312     //     int secondsOfADay = (int) (secondsOfDuration % 86400);
313     //     int hour = secondsOfADay / 3600;
314     //     int minute = ((secondsOfADay % 3600) / 60);
315     //     int second = secondsOfADay % 60;
316 
317     //     if (nanosOfDuration == 0) {
318     //         buffer.writeByte(8);
319     //         buffer.writeByte(isNegative);
320     //         buffer.writeIntLE(days);
321     //         buffer.writeByte(hour);
322     //         buffer.writeByte(minute);
323     //         buffer.writeByte(second);
324     //         return;
325     //     }
326 
327     //     int microSecond;
328     //     if (isNegative == 1 && nanosOfDuration > 0) {
329     //         second = second - 1;
330     //         microSecond = (1000_000_000 - nanosOfDuration) / 1000;
331     //     } else {
332     //         microSecond = nanosOfDuration / 1000;
333     //     }
334 
335     //     buffer.writeByte(12);
336     //     buffer.writeByte(isNegative);
337     //     buffer.writeIntLE(days);
338     //     buffer.writeByte(hour);
339     //     buffer.writeByte(minute);
340     //     buffer.writeByte(second);
341     //     buffer.writeIntLE(microSecond);
342     // }
343 
344     // private static void binaryEncodeDatetime(LocalDateTime value, ByteBuf buffer) {
345     //     int year = value.getYear();
346     //     int month = value.getMonthValue();
347     //     int day = value.getDayOfMonth();
348     //     int hour = value.getHour();
349     //     int minute = value.getMinute();
350     //     int second = value.getSecond();
351     //     int microsecond = value.getNano() / 1000;
352 
353     //     // LocalDateTime does not have a zero value of month or day
354     //     if (hour == 0 && minute == 0 && second == 0 && microsecond == 0) {
355     //         buffer.writeByte(4);
356     //         buffer.writeShortLE(year);
357     //         buffer.writeByte(month);
358     //         buffer.writeByte(day);
359     //     } else if (microsecond == 0) {
360     //         buffer.writeByte(7);
361     //         buffer.writeShortLE(year);
362     //         buffer.writeByte(month);
363     //         buffer.writeByte(day);
364     //         buffer.writeByte(hour);
365     //         buffer.writeByte(minute);
366     //         buffer.writeByte(second);
367     //     } else {
368     //         buffer.writeByte(11);
369     //         buffer.writeShortLE(year);
370     //         buffer.writeByte(month);
371     //         buffer.writeByte(day);
372     //         buffer.writeByte(hour);
373     //         buffer.writeByte(minute);
374     //         buffer.writeByte(second);
375     //         buffer.writeIntLE(microsecond);
376     //     }
377     // }
378 
379     // private static Byte binaryDecodeInt1(ByteBuf buffer) {
380     //     return buffer.readByte();
381     // }
382 
383     // private static Short binaryDecodeInt2(ByteBuf buffer) {
384     //     return buffer.readShortLE();
385     // }
386 
387     // private static Integer binaryDecodeInt3(ByteBuf buffer) {
388     //     return buffer.readIntLE();
389     // }
390 
391     // private static Integer binaryDecodeInt4(ByteBuf buffer) {
392     //     return buffer.readIntLE();
393     // }
394 
395     // private static Long binaryDecodeInt8(ByteBuf buffer) {
396     //     return buffer.readLongLE();
397     // }
398 
399     // private static Float binaryDecodeFloat(ByteBuf buffer) {
400     //     return buffer.readFloatLE();
401     // }
402 
403     // private static Double binaryDecodeDouble(ByteBuf buffer) {
404     //     return buffer.readDoubleLE();
405     // }
406 
407     // private static Numeric binaryDecodeNumeric(Charset charset, ByteBuf buffer) {
408     //     return Numeric.parse(BufferUtils.readLengthEncodedString(buffer, charset));
409     // }
410 
411     // private static Object binaryDecodeBlobOrText(Charset charset, int columnDefinitionFlags, ByteBuf buffer) {
412     //     if (isBinaryField(columnDefinitionFlags)) {
413     //         return binaryDecodeBlob(buffer);
414     //     } else {
415     //         return binaryDecodeText(charset, buffer);
416     //     }
417     // }
418 
419     private static byte[] binaryDecodeBlob(ByteBuf buffer) {
420         int len = cast(int) BufferUtils.readLengthEncodedInteger(buffer);
421         ByteBuf buff = buffer.copy(buffer.readerIndex(), len);
422         buffer.skipBytes(len);
423         return buff.getReadableBytes();
424     }
425 
426     // private static string binaryDecodeText(Charset charset, ByteBuf buffer) {
427     //     return BufferUtils.readLengthEncodedString(buffer, charset);
428     // }
429 
430     // private static LocalDateTime binaryDecodeDatetime(ByteBuf buffer) {
431     //     if (buffer.readableBytes() == 0) {
432     //         return null;
433     //     }
434     //     int length = buffer.readByte();
435     //     if (length == 0) {
436     //         // invalid value '0000-00-00' or '0000-00-00 00:00:00'
437     //         return null;
438     //     } else {
439     //         int year = buffer.readShortLE();
440     //         byte month = buffer.readByte();
441     //         byte day = buffer.readByte();
442     //         if (length == 4) {
443     //             return LocalDateTime.of(year, month, day, 0, 0, 0);
444     //         }
445     //         byte hour = buffer.readByte();
446     //         byte minute = buffer.readByte();
447     //         byte second = buffer.readByte();
448     //         if (length == 11) {
449     //             int microsecond = buffer.readIntLE();
450     //             return LocalDateTime.of(year, month, day, hour, minute, second, microsecond * 1000);
451     //         } else if (length == 7) {
452     //             return LocalDateTime.of(year, month, day, hour, minute, second, 0);
453     //         }
454     //         throw new DecoderException("Invalid Datetime");
455     //     }
456     // }
457 
458     // private static LocalDate binaryDecodeDate(ByteBuf buffer) {
459     //     return binaryDecodeDatetime(buffer).toLocalDate();
460     // }
461 
462     // private static Duration binaryDecodeTime(ByteBuf buffer) {
463     //     byte length = buffer.readByte();
464     //     if (length == 0) {
465     //         return Duration.ZERO;
466     //     } else {
467     //         bool isNegative = (buffer.readByte() == 1);
468     //         int days = buffer.readIntLE();
469     //         int hour = buffer.readByte();
470     //         int minute = buffer.readByte();
471     //         int second = buffer.readByte();
472     //         if (isNegative) {
473     //             days = -days;
474     //             hour = -hour;
475     //             minute = -minute;
476     //             second = -second;
477     //         }
478 
479     //         if (length == 8) {
480     //             return Duration.ofDays(days).plusHours(hour).plusMinutes(minute).plusSeconds(second);
481     //         }
482     //         if (length == 12) {
483     //             long microsecond = buffer.readUnsignedIntLE();
484     //             if (isNegative) {
485     //                 microsecond = -microsecond;
486     //             }
487     //             return Duration.ofDays(days).plusHours(hour).plusMinutes(minute).plusSeconds(second).plusNanos(microsecond * 1000);
488     //         }
489     //         throw new DecoderException("Invalid time format");
490     //     }
491     // }
492 
493     // private static Byte textDecodeInt1(Charset charset, ByteBuf buffer) {
494     //     return Byte.parseByte(buffer.toString(charset));
495     // }
496 
497     // private static Short textDecodeInt2(Charset charset, ByteBuf buffer) {
498     //     return Short.parseShort(buffer.toString(charset));
499     // }
500 
501     // private static Integer textDecodeInt3(Charset charset, ByteBuf buffer) {
502     //     return Integer.parseInt(buffer.toString(charset));
503     // }
504 
505     // private static Integer textDecodeInt4(Charset charset, ByteBuf buffer) {
506     //     return Integer.parseInt(buffer.toString(charset));
507     // }
508 
509     // private static Long textDecodeInt8(Charset charset, ByteBuf buffer) {
510     //     return Long.parseLong(buffer.toString(charset));
511     // }
512 
513     // private static Float textDecodeFloat(Charset charset, ByteBuf buffer) {
514     //     return Float.parseFloat(buffer.toString(charset));
515     // }
516 
517     // private static Double textDecodeDouble(Charset charset, ByteBuf buffer) {
518     //     return Double.parseDouble(buffer.toString(charset));
519     // }
520 
521     // private static Number textDecodeNUMERIC(Charset charset, ByteBuf buff) {
522     //     return Numeric.parse(buff.toString(charset));
523     // }
524 
525     // private static Object textDecodeBlobOrText(Charset charset, int columnDefinitionFlags, 
526     //         int index, int len, ByteBuf buffer) {
527     //     if (isBinaryField(columnDefinitionFlags)) {
528     //         return textDecodeBlob(buffer);
529     //     } else {
530     //         return textDecodeText(charset, buffer);
531     //     }
532     // }
533 
534     private static byte[] textDecodeBlob(int index, int len, ByteBuf buffer) {
535         ByteBuf buff = buffer.copy(index, len);
536         return buff.getReadableBytes();
537     }
538 
539     private static string textDecodeText(Charset charset, int index, int len, ByteBuf buffer) {
540         return buffer.getCharSequence(index, len, charset);
541     }
542 
543     // private static LocalDate textDecodeDate(Charset charset, ByteBuf buffer) {
544     //     CharSequence cs = buffer.toString(charset);
545     //     return LocalDate.parse(cs);
546     // }
547 
548     // private static Duration textDecodeTime(Charset charset, ByteBuf buffer) {
549     //     // HH:mm:ss or HHH:mm:ss
550     //     string timeString = buffer.toString(charset);
551     //     bool isNegative = timeString.charAt(0) == '-';
552     //     if (isNegative) {
553     //         timeString = timeString.substring(1);
554     //     }
555 
556     //     string[] timeElements = timeString.split(":");
557     //     if (timeElements.length != 3) {
558     //         throw new DecoderException("Invalid time format");
559     //     }
560 
561     //     int hour = Integer.parseInt(timeElements[0]);
562     //     int minute = Integer.parseInt(timeElements[1]);
563     //     int second = Integer.parseInt(timeElements[2].substring(0, 2));
564     //     long nanos = 0;
565     //     if (timeElements[2].length() > 2) {
566     //         double fractionalSecondsPart = Double.parseDouble("0." ~ timeElements[2].substring(3));
567     //         nanos = (long) (1000000000 * fractionalSecondsPart);
568     //     }
569     //     if (isNegative) {
570     //         return Duration.ofHours(-hour).minusMinutes(minute).minusSeconds(second).minusNanos(nanos);
571     //     } else {
572     //         return Duration.ofHours(hour).plusMinutes(minute).plusSeconds(second).plusNanos(nanos);
573     //     }
574     // }
575 
576     // private static LocalDateTime textDecodeDateTime(Charset charset, ByteBuf buffer) {
577     //     CharSequence cs = buffer.toString(charset);
578     //     if (cs.equals("0000-00-00 00:00:00")) {
579     //         // Invalid datetime will be converted to zero
580     //         return null;
581     //     }
582     //     return LocalDateTime.parse(cs, DATETIME_FORMAT);
583     // }
584 
585     private static bool isBinaryField(int columnDefinitionFlags) {
586         return (columnDefinitionFlags & ColumnDefinitionFlags.BINARY_FLAG) != 0;
587     }
588 }