serializer.js
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 const bodyBuffer = writer.addCString('').flush(); 14 // this message is sent without a code 15 const 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 /* code.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 /* code.startup */); 31 }; 32 const sendSCRAMClientFinalMessage = function (additionalData) { 33 return writer.addString(additionalData).flush(112 /* code.startup */); 34 }; 35 const query = (text) => { 36 return writer.addCString(text).flush(81 /* code.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 console.error('Warning! Postgres only supports 63 characters for query names.'); 48 console.error('You supplied %s (%s)', name, name.length); 49 console.error('This can cause conflicts and silent errors executing queries'); 50 } 51 const types = query.types || emptyArray; 52 const len = types.length; 53 const buffer = writer 54 .addCString(name) // name of query 55 .addCString(query.text) // actual query text 56 .addInt16(len); 57 for (let i = 0; i < len; i++) { 58 buffer.addInt32(types[i]); 59 } 60 return writer.flush(80 /* code.parse */); 61 }; 62 const paramWriter = new buffer_writer_1.Writer(); 63 const writeValues = function (values, valueMapper) { 64 for (let i = 0; i < values.length; i++) { 65 const mappedVal = valueMapper ? valueMapper(values[i], i) : values[i]; 66 if (mappedVal == null) { 67 // add the param type (string) to the writer 68 writer.addInt16(0 /* ParamType.STRING */); 69 // write -1 to the param writer to indicate null 70 paramWriter.addInt32(-1); 71 } 72 else if (mappedVal instanceof Buffer) { 73 // add the param type (binary) to the writer 74 writer.addInt16(1 /* ParamType.BINARY */); 75 // add the buffer to the param writer 76 paramWriter.addInt32(mappedVal.length); 77 paramWriter.add(mappedVal); 78 } 79 else { 80 // add the param type (string) to the writer 81 writer.addInt16(0 /* ParamType.STRING */); 82 paramWriter.addInt32(Buffer.byteLength(mappedVal)); 83 paramWriter.addString(mappedVal); 84 } 85 } 86 }; 87 const bind = (config = {}) => { 88 // normalize config 89 const portal = config.portal || ''; 90 const statement = config.statement || ''; 91 const binary = config.binary || false; 92 const values = config.values || emptyArray; 93 const len = values.length; 94 writer.addCString(portal).addCString(statement); 95 writer.addInt16(len); 96 writeValues(values, config.valueMapper); 97 writer.addInt16(len); 98 writer.add(paramWriter.flush()); 99 // all results use the same format code 100 writer.addInt16(1); 101 // format code 102 writer.addInt16(binary ? 1 /* ParamType.BINARY */ : 0 /* ParamType.STRING */); 103 return writer.flush(66 /* code.bind */); 104 }; 105 const emptyExecute = Buffer.from([69 /* code.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 /* code.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 /* code.describe */); 145 const emptyDescribeStatement = writer.addCString('S').flush(68 /* code.describe */); 146 const describe = (msg) => { 147 return msg.name 148 ? cstringMessage(68 /* code.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 /* code.close */, text); 156 }; 157 const copyData = (chunk) => { 158 return writer.add(chunk).flush(100 /* code.copyFromChunk */); 159 }; 160 const copyFail = (message) => { 161 return cstringMessage(102 /* code.copyFail */, message); 162 }; 163 const codeOnlyBuffer = (code) => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]); 164 const flushBuffer = codeOnlyBuffer(72 /* code.flush */); 165 const syncBuffer = codeOnlyBuffer(83 /* code.sync */); 166 const endBuffer = codeOnlyBuffer(88 /* code.end */); 167 const copyDoneBuffer = codeOnlyBuffer(99 /* code.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