125 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| // TODO: rename to OK packet
 | |
| // https://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
 | |
| 
 | |
| const Packet = require('./packet.js');
 | |
| const ClientConstants = require('../constants/client.js');
 | |
| const ServerSatusFlags = require('../constants/server_status.js');
 | |
| 
 | |
| const EncodingToCharset = require('../constants/encoding_charset.js');
 | |
| const sessionInfoTypes = require('../constants/session_track.js');
 | |
| 
 | |
| class ResultSetHeader {
 | |
|   constructor(packet, connection) {
 | |
|     const bigNumberStrings = connection.config.bigNumberStrings;
 | |
|     const encoding = connection.serverEncoding;
 | |
|     const flags = connection._handshakePacket.capabilityFlags;
 | |
|     const isSet = function (flag) {
 | |
|       return flags & ClientConstants[flag];
 | |
|     };
 | |
|     if (packet.buffer[packet.offset] !== 0) {
 | |
|       this.fieldCount = packet.readLengthCodedNumber();
 | |
|       if (this.fieldCount === null) {
 | |
|         this.infileName = packet.readString(undefined, encoding);
 | |
|       }
 | |
|       return;
 | |
|     }
 | |
|     this.fieldCount = packet.readInt8(); // skip OK byte
 | |
|     this.affectedRows = packet.readLengthCodedNumber(bigNumberStrings);
 | |
|     this.insertId = packet.readLengthCodedNumberSigned(bigNumberStrings);
 | |
|     this.info = '';
 | |
|     if (isSet('PROTOCOL_41')) {
 | |
|       this.serverStatus = packet.readInt16();
 | |
|       this.warningStatus = packet.readInt16();
 | |
|     } else if (isSet('TRANSACTIONS')) {
 | |
|       this.serverStatus = packet.readInt16();
 | |
|     }
 | |
|     let stateChanges = null;
 | |
|     if (isSet('SESSION_TRACK') && packet.offset < packet.end) {
 | |
|       this.info = packet.readLengthCodedString(encoding);
 | |
| 
 | |
|       if (this.serverStatus && ServerSatusFlags.SERVER_SESSION_STATE_CHANGED) {
 | |
|         // session change info record - see
 | |
|         // https://dev.mysql.com/doc/internals/en/packet-OK_Packet.html#cs-sect-packet-ok-sessioninfo
 | |
|         let len =
 | |
|           packet.offset < packet.end ? packet.readLengthCodedNumber() : 0;
 | |
|         const end = packet.offset + len;
 | |
|         let type, key, stateEnd;
 | |
|         if (len > 0) {
 | |
|           stateChanges = {
 | |
|             systemVariables: {},
 | |
|             schema: null,
 | |
|             gtids: [],
 | |
|             trackStateChange: null,
 | |
|           };
 | |
|         }
 | |
|         while (packet.offset < end) {
 | |
|           type = packet.readInt8();
 | |
|           len = packet.readLengthCodedNumber();
 | |
|           stateEnd = packet.offset + len;
 | |
|           if (type === sessionInfoTypes.SYSTEM_VARIABLES) {
 | |
|             key = packet.readLengthCodedString(encoding);
 | |
|             const val = packet.readLengthCodedString(encoding);
 | |
|             stateChanges.systemVariables[key] = val;
 | |
|             if (key === 'character_set_client') {
 | |
|               const charsetNumber = EncodingToCharset[val];
 | |
|               // TODO - better api for driver users to handle unknown encodings?
 | |
|               // maybe custom coverter in the config?
 | |
|               // For now just ignore character_set_client command if there is
 | |
|               // no known mapping from reported encoding to a charset code
 | |
|               if (typeof charsetNumber !== 'undefined') {
 | |
|                 connection.config.charsetNumber = charsetNumber;
 | |
|               }
 | |
|             }
 | |
|           } else if (type === sessionInfoTypes.SCHEMA) {
 | |
|             key = packet.readLengthCodedString(encoding);
 | |
|             stateChanges.schema = key;
 | |
|           } else if (type === sessionInfoTypes.STATE_CHANGE) {
 | |
|             stateChanges.trackStateChange =
 | |
|               packet.readLengthCodedString(encoding);
 | |
|           } else if (type === sessionInfoTypes.STATE_GTIDS) {
 | |
|             // TODO: find if the first length coded string means anything. Usually comes as empty
 | |
|             // eslint-disable-next-line no-unused-vars
 | |
|             const _unknownString = packet.readLengthCodedString(encoding);
 | |
|             const gtid = packet.readLengthCodedString(encoding);
 | |
|             stateChanges.gtids = gtid.split(',');
 | |
|           } else {
 | |
|             // unsupported session track type. For now just ignore
 | |
|           }
 | |
|           packet.offset = stateEnd;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       this.info = packet.readString(undefined, encoding);
 | |
|     }
 | |
|     if (stateChanges) {
 | |
|       this.stateChanges = stateChanges;
 | |
|     }
 | |
|     const m = this.info.match(/\schanged:\s*(\d+)/i);
 | |
|     if (m !== null) {
 | |
|       this.changedRows = parseInt(m[1], 10);
 | |
|     } else {
 | |
|       this.changedRows = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // TODO: should be consistent instance member, but it's just easier here to have just function
 | |
|   static toPacket(fieldCount, insertId) {
 | |
|     let length = 4 + Packet.lengthCodedNumberLength(fieldCount);
 | |
|     if (typeof insertId !== 'undefined') {
 | |
|       length += Packet.lengthCodedNumberLength(insertId);
 | |
|     }
 | |
|     const buffer = Buffer.allocUnsafe(length);
 | |
|     const packet = new Packet(0, buffer, 0, length);
 | |
|     packet.offset = 4;
 | |
|     packet.writeLengthCodedNumber(fieldCount);
 | |
|     if (typeof insertId !== 'undefined') {
 | |
|       packet.writeLengthCodedNumber(insertId);
 | |
|     }
 | |
|     return packet;
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = ResultSetHeader;
 |