parser.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Parser = void 0;
  4. const messages_1 = require("./messages");
  5. const buffer_reader_1 = require("./buffer-reader");
  6. // every message is prefixed with a single bye
  7. const CODE_LENGTH = 1;
  8. // every message has an int32 length which includes itself but does
  9. // NOT include the code in the length
  10. const LEN_LENGTH = 4;
  11. const HEADER_LENGTH = CODE_LENGTH + LEN_LENGTH;
  12. const emptyBuffer = Buffer.allocUnsafe(0);
  13. class Parser {
  14. constructor(opts) {
  15. this.buffer = emptyBuffer;
  16. this.bufferLength = 0;
  17. this.bufferOffset = 0;
  18. this.reader = new buffer_reader_1.BufferReader();
  19. if ((opts === null || opts === void 0 ? void 0 : opts.mode) === 'binary') {
  20. throw new Error('Binary mode not supported yet');
  21. }
  22. this.mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'text';
  23. }
  24. parse(buffer, callback) {
  25. this.mergeBuffer(buffer);
  26. const bufferFullLength = this.bufferOffset + this.bufferLength;
  27. let offset = this.bufferOffset;
  28. while (offset + HEADER_LENGTH <= bufferFullLength) {
  29. // code is 1 byte long - it identifies the message type
  30. const code = this.buffer[offset];
  31. // length is 1 Uint32BE - it is the length of the message EXCLUDING the code
  32. const length = this.buffer.readUInt32BE(offset + CODE_LENGTH);
  33. const fullMessageLength = CODE_LENGTH + length;
  34. if (fullMessageLength + offset <= bufferFullLength) {
  35. const message = this.handlePacket(offset + HEADER_LENGTH, code, length, this.buffer);
  36. callback(message);
  37. offset += fullMessageLength;
  38. }
  39. else {
  40. break;
  41. }
  42. }
  43. if (offset === bufferFullLength) {
  44. // No more use for the buffer
  45. this.buffer = emptyBuffer;
  46. this.bufferLength = 0;
  47. this.bufferOffset = 0;
  48. }
  49. else {
  50. // Adjust the cursors of remainingBuffer
  51. this.bufferLength = bufferFullLength - offset;
  52. this.bufferOffset = offset;
  53. }
  54. }
  55. mergeBuffer(buffer) {
  56. if (this.bufferLength > 0) {
  57. const newLength = this.bufferLength + buffer.byteLength;
  58. const newFullLength = newLength + this.bufferOffset;
  59. if (newFullLength > this.buffer.byteLength) {
  60. // We can't concat the new buffer with the remaining one
  61. let newBuffer;
  62. if (newLength <= this.buffer.byteLength && this.bufferOffset >= this.bufferLength) {
  63. // We can move the relevant part to the beginning of the buffer instead of allocating a new buffer
  64. newBuffer = this.buffer;
  65. }
  66. else {
  67. // Allocate a new larger buffer
  68. let newBufferLength = this.buffer.byteLength * 2;
  69. while (newLength >= newBufferLength) {
  70. newBufferLength *= 2;
  71. }
  72. newBuffer = Buffer.allocUnsafe(newBufferLength);
  73. }
  74. // Move the remaining buffer to the new one
  75. this.buffer.copy(newBuffer, 0, this.bufferOffset, this.bufferOffset + this.bufferLength);
  76. this.buffer = newBuffer;
  77. this.bufferOffset = 0;
  78. }
  79. // Concat the new buffer with the remaining one
  80. buffer.copy(this.buffer, this.bufferOffset + this.bufferLength);
  81. this.bufferLength = newLength;
  82. }
  83. else {
  84. this.buffer = buffer;
  85. this.bufferOffset = 0;
  86. this.bufferLength = buffer.byteLength;
  87. }
  88. }
  89. handlePacket(offset, code, length, bytes) {
  90. switch (code) {
  91. case 50 /* MessageCodes.BindComplete */:
  92. return messages_1.bindComplete;
  93. case 49 /* MessageCodes.ParseComplete */:
  94. return messages_1.parseComplete;
  95. case 51 /* MessageCodes.CloseComplete */:
  96. return messages_1.closeComplete;
  97. case 110 /* MessageCodes.NoData */:
  98. return messages_1.noData;
  99. case 115 /* MessageCodes.PortalSuspended */:
  100. return messages_1.portalSuspended;
  101. case 99 /* MessageCodes.CopyDone */:
  102. return messages_1.copyDone;
  103. case 87 /* MessageCodes.ReplicationStart */:
  104. return messages_1.replicationStart;
  105. case 73 /* MessageCodes.EmptyQuery */:
  106. return messages_1.emptyQuery;
  107. case 68 /* MessageCodes.DataRow */:
  108. return this.parseDataRowMessage(offset, length, bytes);
  109. case 67 /* MessageCodes.CommandComplete */:
  110. return this.parseCommandCompleteMessage(offset, length, bytes);
  111. case 90 /* MessageCodes.ReadyForQuery */:
  112. return this.parseReadyForQueryMessage(offset, length, bytes);
  113. case 65 /* MessageCodes.NotificationResponse */:
  114. return this.parseNotificationMessage(offset, length, bytes);
  115. case 82 /* MessageCodes.AuthenticationResponse */:
  116. return this.parseAuthenticationResponse(offset, length, bytes);
  117. case 83 /* MessageCodes.ParameterStatus */:
  118. return this.parseParameterStatusMessage(offset, length, bytes);
  119. case 75 /* MessageCodes.BackendKeyData */:
  120. return this.parseBackendKeyData(offset, length, bytes);
  121. case 69 /* MessageCodes.ErrorMessage */:
  122. return this.parseErrorMessage(offset, length, bytes, 'error');
  123. case 78 /* MessageCodes.NoticeMessage */:
  124. return this.parseErrorMessage(offset, length, bytes, 'notice');
  125. case 84 /* MessageCodes.RowDescriptionMessage */:
  126. return this.parseRowDescriptionMessage(offset, length, bytes);
  127. case 116 /* MessageCodes.ParameterDescriptionMessage */:
  128. return this.parseParameterDescriptionMessage(offset, length, bytes);
  129. case 71 /* MessageCodes.CopyIn */:
  130. return this.parseCopyInMessage(offset, length, bytes);
  131. case 72 /* MessageCodes.CopyOut */:
  132. return this.parseCopyOutMessage(offset, length, bytes);
  133. case 100 /* MessageCodes.CopyData */:
  134. return this.parseCopyData(offset, length, bytes);
  135. default:
  136. return new messages_1.DatabaseError('received invalid response: ' + code.toString(16), length, 'error');
  137. }
  138. }
  139. parseReadyForQueryMessage(offset, length, bytes) {
  140. this.reader.setBuffer(offset, bytes);
  141. const status = this.reader.string(1);
  142. return new messages_1.ReadyForQueryMessage(length, status);
  143. }
  144. parseCommandCompleteMessage(offset, length, bytes) {
  145. this.reader.setBuffer(offset, bytes);
  146. const text = this.reader.cstring();
  147. return new messages_1.CommandCompleteMessage(length, text);
  148. }
  149. parseCopyData(offset, length, bytes) {
  150. const chunk = bytes.slice(offset, offset + (length - 4));
  151. return new messages_1.CopyDataMessage(length, chunk);
  152. }
  153. parseCopyInMessage(offset, length, bytes) {
  154. return this.parseCopyMessage(offset, length, bytes, 'copyInResponse');
  155. }
  156. parseCopyOutMessage(offset, length, bytes) {
  157. return this.parseCopyMessage(offset, length, bytes, 'copyOutResponse');
  158. }
  159. parseCopyMessage(offset, length, bytes, messageName) {
  160. this.reader.setBuffer(offset, bytes);
  161. const isBinary = this.reader.byte() !== 0;
  162. const columnCount = this.reader.int16();
  163. const message = new messages_1.CopyResponse(length, messageName, isBinary, columnCount);
  164. for (let i = 0; i < columnCount; i++) {
  165. message.columnTypes[i] = this.reader.int16();
  166. }
  167. return message;
  168. }
  169. parseNotificationMessage(offset, length, bytes) {
  170. this.reader.setBuffer(offset, bytes);
  171. const processId = this.reader.int32();
  172. const channel = this.reader.cstring();
  173. const payload = this.reader.cstring();
  174. return new messages_1.NotificationResponseMessage(length, processId, channel, payload);
  175. }
  176. parseRowDescriptionMessage(offset, length, bytes) {
  177. this.reader.setBuffer(offset, bytes);
  178. const fieldCount = this.reader.int16();
  179. const message = new messages_1.RowDescriptionMessage(length, fieldCount);
  180. for (let i = 0; i < fieldCount; i++) {
  181. message.fields[i] = this.parseField();
  182. }
  183. return message;
  184. }
  185. parseField() {
  186. const name = this.reader.cstring();
  187. const tableID = this.reader.int32();
  188. const columnID = this.reader.int16();
  189. const dataTypeID = this.reader.int32();
  190. const dataTypeSize = this.reader.int16();
  191. const dataTypeModifier = this.reader.int32();
  192. const mode = this.reader.int16() === 0 ? 'text' : 'binary';
  193. return new messages_1.Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode);
  194. }
  195. parseParameterDescriptionMessage(offset, length, bytes) {
  196. this.reader.setBuffer(offset, bytes);
  197. const parameterCount = this.reader.int16();
  198. const message = new messages_1.ParameterDescriptionMessage(length, parameterCount);
  199. for (let i = 0; i < parameterCount; i++) {
  200. message.dataTypeIDs[i] = this.reader.int32();
  201. }
  202. return message;
  203. }
  204. parseDataRowMessage(offset, length, bytes) {
  205. this.reader.setBuffer(offset, bytes);
  206. const fieldCount = this.reader.int16();
  207. const fields = new Array(fieldCount);
  208. for (let i = 0; i < fieldCount; i++) {
  209. const len = this.reader.int32();
  210. // a -1 for length means the value of the field is null
  211. fields[i] = len === -1 ? null : this.reader.string(len);
  212. }
  213. return new messages_1.DataRowMessage(length, fields);
  214. }
  215. parseParameterStatusMessage(offset, length, bytes) {
  216. this.reader.setBuffer(offset, bytes);
  217. const name = this.reader.cstring();
  218. const value = this.reader.cstring();
  219. return new messages_1.ParameterStatusMessage(length, name, value);
  220. }
  221. parseBackendKeyData(offset, length, bytes) {
  222. this.reader.setBuffer(offset, bytes);
  223. const processID = this.reader.int32();
  224. const secretKey = this.reader.int32();
  225. return new messages_1.BackendKeyDataMessage(length, processID, secretKey);
  226. }
  227. parseAuthenticationResponse(offset, length, bytes) {
  228. this.reader.setBuffer(offset, bytes);
  229. const code = this.reader.int32();
  230. // TODO(bmc): maybe better types here
  231. const message = {
  232. name: 'authenticationOk',
  233. length,
  234. };
  235. switch (code) {
  236. case 0: // AuthenticationOk
  237. break;
  238. case 3: // AuthenticationCleartextPassword
  239. if (message.length === 8) {
  240. message.name = 'authenticationCleartextPassword';
  241. }
  242. break;
  243. case 5: // AuthenticationMD5Password
  244. if (message.length === 12) {
  245. message.name = 'authenticationMD5Password';
  246. const salt = this.reader.bytes(4);
  247. return new messages_1.AuthenticationMD5Password(length, salt);
  248. }
  249. break;
  250. case 10: // AuthenticationSASL
  251. message.name = 'authenticationSASL';
  252. message.mechanisms = [];
  253. let mechanism;
  254. do {
  255. mechanism = this.reader.cstring();
  256. if (mechanism) {
  257. message.mechanisms.push(mechanism);
  258. }
  259. } while (mechanism);
  260. break;
  261. case 11: // AuthenticationSASLContinue
  262. message.name = 'authenticationSASLContinue';
  263. message.data = this.reader.string(length - 8);
  264. break;
  265. case 12: // AuthenticationSASLFinal
  266. message.name = 'authenticationSASLFinal';
  267. message.data = this.reader.string(length - 8);
  268. break;
  269. default:
  270. throw new Error('Unknown authenticationOk message type ' + code);
  271. }
  272. return message;
  273. }
  274. parseErrorMessage(offset, length, bytes, name) {
  275. this.reader.setBuffer(offset, bytes);
  276. const fields = {};
  277. let fieldType = this.reader.string(1);
  278. while (fieldType !== '\0') {
  279. fields[fieldType] = this.reader.cstring();
  280. fieldType = this.reader.string(1);
  281. }
  282. const messageValue = fields.M;
  283. const message = name === 'notice' ? new messages_1.NoticeMessage(length, messageValue) : new messages_1.DatabaseError(messageValue, length, name);
  284. message.severity = fields.S;
  285. message.code = fields.C;
  286. message.detail = fields.D;
  287. message.hint = fields.H;
  288. message.position = fields.P;
  289. message.internalPosition = fields.p;
  290. message.internalQuery = fields.q;
  291. message.where = fields.W;
  292. message.schema = fields.s;
  293. message.table = fields.t;
  294. message.column = fields.c;
  295. message.dataType = fields.d;
  296. message.constraint = fields.n;
  297. message.file = fields.F;
  298. message.line = fields.L;
  299. message.routine = fields.R;
  300. return message;
  301. }
  302. }
  303. exports.Parser = Parser;
  304. //# sourceMappingURL=parser.js.map