1 module hunt.database.driver.mysql.impl.codec.RowResultDecoder;
2 
3 import hunt.database.driver.mysql.impl.codec.CommandCodec;
4 import hunt.database.driver.mysql.impl.codec.ColumnDefinition;
5 import hunt.database.driver.mysql.impl.codec.CommandType;
6 import hunt.database.driver.mysql.impl.codec.DataFormat;
7 import hunt.database.driver.mysql.impl.codec.DataType;
8 import hunt.database.driver.mysql.impl.codec.DataTypeCodec;
9 import hunt.database.driver.mysql.impl.codec.MySQLEncoder;
10 import hunt.database.driver.mysql.impl.codec.MySQLRowDesc;
11 import hunt.database.driver.mysql.impl.codec.Packets;
12 
13 import hunt.database.driver.mysql.impl.MySQLCollation;
14 import hunt.database.driver.mysql.impl.MySQLRowImpl;
15 
16 import hunt.database.base.Row;
17 import hunt.database.base.impl.RowDecoder;
18 import hunt.database.base.impl.RowSetImpl;
19 
20 import hunt.Exceptions;
21 import hunt.Functions;
22 import hunt.logging;
23 import hunt.net.buffer.ByteBuf;
24 import hunt.text.Charset;
25 
26 import std.variant;
27 
28 
29 /**
30  * 
31  */
32 class RowResultDecoder(R) : RowDecoder {
33     private enum int NULL = 0xFB;
34 
35     private int _size;
36     private RowSetImpl container;
37     private Row row;
38     private bool singleton;
39     MySQLRowDesc rowDesc;
40 
41     this(bool singleton, MySQLRowDesc rowDesc) {
42         this.singleton = singleton;
43         this.rowDesc = rowDesc;
44     }
45 
46     int size() {
47         return _size;
48     }
49 
50     override
51     void decodeRow(int len, ByteBuf inBuffer) {
52         if (container is null) {
53             container = new RowSetImpl(); 
54         }
55 
56         if (singleton) {
57             if (row is null) {
58                 row = new MySQLRowImpl(rowDesc);
59             } else {
60                 row.clear();
61             }
62         } else {
63             row = new MySQLRowImpl(rowDesc);
64         }
65 
66         version(HUNT_DB_DEBUG_MORE) infof("row: %d", _size+1);
67         Row row = new MySQLRowImpl(rowDesc);
68         if (rowDesc.dataFormat() == DataFormat.BINARY) {
69             // BINARY row decoding
70             // 0x00 packet header
71             // null_bitmap
72             int nullBitmapLength = (len + 7 + 2) >>  3;
73             int nullBitmapIdx = 1 + inBuffer.readerIndex();
74             inBuffer.skipBytes(1 + nullBitmapLength);
75 
76             // values
77             for (int c = 0; c < len; c++) {
78                 int val = c + 2;
79                 int bytePos = val >> 3;
80                 int bitPos = val & 7;
81                 byte mask = cast(byte) (1 << bitPos);
82                 byte nullByte = cast(byte) (inBuffer.getByte(nullBitmapIdx + bytePos) & mask);
83                 Variant decoded = null;
84                 if (nullByte == 0) {
85                     // non-null
86                     ColumnDefinition columnDesc = rowDesc.columnDefinitions()[c];
87                     DataType dataType = columnDesc.type();
88                     int collationId = rowDesc.columnDefinitions()[c].characterSet();
89                     Charset charset = (MySQLCollation.valueOfId(collationId).mappedCharsetName()); // Charset.forName
90                     int columnDefinitionFlags = columnDesc.flags();
91 
92                     version(HUNT_DB_DEBUG_MORE) {
93                         tracef("    column[%d]: name=%s, type=%s, flags=%d, charset=%s", 
94                             c, columnDesc.name(), dataType, columnDefinitionFlags, charset);
95                     }
96 
97                     decoded = DataTypeCodec.decodeBinary(dataType, charset, columnDefinitionFlags, inBuffer);
98                     
99                     version(HUNT_DB_DEBUG_MORE) {
100                         tracef("    column[%d]: value=%s", c, decoded.toString());
101                     }
102                 }
103                 row.addValue(decoded);
104             }
105         } else {
106             // TEXT row decoding
107             for (int c = 0; c < len; c++) {
108                 Variant decoded = null;
109                 if (inBuffer.getUnsignedByte(inBuffer.readerIndex()) == NULL) {
110                     inBuffer.skipBytes(1);
111                 } else {
112                     ColumnDefinition columnDesc = rowDesc.columnDefinitions()[c];
113                     
114                     DataType dataType = columnDesc.type();
115                     int columnDefinitionFlags = columnDesc.flags();
116                     int collationId = columnDesc.characterSet();
117                     Charset charset = (MySQLCollation.valueOfId(collationId).mappedCharsetName()); // Charset.forName
118 
119                     version(HUNT_DB_DEBUG_MORE) {
120                         infof("    column[%d]: name=%s, type=%s, flags=%d, charset=%s", 
121                             c, columnDesc.name(), dataType, columnDefinitionFlags, charset);
122                     }
123 
124                     decoded = DataTypeCodec.decodeText(dataType, charset, columnDefinitionFlags, inBuffer);
125 
126                     version(HUNT_DB_DEBUG_MORE) {
127                         tracef("    colum[%d]: value=%s", c, decoded.toString());
128                     }
129                 }
130                 row.addValue(decoded);
131             }
132         }
133         container.append(row);
134         _size++;
135     }
136 
137     R complete() {
138         if (container is null) {
139             container = new RowSetImpl(); 
140         }
141         return container;
142     }
143 
144     void reset() {
145         container = null;
146         _size = 0;
147     }
148 }
149