1 module hunt.database.driver.mysql.impl.codec.ExtendedQueryCommandBaseCodec; 2 3 import hunt.database.driver.mysql.impl.codec.ColumnDefinition; 4 import hunt.database.driver.mysql.impl.codec.CommandType; 5 import hunt.database.driver.mysql.impl.codec.DataFormat; 6 import hunt.database.driver.mysql.impl.codec.DataType; 7 import hunt.database.driver.mysql.impl.codec.DataTypeCodec; 8 import hunt.database.driver.mysql.impl.codec.DataTypeDesc; 9 import hunt.database.driver.mysql.impl.codec.MySQLPreparedStatement; 10 import hunt.database.driver.mysql.impl.codec.Packets; 11 import hunt.database.driver.mysql.impl.codec.QueryCommandBaseCodec; 12 13 import hunt.database.driver.mysql.impl.MySQLCollation; 14 import hunt.database.base.Tuple; 15 import hunt.database.base.impl.command.ExtendedQueryCommandBase; 16 17 import hunt.Exceptions; 18 import hunt.net.buffer.ByteBuf; 19 import hunt.text.Charset; 20 // import java.time.Duration; 21 // import java.time.LocalDate; 22 // import java.time.LocalDateTime; 23 24 import std.variant; 25 26 /** 27 * 28 */ 29 abstract class ExtendedQueryCommandBaseCodec(R, C) : QueryCommandBaseCodec!(R, C) { 30 // C extends ExtendedQueryCommandBase!(R) 31 // TODO handle re-bound situations? 32 // Flag if parameters must be re-bound 33 protected byte sendType = 1; 34 35 protected MySQLPreparedStatement statement; 36 37 this(C cmd) { 38 super(cmd, DataFormat.BINARY); 39 statement = cast(MySQLPreparedStatement) cmd.preparedStatement(); 40 } 41 42 override 43 protected void handleInitPacket(ByteBuf payload) { 44 // may receive ERR_Packet, OK_Packet, Binary Protocol Resultset 45 int firstByte = payload.getUnsignedByte(payload.readerIndex()); 46 if (firstByte == Packets.OK_PACKET_HEADER) { 47 OkPacket okPacket = decodeOkPacketPayload(payload, StandardCharsets.UTF_8); 48 handleSingleResultsetDecodingCompleted(okPacket.serverStatusFlags(), 49 cast(int) okPacket.affectedRows(), cast(int) okPacket.lastInsertId()); 50 } else if (firstByte == Packets.ERROR_PACKET_HEADER) { 51 handleErrorPacketPayload(payload); 52 } else { 53 handleResultsetColumnCountPacketBody(payload); 54 } 55 } 56 57 protected void sendStatementExecuteCommand(long statementId, ColumnDefinition[] paramsColumnDefinitions, 58 byte sendType, Tuple params, byte cursorType) { 59 ByteBuf packet = allocateBuffer(); 60 // encode packet header 61 int packetStartIdx = packet.writerIndex(); 62 packet.writeMediumLE(0); // will set payload length later by calculation 63 packet.writeByte(sequenceId); 64 65 // encode packet payload 66 packet.writeByte(CommandType.COM_STMT_EXECUTE); 67 packet.writeIntLE(cast(int) statementId); 68 packet.writeByte(cursorType); 69 // iteration count, always 1 70 packet.writeIntLE(1); 71 72 int numOfParams = cast(int)paramsColumnDefinitions.length; 73 int bitmapLength = (numOfParams + 7) / 8; 74 byte[] nullBitmap = new byte[bitmapLength]; 75 76 int pos = packet.writerIndex(); 77 78 if (numOfParams > 0) { 79 // write a dummy bitmap first 80 packet.writeBytes(nullBitmap); 81 packet.writeByte(sendType); 82 if (sendType == 1) { 83 for (int i = 0; i < numOfParams; i++) { 84 Variant value = params.getValue(i); 85 packet.writeByte(parseDataTypeByEncodingValue(value).id); 86 packet.writeByte(0); // parameter flag: signed 87 } 88 } 89 90 for (int i = 0; i < numOfParams; i++) { 91 Variant value = params.getValue(i); 92 if (value.hasValue() && value != null) { 93 MySQLCollation collation = MySQLCollation.valueOfId(paramsColumnDefinitions[i].characterSet()); 94 DataTypeCodec.encodeBinary(cast(DataType)parseDataTypeByEncodingValue(value).id, 95 (collation.mappedCharsetName()), value, packet); // Charset.forName 96 } else { 97 nullBitmap[i / 8] |= (1 << (i & 7)); 98 } 99 } 100 101 // padding null-bitmap content 102 packet.setBytes(pos, nullBitmap); 103 } 104 105 // set payload length 106 int payloadLength = packet.writerIndex() - packetStartIdx - 4; 107 packet.setMediumLE(packetStartIdx, payloadLength); 108 109 sendPacket(packet, payloadLength); 110 } 111 112 private DataTypeDesc parseDataTypeByEncodingValue(ref Variant value) { 113 // FIXME: Needing refactor or cleanup -@zxp at 9/7/2019, 9:54:11 AM 114 // 115 if (value == null) { 116 // ProtocolBinary::MYSQL_TYPE_NULL 117 return DataTypes.NULL; 118 } else if (value.type == typeid(byte) || value.type == typeid(ubyte)) { 119 // ProtocolBinary::MYSQL_TYPE_TINY 120 return DataTypes.INT1; 121 } else if (value.type == typeid(bool)) { 122 // ProtocolBinary::MYSQL_TYPE_TINY 123 return DataTypes.INT1; 124 } else if (value.type == typeid(short) || value.type == typeid(ushort)) { 125 // ProtocolBinary::MYSQL_TYPE_SHORT, ProtocolBinary::MYSQL_TYPE_YEAR 126 return DataTypes.INT2; 127 } else if (value.type == typeid(int) || value.type == typeid(uint)) { 128 // ProtocolBinary::MYSQL_TYPE_LONG, ProtocolBinary::MYSQL_TYPE_INT24 129 return DataTypes.INT4; 130 } else if (value.type == typeid(long) || value.type == typeid(ulong)) { 131 // ProtocolBinary::MYSQL_TYPE_LONGLONG 132 return DataTypes.INT8; 133 } else if (value.type == typeid(double)) { 134 // ProtocolBinary::MYSQL_TYPE_DOUBLE 135 return DataTypes.DOUBLE; 136 } else if (value.type == typeid(float)) { 137 // ProtocolBinary::MYSQL_TYPE_FLOAT 138 return DataTypes.FLOAT; 139 // } else if (value instanceof LocalDate) { 140 // // ProtocolBinary::MYSQL_TYPE_DATE 141 // return DataTypes.DATE; 142 // } else if (value instanceof Duration) { 143 // // ProtocolBinary::MYSQL_TYPE_TIME 144 // return DataTypes.TIME; 145 } else if (value.type == typeid(byte[]) || value.type == typeid(ubyte[])) { 146 // ProtocolBinary::MYSQL_TYPE_LONG_BLOB, ProtocolBinary::MYSQL_TYPE_MEDIUM_BLOB, ProtocolBinary::MYSQL_TYPE_BLOB, ProtocolBinary::MYSQL_TYPE_TINY_BLOB 147 return DataTypes.BLOB; 148 // } else if (value instanceof LocalDateTime) { 149 // // ProtocolBinary::MYSQL_TYPE_DATETIME, ProtocolBinary::MYSQL_TYPE_TIMESTAMP 150 // return DataTypes.DATETIME; 151 } else { 152 /* 153 ProtocolBinary::MYSQL_TYPE_STRING, ProtocolBinary::MYSQL_TYPE_VARCHAR, ProtocolBinary::MYSQL_TYPE_VAR_STRING, 154 ProtocolBinary::MYSQL_TYPE_ENUM, ProtocolBinary::MYSQL_TYPE_SET, ProtocolBinary::MYSQL_TYPE_GEOMETRY, 155 ProtocolBinary::MYSQL_TYPE_BIT, ProtocolBinary::MYSQL_TYPE_DECIMAL, ProtocolBinary::MYSQL_TYPE_NEWDECIMAL 156 */ 157 return DataTypes.STRING; 158 } 159 } 160 }