serializer.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.serialize = void 0;
  4. const buffer_writer_1 = require("./buffer-writer");
  5. const writer = new buffer_writer_1.Writer();
  6. const startup = (opts) => {
  7. // protocol version
  8. writer.addInt16(3).addInt16(0);
  9. for (const key of Object.keys(opts)) {
  10. writer.addCString(key).addCString(opts[key]);
  11. }
  12. writer.addCString('client_encoding').addCString('UTF8');
  13. var bodyBuffer = writer.addCString('').flush();
  14. // this message is sent without a code
  15. var length = bodyBuffer.length + 4;
  16. return new buffer_writer_1.Writer().addInt32(length).add(bodyBuffer).flush();
  17. };
  18. const requestSsl = () => {
  19. const response = Buffer.allocUnsafe(8);
  20. response.writeInt32BE(8, 0);
  21. response.writeInt32BE(80877103, 4);
  22. return response;
  23. };
  24. const password = (password) => {
  25. return writer.addCString(password).flush(112 /* startup */);
  26. };
  27. const sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
  28. // 0x70 = 'p'
  29. writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse);
  30. return writer.flush(112 /* startup */);
  31. };
  32. const sendSCRAMClientFinalMessage = function (additionalData) {
  33. return writer.addString(additionalData).flush(112 /* startup */);
  34. };
  35. const query = (text) => {
  36. return writer.addCString(text).flush(81 /* query */);
  37. };
  38. const emptyArray = [];
  39. const parse = (query) => {
  40. // expect something like this:
  41. // { name: 'queryName',
  42. // text: 'select * from blah',
  43. // types: ['int8', 'bool'] }
  44. // normalize missing query names to allow for null
  45. const name = query.name || '';
  46. if (name.length > 63) {
  47. /* eslint-disable no-console */
  48. console.error('Warning! Postgres only supports 63 characters for query names.');
  49. console.error('You supplied %s (%s)', name, name.length);
  50. console.error('This can cause conflicts and silent errors executing queries');
  51. /* eslint-enable no-console */
  52. }
  53. const types = query.types || emptyArray;
  54. var len = types.length;
  55. var buffer = writer
  56. .addCString(name) // name of query
  57. .addCString(query.text) // actual query text
  58. .addInt16(len);
  59. for (var i = 0; i < len; i++) {
  60. buffer.addInt32(types[i]);
  61. }
  62. return writer.flush(80 /* parse */);
  63. };
  64. const paramWriter = new buffer_writer_1.Writer();
  65. const writeValues = function (values, valueMapper) {
  66. for (let i = 0; i < values.length; i++) {
  67. const mappedVal = valueMapper ? valueMapper(values[i], i) : values[i];
  68. if (mappedVal == null) {
  69. // add the param type (string) to the writer
  70. writer.addInt16(0 /* STRING */);
  71. // write -1 to the param writer to indicate null
  72. paramWriter.addInt32(-1);
  73. }
  74. else if (mappedVal instanceof Buffer) {
  75. // add the param type (binary) to the writer
  76. writer.addInt16(1 /* BINARY */);
  77. // add the buffer to the param writer
  78. paramWriter.addInt32(mappedVal.length);
  79. paramWriter.add(mappedVal);
  80. }
  81. else {
  82. // add the param type (string) to the writer
  83. writer.addInt16(0 /* STRING */);
  84. paramWriter.addInt32(Buffer.byteLength(mappedVal));
  85. paramWriter.addString(mappedVal);
  86. }
  87. }
  88. };
  89. const bind = (config = {}) => {
  90. // normalize config
  91. const portal = config.portal || '';
  92. const statement = config.statement || '';
  93. const binary = config.binary || false;
  94. const values = config.values || emptyArray;
  95. const len = values.length;
  96. writer.addCString(portal).addCString(statement);
  97. writer.addInt16(len);
  98. writeValues(values, config.valueMapper);
  99. writer.addInt16(len);
  100. writer.add(paramWriter.flush());
  101. // format code
  102. writer.addInt16(binary ? 1 /* BINARY */ : 0 /* STRING */);
  103. return writer.flush(66 /* bind */);
  104. };
  105. const emptyExecute = Buffer.from([69 /* execute */, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]);
  106. const execute = (config) => {
  107. // this is the happy path for most queries
  108. if (!config || (!config.portal && !config.rows)) {
  109. return emptyExecute;
  110. }
  111. const portal = config.portal || '';
  112. const rows = config.rows || 0;
  113. const portalLength = Buffer.byteLength(portal);
  114. const len = 4 + portalLength + 1 + 4;
  115. // one extra bit for code
  116. const buff = Buffer.allocUnsafe(1 + len);
  117. buff[0] = 69 /* execute */;
  118. buff.writeInt32BE(len, 1);
  119. buff.write(portal, 5, 'utf-8');
  120. buff[portalLength + 5] = 0; // null terminate portal cString
  121. buff.writeUInt32BE(rows, buff.length - 4);
  122. return buff;
  123. };
  124. const cancel = (processID, secretKey) => {
  125. const buffer = Buffer.allocUnsafe(16);
  126. buffer.writeInt32BE(16, 0);
  127. buffer.writeInt16BE(1234, 4);
  128. buffer.writeInt16BE(5678, 6);
  129. buffer.writeInt32BE(processID, 8);
  130. buffer.writeInt32BE(secretKey, 12);
  131. return buffer;
  132. };
  133. const cstringMessage = (code, string) => {
  134. const stringLen = Buffer.byteLength(string);
  135. const len = 4 + stringLen + 1;
  136. // one extra bit for code
  137. const buffer = Buffer.allocUnsafe(1 + len);
  138. buffer[0] = code;
  139. buffer.writeInt32BE(len, 1);
  140. buffer.write(string, 5, 'utf-8');
  141. buffer[len] = 0; // null terminate cString
  142. return buffer;
  143. };
  144. const emptyDescribePortal = writer.addCString('P').flush(68 /* describe */);
  145. const emptyDescribeStatement = writer.addCString('S').flush(68 /* describe */);
  146. const describe = (msg) => {
  147. return msg.name
  148. ? cstringMessage(68 /* describe */, `${msg.type}${msg.name || ''}`)
  149. : msg.type === 'P'
  150. ? emptyDescribePortal
  151. : emptyDescribeStatement;
  152. };
  153. const close = (msg) => {
  154. const text = `${msg.type}${msg.name || ''}`;
  155. return cstringMessage(67 /* close */, text);
  156. };
  157. const copyData = (chunk) => {
  158. return writer.add(chunk).flush(100 /* copyFromChunk */);
  159. };
  160. const copyFail = (message) => {
  161. return cstringMessage(102 /* copyFail */, message);
  162. };
  163. const codeOnlyBuffer = (code) => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]);
  164. const flushBuffer = codeOnlyBuffer(72 /* flush */);
  165. const syncBuffer = codeOnlyBuffer(83 /* sync */);
  166. const endBuffer = codeOnlyBuffer(88 /* end */);
  167. const copyDoneBuffer = codeOnlyBuffer(99 /* copyDone */);
  168. const serialize = {
  169. startup,
  170. password,
  171. requestSsl,
  172. sendSASLInitialResponseMessage,
  173. sendSCRAMClientFinalMessage,
  174. query,
  175. parse,
  176. bind,
  177. execute,
  178. describe,
  179. close,
  180. flush: () => flushBuffer,
  181. sync: () => syncBuffer,
  182. end: () => endBuffer,
  183. copyData,
  184. copyDone: () => copyDoneBuffer,
  185. copyFail,
  186. cancel,
  187. };
  188. exports.serialize = serialize;
  189. //# sourceMappingURL=serializer.js.map