diff --git a/.eslintrc b/.eslintrc index 968b93e5..2840a364 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,14 +2,15 @@ "plugins": [ "prettier" ], + "parser": "@typescript-eslint/parser", "extends": [ - "plugin:prettier/recommended" + "plugin:prettier/recommended", + "prettier/@typescript-eslint" ], "ignorePatterns": [ - "**/*.ts", "node_modules", "packages/pg", - "packages/pg-protocol", + "packages/pg-protocol/dist/**/*", "packages/pg-pool" ], "parserOptions": { diff --git a/package.json b/package.json index 0e2841fd..83867563 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,11 @@ "test": "yarn lerna exec yarn test", "build": "yarn lerna exec --scope pg-protocol yarn build", "pretest": "yarn build", - "lint": "yarn lerna exec --parallel yarn lint" + "lint": "eslint '*/**/*.{js,ts,tsx}'" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^2.27.0", + "@typescript-eslint/parser": "^2.27.0", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.1", "eslint-plugin-node": "^11.1.0", diff --git a/packages/pg-protocol/src/b.ts b/packages/pg-protocol/src/b.ts index dbf9f52e..27a24c6a 100644 --- a/packages/pg-protocol/src/b.ts +++ b/packages/pg-protocol/src/b.ts @@ -1,28 +1,28 @@ -// file for microbenchmarking +// file for microbenchmarking -import { Writer } from './buffer-writer' -import { serialize } from './index' -import { BufferReader } from './buffer-reader' +import { Writer } from './buffer-writer'; +import { serialize } from './index'; +import { BufferReader } from './buffer-reader'; -const LOOPS = 1000 -let count = 0 -let start = Date.now() -const writer = new Writer() +const LOOPS = 1000; +let count = 0; +let start = Date.now(); +const writer = new Writer(); -const reader = new BufferReader() -const buffer = Buffer.from([33, 33, 33, 33, 33, 33, 33, 0]) +const reader = new BufferReader(); +const buffer = Buffer.from([33, 33, 33, 33, 33, 33, 33, 0]); const run = () => { if (count > LOOPS) { - console.log(Date.now() - start) + console.log(Date.now() - start); return; } - count++ - for(let i = 0; i < LOOPS; i++) { - reader.setBuffer(0, buffer) - reader.cstring() + count++; + for (let i = 0; i < LOOPS; i++) { + reader.setBuffer(0, buffer); + reader.cstring(); } - setImmediate(run) -} + setImmediate(run); +}; -run() +run(); diff --git a/packages/pg-protocol/src/buffer-reader.ts b/packages/pg-protocol/src/buffer-reader.ts index cb7d4e3b..62ea8524 100644 --- a/packages/pg-protocol/src/buffer-reader.ts +++ b/packages/pg-protocol/src/buffer-reader.ts @@ -6,8 +6,7 @@ export class BufferReader { // TODO(bmc): support non-utf8 encoding? private encoding: string = 'utf-8'; - constructor(private offset: number = 0) { - } + constructor(private offset: number = 0) {} public setBuffer(offset: number, buffer: Buffer): void { this.offset = offset; @@ -40,8 +39,8 @@ export class BufferReader { public cstring(): string { const start = this.offset; - let end = start - while(this.buffer[end++] !== 0) { }; + let end = start; + while (this.buffer[end++] !== 0) {} this.offset = end; return this.buffer.toString(this.encoding, start, end - 1); } diff --git a/packages/pg-protocol/src/buffer-writer.ts b/packages/pg-protocol/src/buffer-writer.ts index 2299070d..58efb3b2 100644 --- a/packages/pg-protocol/src/buffer-writer.ts +++ b/packages/pg-protocol/src/buffer-writer.ts @@ -5,7 +5,7 @@ export class Writer { private offset: number = 5; private headerPosition: number = 0; constructor(private size = 256) { - this.buffer = Buffer.alloc(size) + this.buffer = Buffer.alloc(size); } private ensure(size: number): void { @@ -22,28 +22,27 @@ export class Writer { public addInt32(num: number): Writer { this.ensure(4); - this.buffer[this.offset++] = (num >>> 24 & 0xFF); - this.buffer[this.offset++] = (num >>> 16 & 0xFF); - this.buffer[this.offset++] = (num >>> 8 & 0xFF); - this.buffer[this.offset++] = (num >>> 0 & 0xFF); + this.buffer[this.offset++] = (num >>> 24) & 0xff; + this.buffer[this.offset++] = (num >>> 16) & 0xff; + this.buffer[this.offset++] = (num >>> 8) & 0xff; + this.buffer[this.offset++] = (num >>> 0) & 0xff; return this; } public addInt16(num: number): Writer { this.ensure(2); - this.buffer[this.offset++] = (num >>> 8 & 0xFF); - this.buffer[this.offset++] = (num >>> 0 & 0xFF); + this.buffer[this.offset++] = (num >>> 8) & 0xff; + this.buffer[this.offset++] = (num >>> 0) & 0xff; return this; } - public addCString(string: string): Writer { if (!string) { this.ensure(1); } else { var len = Buffer.byteLength(string); this.ensure(len + 1); // +1 for null terminator - this.buffer.write(string, this.offset, 'utf-8') + this.buffer.write(string, this.offset, 'utf-8'); this.offset += len; } @@ -51,7 +50,7 @@ export class Writer { return this; } - public addString(string: string = ""): Writer { + public addString(string: string = ''): Writer { var len = Buffer.byteLength(string); this.ensure(len); this.buffer.write(string, this.offset); @@ -70,8 +69,8 @@ export class Writer { if (code) { this.buffer[this.headerPosition] = code; //length is everything in this packet minus the code - const length = this.offset - (this.headerPosition + 1) - this.buffer.writeInt32BE(length, this.headerPosition + 1) + const length = this.offset - (this.headerPosition + 1); + this.buffer.writeInt32BE(length, this.headerPosition + 1); } return this.buffer.slice(code ? 0 : 5, this.offset); } @@ -80,8 +79,7 @@ export class Writer { var result = this.join(code); this.offset = 5; this.headerPosition = 0; - this.buffer = Buffer.allocUnsafe(this.size) + this.buffer = Buffer.allocUnsafe(this.size); return result; } } - diff --git a/packages/pg-protocol/src/inbound-parser.test.ts b/packages/pg-protocol/src/inbound-parser.test.ts index 461ab262..f50e95be 100644 --- a/packages/pg-protocol/src/inbound-parser.test.ts +++ b/packages/pg-protocol/src/inbound-parser.test.ts @@ -1,28 +1,29 @@ -import buffers from './testing/test-buffers' -import BufferList from './testing/buffer-list' -import { parse } from '.' -import assert from 'assert' -import { PassThrough } from 'stream' -import { BackendMessage } from './messages' +import buffers from './testing/test-buffers'; +import BufferList from './testing/buffer-list'; +import { parse } from '.'; +import assert from 'assert'; +import { PassThrough } from 'stream'; +import { BackendMessage } from './messages'; -var authOkBuffer = buffers.authenticationOk() -var paramStatusBuffer = buffers.parameterStatus('client_encoding', 'UTF8') -var readyForQueryBuffer = buffers.readyForQuery() -var backendKeyDataBuffer = buffers.backendKeyData(1, 2) -var commandCompleteBuffer = buffers.commandComplete('SELECT 3') -var parseCompleteBuffer = buffers.parseComplete() -var bindCompleteBuffer = buffers.bindComplete() -var portalSuspendedBuffer = buffers.portalSuspended() +var authOkBuffer = buffers.authenticationOk(); +var paramStatusBuffer = buffers.parameterStatus('client_encoding', 'UTF8'); +var readyForQueryBuffer = buffers.readyForQuery(); +var backendKeyDataBuffer = buffers.backendKeyData(1, 2); +var commandCompleteBuffer = buffers.commandComplete('SELECT 3'); +var parseCompleteBuffer = buffers.parseComplete(); +var bindCompleteBuffer = buffers.bindComplete(); +var portalSuspendedBuffer = buffers.portalSuspended(); var addRow = function (bufferList: BufferList, name: string, offset: number) { - return bufferList.addCString(name) // field name + return bufferList + .addCString(name) // field name .addInt32(offset++) // table id .addInt16(offset++) // attribute of column number .addInt32(offset++) // objectId of field's data type .addInt16(offset++) // datatype size .addInt32(offset++) // type modifier - .addInt16(0) // format code, 0 => text -} + .addInt16(0); // format code, 0 => text +}; var row1 = { name: 'id', @@ -31,274 +32,291 @@ var row1 = { dataTypeID: 3, dataTypeSize: 4, typeModifier: 5, - formatCode: 0 -} -var oneRowDescBuff = buffers.rowDescription([row1]) -row1.name = 'bang' + formatCode: 0, +}; +var oneRowDescBuff = buffers.rowDescription([row1]); +row1.name = 'bang'; -var twoRowBuf = buffers.rowDescription([row1, { - name: 'whoah', - tableID: 10, - attributeNumber: 11, - dataTypeID: 12, - dataTypeSize: 13, - typeModifier: 14, - formatCode: 0 -}]) +var twoRowBuf = buffers.rowDescription([ + row1, + { + name: 'whoah', + tableID: 10, + attributeNumber: 11, + dataTypeID: 12, + dataTypeSize: 13, + typeModifier: 14, + formatCode: 0, + }, +]); -var emptyRowFieldBuf = new BufferList() - .addInt16(0) - .join(true, 'D') +var emptyRowFieldBuf = new BufferList().addInt16(0).join(true, 'D'); -var emptyRowFieldBuf = buffers.dataRow([]) +var emptyRowFieldBuf = buffers.dataRow([]); var oneFieldBuf = new BufferList() .addInt16(1) // number of fields .addInt32(5) // length of bytes of fields .addCString('test') - .join(true, 'D') + .join(true, 'D'); -var oneFieldBuf = buffers.dataRow(['test']) +var oneFieldBuf = buffers.dataRow(['test']); var expectedAuthenticationOkayMessage = { name: 'authenticationOk', - length: 8 -} + length: 8, +}; var expectedParameterStatusMessage = { name: 'parameterStatus', parameterName: 'client_encoding', parameterValue: 'UTF8', - length: 25 -} + length: 25, +}; var expectedBackendKeyDataMessage = { name: 'backendKeyData', processID: 1, - secretKey: 2 -} + secretKey: 2, +}; var expectedReadyForQueryMessage = { name: 'readyForQuery', length: 5, - status: 'I' -} + status: 'I', +}; var expectedCommandCompleteMessage = { name: 'commandComplete', length: 13, - text: 'SELECT 3' -} + text: 'SELECT 3', +}; var emptyRowDescriptionBuffer = new BufferList() .addInt16(0) // number of fields - .join(true, 'T') + .join(true, 'T'); var expectedEmptyRowDescriptionMessage = { name: 'rowDescription', length: 6, fieldCount: 0, fields: [], -} +}; var expectedOneRowMessage = { name: 'rowDescription', length: 27, fieldCount: 1, - fields: [{ - name: 'id', - tableID: 1, - columnID: 2, - dataTypeID: 3, - dataTypeSize: 4, - dataTypeModifier: 5, - format: 'text' - }] -} + fields: [ + { + name: 'id', + tableID: 1, + columnID: 2, + dataTypeID: 3, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + ], +}; var expectedTwoRowMessage = { name: 'rowDescription', length: 53, fieldCount: 2, - fields: [{ - name: 'bang', - tableID: 1, - columnID: 2, - dataTypeID: 3, - dataTypeSize: 4, - dataTypeModifier: 5, - format: 'text' - }, - { - name: 'whoah', - tableID: 10, - columnID: 11, - dataTypeID: 12, - dataTypeSize: 13, - dataTypeModifier: 14, - format: 'text' - }] -} + fields: [ + { + name: 'bang', + tableID: 1, + columnID: 2, + dataTypeID: 3, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + { + name: 'whoah', + tableID: 10, + columnID: 11, + dataTypeID: 12, + dataTypeSize: 13, + dataTypeModifier: 14, + format: 'text', + }, + ], +}; var testForMessage = function (buffer: Buffer, expectedMessage: any) { it('recieves and parses ' + expectedMessage.name, async () => { - const messages = await parseBuffers([buffer]) + const messages = await parseBuffers([buffer]); const [lastMessage] = messages; for (const key in expectedMessage) { - assert.deepEqual((lastMessage as any)[key], expectedMessage[key]) + assert.deepEqual((lastMessage as any)[key], expectedMessage[key]); } - }) -} + }); +}; -var plainPasswordBuffer = buffers.authenticationCleartextPassword() -var md5PasswordBuffer = buffers.authenticationMD5Password() -var SASLBuffer = buffers.authenticationSASL() -var SASLContinueBuffer = buffers.authenticationSASLContinue() -var SASLFinalBuffer = buffers.authenticationSASLFinal() +var plainPasswordBuffer = buffers.authenticationCleartextPassword(); +var md5PasswordBuffer = buffers.authenticationMD5Password(); +var SASLBuffer = buffers.authenticationSASL(); +var SASLContinueBuffer = buffers.authenticationSASLContinue(); +var SASLFinalBuffer = buffers.authenticationSASLFinal(); var expectedPlainPasswordMessage = { - name: 'authenticationCleartextPassword' -} + name: 'authenticationCleartextPassword', +}; var expectedMD5PasswordMessage = { name: 'authenticationMD5Password', - salt: Buffer.from([1, 2, 3, 4]) -} + salt: Buffer.from([1, 2, 3, 4]), +}; var expectedSASLMessage = { name: 'authenticationSASL', - mechanisms: ['SCRAM-SHA-256'] -} + mechanisms: ['SCRAM-SHA-256'], +}; var expectedSASLContinueMessage = { name: 'authenticationSASLContinue', data: 'data', -} +}; var expectedSASLFinalMessage = { name: 'authenticationSASLFinal', data: 'data', -} +}; -var notificationResponseBuffer = buffers.notification(4, 'hi', 'boom') +var notificationResponseBuffer = buffers.notification(4, 'hi', 'boom'); var expectedNotificationResponseMessage = { name: 'notification', processId: 4, channel: 'hi', - payload: 'boom' -} - - + payload: 'boom', +}; const parseBuffers = async (buffers: Buffer[]): Promise => { const stream = new PassThrough(); for (const buffer of buffers) { stream.write(buffer); } - stream.end() - const msgs: BackendMessage[] = [] - await parse(stream, (msg) => msgs.push(msg)) - return msgs -} + stream.end(); + const msgs: BackendMessage[] = []; + await parse(stream, (msg) => msgs.push(msg)); + return msgs; +}; describe('PgPacketStream', function () { - testForMessage(authOkBuffer, expectedAuthenticationOkayMessage) - testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage) - testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage) - testForMessage(SASLBuffer, expectedSASLMessage) - testForMessage(SASLContinueBuffer, expectedSASLContinueMessage) - testForMessage(SASLFinalBuffer, expectedSASLFinalMessage) + testForMessage(authOkBuffer, expectedAuthenticationOkayMessage); + testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage); + testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage); + testForMessage(SASLBuffer, expectedSASLMessage); + testForMessage(SASLContinueBuffer, expectedSASLContinueMessage); + testForMessage(SASLFinalBuffer, expectedSASLFinalMessage); - testForMessage(paramStatusBuffer, expectedParameterStatusMessage) - testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage) - testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage) - testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage) - testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage) + testForMessage(paramStatusBuffer, expectedParameterStatusMessage); + testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage); + testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage); + testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage); + testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage); testForMessage(buffers.emptyQuery(), { name: 'emptyQuery', length: 4, - }) + }); testForMessage(Buffer.from([0x6e, 0, 0, 0, 4]), { - name: 'noData' - }) + name: 'noData', + }); describe('rowDescription messages', function () { - testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage) - testForMessage(oneRowDescBuff, expectedOneRowMessage) - testForMessage(twoRowBuf, expectedTwoRowMessage) - }) + testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage); + testForMessage(oneRowDescBuff, expectedOneRowMessage); + testForMessage(twoRowBuf, expectedTwoRowMessage); + }); describe('parsing rows', function () { describe('parsing empty row', function () { testForMessage(emptyRowFieldBuf, { name: 'dataRow', - fieldCount: 0 - }) - }) + fieldCount: 0, + }); + }); describe('parsing data row with fields', function () { testForMessage(oneFieldBuf, { name: 'dataRow', fieldCount: 1, - fields: ['test'] - }) - }) - }) + fields: ['test'], + }); + }); + }); describe('notice message', function () { // this uses the same logic as error message - var buff = buffers.notice([{ type: 'C', value: 'code' }]) + var buff = buffers.notice([{ type: 'C', value: 'code' }]); testForMessage(buff, { name: 'notice', - code: 'code' - }) - }) + code: 'code', + }); + }); testForMessage(buffers.error([]), { - name: 'error' - }) + name: 'error', + }); describe('with all the fields', function () { - var buffer = buffers.error([{ - type: 'S', - value: 'ERROR' - }, { - type: 'C', - value: 'code' - }, { - type: 'M', - value: 'message' - }, { - type: 'D', - value: 'details' - }, { - type: 'H', - value: 'hint' - }, { - type: 'P', - value: '100' - }, { - type: 'p', - value: '101' - }, { - type: 'q', - value: 'query' - }, { - type: 'W', - value: 'where' - }, { - type: 'F', - value: 'file' - }, { - type: 'L', - value: 'line' - }, { - type: 'R', - value: 'routine' - }, { - type: 'Z', // ignored - value: 'alsdkf' - }]) + var buffer = buffers.error([ + { + type: 'S', + value: 'ERROR', + }, + { + type: 'C', + value: 'code', + }, + { + type: 'M', + value: 'message', + }, + { + type: 'D', + value: 'details', + }, + { + type: 'H', + value: 'hint', + }, + { + type: 'P', + value: '100', + }, + { + type: 'p', + value: '101', + }, + { + type: 'q', + value: 'query', + }, + { + type: 'W', + value: 'where', + }, + { + type: 'F', + value: 'file', + }, + { + type: 'L', + value: 'line', + }, + { + type: 'R', + value: 'routine', + }, + { + type: 'Z', // ignored + value: 'alsdkf', + }, + ]); testForMessage(buffer, { name: 'error', @@ -313,184 +331,179 @@ describe('PgPacketStream', function () { where: 'where', file: 'file', line: 'line', - routine: 'routine' - }) - }) + routine: 'routine', + }); + }); testForMessage(parseCompleteBuffer, { - name: 'parseComplete' - }) + name: 'parseComplete', + }); testForMessage(bindCompleteBuffer, { - name: 'bindComplete' - }) + name: 'bindComplete', + }); testForMessage(bindCompleteBuffer, { - name: 'bindComplete' - }) + name: 'bindComplete', + }); testForMessage(buffers.closeComplete(), { - name: 'closeComplete' - }) + name: 'closeComplete', + }); describe('parses portal suspended message', function () { testForMessage(portalSuspendedBuffer, { - name: 'portalSuspended' - }) - }) + name: 'portalSuspended', + }); + }); describe('parses replication start message', function () { testForMessage(Buffer.from([0x57, 0x00, 0x00, 0x00, 0x04]), { name: 'replicationStart', - length: 4 - }) - }) + length: 4, + }); + }); describe('copy', () => { testForMessage(buffers.copyIn(0), { name: 'copyInResponse', length: 7, binary: false, - columnTypes: [] - }) + columnTypes: [], + }); testForMessage(buffers.copyIn(2), { name: 'copyInResponse', length: 11, binary: false, - columnTypes: [0, 1] - }) + columnTypes: [0, 1], + }); testForMessage(buffers.copyOut(0), { name: 'copyOutResponse', length: 7, binary: false, - columnTypes: [] - }) + columnTypes: [], + }); testForMessage(buffers.copyOut(3), { name: 'copyOutResponse', length: 13, binary: false, - columnTypes: [0, 1, 2] - }) + columnTypes: [0, 1, 2], + }); testForMessage(buffers.copyDone(), { name: 'copyDone', length: 4, - }) + }); testForMessage(buffers.copyData(Buffer.from([5, 6, 7])), { name: 'copyData', length: 7, - chunk: Buffer.from([5, 6, 7]) - }) - }) - + chunk: Buffer.from([5, 6, 7]), + }); + }); // since the data message on a stream can randomly divide the incomming // tcp packets anywhere, we need to make sure we can parse every single // split on a tcp message describe('split buffer, single message parsing', function () { - var fullBuffer = buffers.dataRow([null, 'bang', 'zug zug', null, '!']) + var fullBuffer = buffers.dataRow([null, 'bang', 'zug zug', null, '!']); it('parses when full buffer comes in', async function () { const messages = await parseBuffers([fullBuffer]); - const message = messages[0] as any - assert.equal(message.fields.length, 5) - assert.equal(message.fields[0], null) - assert.equal(message.fields[1], 'bang') - assert.equal(message.fields[2], 'zug zug') - assert.equal(message.fields[3], null) - assert.equal(message.fields[4], '!') - }) + const message = messages[0] as any; + assert.equal(message.fields.length, 5); + assert.equal(message.fields[0], null); + assert.equal(message.fields[1], 'bang'); + assert.equal(message.fields[2], 'zug zug'); + assert.equal(message.fields[3], null); + assert.equal(message.fields[4], '!'); + }); var testMessageRecievedAfterSpiltAt = async function (split: number) { - var firstBuffer = Buffer.alloc(fullBuffer.length - split) - var secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length) - fullBuffer.copy(firstBuffer, 0, 0) - fullBuffer.copy(secondBuffer, 0, firstBuffer.length) + var firstBuffer = Buffer.alloc(fullBuffer.length - split); + var secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length); + fullBuffer.copy(firstBuffer, 0, 0); + fullBuffer.copy(secondBuffer, 0, firstBuffer.length); const messages = await parseBuffers([fullBuffer]); - const message = messages[0] as any - assert.equal(message.fields.length, 5) - assert.equal(message.fields[0], null) - assert.equal(message.fields[1], 'bang') - assert.equal(message.fields[2], 'zug zug') - assert.equal(message.fields[3], null) - assert.equal(message.fields[4], '!') - } + const message = messages[0] as any; + assert.equal(message.fields.length, 5); + assert.equal(message.fields[0], null); + assert.equal(message.fields[1], 'bang'); + assert.equal(message.fields[2], 'zug zug'); + assert.equal(message.fields[3], null); + assert.equal(message.fields[4], '!'); + }; it('parses when split in the middle', function () { - testMessageRecievedAfterSpiltAt(6) - }) + testMessageRecievedAfterSpiltAt(6); + }); it('parses when split at end', function () { - testMessageRecievedAfterSpiltAt(2) - }) + testMessageRecievedAfterSpiltAt(2); + }); it('parses when split at beginning', function () { - testMessageRecievedAfterSpiltAt(fullBuffer.length - 2) - testMessageRecievedAfterSpiltAt(fullBuffer.length - 1) - testMessageRecievedAfterSpiltAt(fullBuffer.length - 5) - }) - }) + testMessageRecievedAfterSpiltAt(fullBuffer.length - 2); + testMessageRecievedAfterSpiltAt(fullBuffer.length - 1); + testMessageRecievedAfterSpiltAt(fullBuffer.length - 5); + }); + }); describe('split buffer, multiple message parsing', function () { - var dataRowBuffer = buffers.dataRow(['!']) - var readyForQueryBuffer = buffers.readyForQuery() - var fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length) - dataRowBuffer.copy(fullBuffer, 0, 0) - readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0) + var dataRowBuffer = buffers.dataRow(['!']); + var readyForQueryBuffer = buffers.readyForQuery(); + var fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length); + dataRowBuffer.copy(fullBuffer, 0, 0); + readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0); var verifyMessages = function (messages: any[]) { - assert.strictEqual(messages.length, 2) + assert.strictEqual(messages.length, 2); assert.deepEqual(messages[0], { name: 'dataRow', fieldCount: 1, length: 11, - fields: ['!'] - }) - assert.equal(messages[0].fields[0], '!') + fields: ['!'], + }); + assert.equal(messages[0].fields[0], '!'); assert.deepEqual(messages[1], { name: 'readyForQuery', length: 5, - status: 'I' - }) - } + status: 'I', + }); + }; // sanity check it('recieves both messages when packet is not split', async function () { - const messages = await parseBuffers([fullBuffer]) - verifyMessages(messages) - }) + const messages = await parseBuffers([fullBuffer]); + verifyMessages(messages); + }); var splitAndVerifyTwoMessages = async function (split: number) { - var firstBuffer = Buffer.alloc(fullBuffer.length - split) - var secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length) - fullBuffer.copy(firstBuffer, 0, 0) - fullBuffer.copy(secondBuffer, 0, firstBuffer.length) - const messages = await parseBuffers([firstBuffer, secondBuffer]) - verifyMessages(messages) - } + var firstBuffer = Buffer.alloc(fullBuffer.length - split); + var secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length); + fullBuffer.copy(firstBuffer, 0, 0); + fullBuffer.copy(secondBuffer, 0, firstBuffer.length); + const messages = await parseBuffers([firstBuffer, secondBuffer]); + verifyMessages(messages); + }; describe('recieves both messages when packet is split', function () { it('in the middle', function () { - return splitAndVerifyTwoMessages(11) - }) + return splitAndVerifyTwoMessages(11); + }); it('at the front', function () { return Promise.all([ splitAndVerifyTwoMessages(fullBuffer.length - 1), splitAndVerifyTwoMessages(fullBuffer.length - 4), - splitAndVerifyTwoMessages(fullBuffer.length - 6) - ]) - }) + splitAndVerifyTwoMessages(fullBuffer.length - 6), + ]); + }); it('at the end', function () { - return Promise.all([ - splitAndVerifyTwoMessages(8), - splitAndVerifyTwoMessages(1) - ]) - }) - }) - }) - -}) + return Promise.all([splitAndVerifyTwoMessages(8), splitAndVerifyTwoMessages(1)]); + }); + }); + }); +}); diff --git a/packages/pg-protocol/src/index.ts b/packages/pg-protocol/src/index.ts index f4ade017..57580f6e 100644 --- a/packages/pg-protocol/src/index.ts +++ b/packages/pg-protocol/src/index.ts @@ -1,11 +1,11 @@ import { BackendMessage } from './messages'; import { serialize } from './serializer'; -import { Parser, MessageCallback } from './parser' +import { Parser, MessageCallback } from './parser'; export function parse(stream: NodeJS.ReadableStream, callback: MessageCallback): Promise { - const parser = new Parser() - stream.on('data', (buffer: Buffer) => parser.parse(buffer, callback)) - return new Promise((resolve) => stream.on('end', () => resolve())) + const parser = new Parser(); + stream.on('data', (buffer: Buffer) => parser.parse(buffer, callback)); + return new Promise((resolve) => stream.on('end', () => resolve())); } export { serialize }; diff --git a/packages/pg-protocol/src/messages.ts b/packages/pg-protocol/src/messages.ts index 222a2490..20d17f1d 100644 --- a/packages/pg-protocol/src/messages.ts +++ b/packages/pg-protocol/src/messages.ts @@ -42,37 +42,37 @@ export const parseComplete: BackendMessage = { export const bindComplete: BackendMessage = { name: MessageName.bindComplete, length: 5, -} +}; export const closeComplete: BackendMessage = { name: MessageName.closeComplete, length: 5, -} +}; export const noData: BackendMessage = { name: MessageName.noData, - length: 5 -} + length: 5, +}; export const portalSuspended: BackendMessage = { name: MessageName.portalSuspended, length: 5, -} +}; export const replicationStart: BackendMessage = { name: MessageName.replicationStart, length: 4, -} +}; export const emptyQuery: BackendMessage = { name: MessageName.emptyQuery, length: 4, -} +}; export const copyDone: BackendMessage = { name: MessageName.copyDone, length: 4, -} +}; interface NoticeOrError { message: string | undefined; @@ -112,77 +112,89 @@ export class DatabaseError extends Error implements NoticeOrError { public line: string | undefined; public routine: string | undefined; constructor(message: string, public readonly length: number, public readonly name: MessageName) { - super(message) + super(message); } } export class CopyDataMessage { public readonly name = MessageName.copyData; - constructor(public readonly length: number, public readonly chunk: Buffer) { - - } + constructor(public readonly length: number, public readonly chunk: Buffer) {} } export class CopyResponse { public readonly columnTypes: number[]; - constructor(public readonly length: number, public readonly name: MessageName, public readonly binary: boolean, columnCount: number) { + constructor( + public readonly length: number, + public readonly name: MessageName, + public readonly binary: boolean, + columnCount: number + ) { this.columnTypes = new Array(columnCount); } } export class Field { - constructor(public readonly name: string, public readonly tableID: number, public readonly columnID: number, public readonly dataTypeID: number, public readonly dataTypeSize: number, public readonly dataTypeModifier: number, public readonly format: Mode) { - } + constructor( + public readonly name: string, + public readonly tableID: number, + public readonly columnID: number, + public readonly dataTypeID: number, + public readonly dataTypeSize: number, + public readonly dataTypeModifier: number, + public readonly format: Mode + ) {} } export class RowDescriptionMessage { public readonly name: MessageName = MessageName.rowDescription; public readonly fields: Field[]; constructor(public readonly length: number, public readonly fieldCount: number) { - this.fields = new Array(this.fieldCount) + this.fields = new Array(this.fieldCount); } } export class ParameterStatusMessage { public readonly name: MessageName = MessageName.parameterStatus; - constructor(public readonly length: number, public readonly parameterName: string, public readonly parameterValue: string) { - - } + constructor( + public readonly length: number, + public readonly parameterName: string, + public readonly parameterValue: string + ) {} } export class AuthenticationMD5Password implements BackendMessage { public readonly name: MessageName = MessageName.authenticationMD5Password; - constructor(public readonly length: number, public readonly salt: Buffer) { - } + constructor(public readonly length: number, public readonly salt: Buffer) {} } export class BackendKeyDataMessage { public readonly name: MessageName = MessageName.backendKeyData; - constructor(public readonly length: number, public readonly processID: number, public readonly secretKey: number) { - } + constructor(public readonly length: number, public readonly processID: number, public readonly secretKey: number) {} } export class NotificationResponseMessage { public readonly name: MessageName = MessageName.notification; - constructor(public readonly length: number, public readonly processId: number, public readonly channel: string, public readonly payload: string) { - } + constructor( + public readonly length: number, + public readonly processId: number, + public readonly channel: string, + public readonly payload: string + ) {} } export class ReadyForQueryMessage { public readonly name: MessageName = MessageName.readyForQuery; - constructor(public readonly length: number, public readonly status: string) { - } + constructor(public readonly length: number, public readonly status: string) {} } export class CommandCompleteMessage { - public readonly name: MessageName = MessageName.commandComplete - constructor(public readonly length: number, public readonly text: string) { - } + public readonly name: MessageName = MessageName.commandComplete; + constructor(public readonly length: number, public readonly text: string) {} } export class DataRowMessage { public readonly fieldCount: number; - public readonly name: MessageName = MessageName.dataRow + public readonly name: MessageName = MessageName.dataRow; constructor(public length: number, public fields: any[]) { this.fieldCount = fields.length; } diff --git a/packages/pg-protocol/src/outbound-serializer.test.ts b/packages/pg-protocol/src/outbound-serializer.test.ts index 110b932c..c2ef22db 100644 --- a/packages/pg-protocol/src/outbound-serializer.test.ts +++ b/packages/pg-protocol/src/outbound-serializer.test.ts @@ -1,85 +1,79 @@ -import assert from 'assert' -import { serialize } from './serializer' -import BufferList from './testing/buffer-list' +import assert from 'assert'; +import { serialize } from './serializer'; +import BufferList from './testing/buffer-list'; describe('serializer', () => { it('builds startup message', function () { const actual = serialize.startup({ user: 'brian', - database: 'bang' - }) - assert.deepEqual(actual, new BufferList() - .addInt16(3) - .addInt16(0) - .addCString('user') - .addCString('brian') - .addCString('database') - .addCString('bang') - .addCString('client_encoding') - .addCString("'utf-8'") - .addCString('').join(true)) - }) + database: 'bang', + }); + assert.deepEqual( + actual, + new BufferList() + .addInt16(3) + .addInt16(0) + .addCString('user') + .addCString('brian') + .addCString('database') + .addCString('bang') + .addCString('client_encoding') + .addCString("'utf-8'") + .addCString('') + .join(true) + ); + }); it('builds password message', function () { - const actual = serialize.password('!') - assert.deepEqual(actual, new BufferList().addCString('!').join(true, 'p')) - }) + const actual = serialize.password('!'); + assert.deepEqual(actual, new BufferList().addCString('!').join(true, 'p')); + }); it('builds request ssl message', function () { - const actual = serialize.requestSsl() - const expected = new BufferList().addInt32(80877103).join(true) + const actual = serialize.requestSsl(); + const expected = new BufferList().addInt32(80877103).join(true); assert.deepEqual(actual, expected); - }) + }); it('builds SASLInitialResponseMessage message', function () { - const actual = serialize.sendSASLInitialResponseMessage('mech', 'data') - assert.deepEqual(actual, new BufferList().addCString('mech').addInt32(4).addString('data').join(true, 'p')) - }) - + const actual = serialize.sendSASLInitialResponseMessage('mech', 'data'); + assert.deepEqual(actual, new BufferList().addCString('mech').addInt32(4).addString('data').join(true, 'p')); + }); it('builds SCRAMClientFinalMessage message', function () { - const actual = serialize.sendSCRAMClientFinalMessage('data') - assert.deepEqual(actual, new BufferList().addString('data').join(true, 'p')) - }) - + const actual = serialize.sendSCRAMClientFinalMessage('data'); + assert.deepEqual(actual, new BufferList().addString('data').join(true, 'p')); + }); it('builds query message', function () { - var txt = 'select * from boom' - const actual = serialize.query(txt) - assert.deepEqual(actual, new BufferList().addCString(txt).join(true, 'Q')) - }) - + var txt = 'select * from boom'; + const actual = serialize.query(txt); + assert.deepEqual(actual, new BufferList().addCString(txt).join(true, 'Q')); + }); describe('parse message', () => { - it('builds parse message', function () { - const actual = serialize.parse({ text: '!' }) - var expected = new BufferList() - .addCString('') - .addCString('!') - .addInt16(0).join(true, 'P') - assert.deepEqual(actual, expected) - }) + const actual = serialize.parse({ text: '!' }); + var expected = new BufferList().addCString('').addCString('!').addInt16(0).join(true, 'P'); + assert.deepEqual(actual, expected); + }); it('builds parse message with named query', function () { const actual = serialize.parse({ name: 'boom', text: 'select * from boom', - types: [] - }) - var expected = new BufferList() - .addCString('boom') - .addCString('select * from boom') - .addInt16(0).join(true, 'P') - assert.deepEqual(actual, expected) - }) + types: [], + }); + var expected = new BufferList().addCString('boom').addCString('select * from boom').addInt16(0).join(true, 'P'); + assert.deepEqual(actual, expected); + }); it('with multiple parameters', function () { const actual = serialize.parse({ name: 'force', text: 'select * from bang where name = $1', - types: [1, 2, 3, 4] - }) + types: [1, 2, 3, 4], + }); var expected = new BufferList() .addCString('force') .addCString('select * from bang where name = $1') @@ -87,16 +81,15 @@ describe('serializer', () => { .addInt32(1) .addInt32(2) .addInt32(3) - .addInt32(4).join(true, 'P') - assert.deepEqual(actual, expected) - }) - - }) - + .addInt32(4) + .join(true, 'P'); + assert.deepEqual(actual, expected); + }); + }); describe('bind messages', function () { it('with no values', function () { - const actual = serialize.bind() + const actual = serialize.bind(); var expectedBuffer = new BufferList() .addCString('') @@ -104,18 +97,18 @@ describe('serializer', () => { .addInt16(0) .addInt16(0) .addInt16(0) - .join(true, 'B') - assert.deepEqual(actual, expectedBuffer) - }) + .join(true, 'B'); + assert.deepEqual(actual, expectedBuffer); + }); it('with named statement, portal, and values', function () { const actual = serialize.bind({ portal: 'bang', statement: 'woo', - values: ['1', 'hi', null, 'zing'] - }) + values: ['1', 'hi', null, 'zing'], + }); var expectedBuffer = new BufferList() - .addCString('bang') // portal name + .addCString('bang') // portal name .addCString('woo') // statement name .addInt16(0) .addInt16(4) @@ -127,25 +120,25 @@ describe('serializer', () => { .addInt32(4) .add(Buffer.from('zing')) .addInt16(0) - .join(true, 'B') - assert.deepEqual(actual, expectedBuffer) - }) - }) + .join(true, 'B'); + assert.deepEqual(actual, expectedBuffer); + }); + }); it('with named statement, portal, and buffer value', function () { const actual = serialize.bind({ portal: 'bang', statement: 'woo', - values: ['1', 'hi', null, Buffer.from('zing', 'utf8')] - }) + values: ['1', 'hi', null, Buffer.from('zing', 'utf8')], + }); var expectedBuffer = new BufferList() - .addCString('bang') // portal name + .addCString('bang') // portal name .addCString('woo') // statement name - .addInt16(4)// value count - .addInt16(0)// string - .addInt16(0)// string - .addInt16(0)// string - .addInt16(1)// binary + .addInt16(4) // value count + .addInt16(0) // string + .addInt16(0) // string + .addInt16(0) // string + .addInt16(1) // binary .addInt16(4) .addInt32(1) .add(Buffer.from('1')) @@ -155,102 +148,96 @@ describe('serializer', () => { .addInt32(4) .add(Buffer.from('zing', 'utf-8')) .addInt16(0) - .join(true, 'B') - assert.deepEqual(actual, expectedBuffer) - }) + .join(true, 'B'); + assert.deepEqual(actual, expectedBuffer); + }); describe('builds execute message', function () { it('for unamed portal with no row limit', function () { - const actual = serialize.execute() - var expectedBuffer = new BufferList() - .addCString('') - .addInt32(0) - .join(true, 'E') - assert.deepEqual(actual, expectedBuffer) - }) + const actual = serialize.execute(); + var expectedBuffer = new BufferList().addCString('').addInt32(0).join(true, 'E'); + assert.deepEqual(actual, expectedBuffer); + }); it('for named portal with row limit', function () { const actual = serialize.execute({ portal: 'my favorite portal', - rows: 100 - }) - var expectedBuffer = new BufferList() - .addCString('my favorite portal') - .addInt32(100) - .join(true, 'E') - assert.deepEqual(actual, expectedBuffer) - }) - }) + rows: 100, + }); + var expectedBuffer = new BufferList().addCString('my favorite portal').addInt32(100).join(true, 'E'); + assert.deepEqual(actual, expectedBuffer); + }); + }); it('builds flush command', function () { - const actual = serialize.flush() - var expected = new BufferList().join(true, 'H') - assert.deepEqual(actual, expected) - }) + const actual = serialize.flush(); + var expected = new BufferList().join(true, 'H'); + assert.deepEqual(actual, expected); + }); it('builds sync command', function () { - const actual = serialize.sync() - var expected = new BufferList().join(true, 'S') - assert.deepEqual(actual, expected) - }) + const actual = serialize.sync(); + var expected = new BufferList().join(true, 'S'); + assert.deepEqual(actual, expected); + }); it('builds end command', function () { - const actual = serialize.end() - var expected = Buffer.from([0x58, 0, 0, 0, 4]) - assert.deepEqual(actual, expected) - }) + const actual = serialize.end(); + var expected = Buffer.from([0x58, 0, 0, 0, 4]); + assert.deepEqual(actual, expected); + }); describe('builds describe command', function () { it('describe statement', function () { - const actual = serialize.describe({ type: 'S', name: 'bang' }) - var expected = new BufferList().addChar('S').addCString('bang').join(true, 'D') - assert.deepEqual(actual, expected) - }) + const actual = serialize.describe({ type: 'S', name: 'bang' }); + var expected = new BufferList().addChar('S').addCString('bang').join(true, 'D'); + assert.deepEqual(actual, expected); + }); it('describe unnamed portal', function () { - const actual = serialize.describe({ type: 'P' }) - var expected = new BufferList().addChar('P').addCString('').join(true, 'D') - assert.deepEqual(actual, expected) - }) - }) + const actual = serialize.describe({ type: 'P' }); + var expected = new BufferList().addChar('P').addCString('').join(true, 'D'); + assert.deepEqual(actual, expected); + }); + }); describe('builds close command', function () { it('describe statement', function () { - const actual = serialize.close({ type: 'S', name: 'bang' }) - var expected = new BufferList().addChar('S').addCString('bang').join(true, 'C') - assert.deepEqual(actual, expected) - }) + const actual = serialize.close({ type: 'S', name: 'bang' }); + var expected = new BufferList().addChar('S').addCString('bang').join(true, 'C'); + assert.deepEqual(actual, expected); + }); it('describe unnamed portal', function () { - const actual = serialize.close({ type: 'P' }) - var expected = new BufferList().addChar('P').addCString('').join(true, 'C') - assert.deepEqual(actual, expected) - }) - }) + const actual = serialize.close({ type: 'P' }); + var expected = new BufferList().addChar('P').addCString('').join(true, 'C'); + assert.deepEqual(actual, expected); + }); + }); describe('copy messages', function () { it('builds copyFromChunk', () => { - const actual = serialize.copyData(Buffer.from([1, 2, 3])) - const expected = new BufferList().add(Buffer.from([1, 2,3 ])).join(true, 'd') - assert.deepEqual(actual, expected) - }) + const actual = serialize.copyData(Buffer.from([1, 2, 3])); + const expected = new BufferList().add(Buffer.from([1, 2, 3])).join(true, 'd'); + assert.deepEqual(actual, expected); + }); it('builds copy fail', () => { - const actual = serialize.copyFail('err!') - const expected = new BufferList().addCString('err!').join(true, 'f') - assert.deepEqual(actual, expected) - }) + const actual = serialize.copyFail('err!'); + const expected = new BufferList().addCString('err!').join(true, 'f'); + assert.deepEqual(actual, expected); + }); it('builds copy done', () => { - const actual = serialize.copyDone() - const expected = new BufferList().join(true, 'c') - assert.deepEqual(actual, expected) - }) - }) + const actual = serialize.copyDone(); + const expected = new BufferList().join(true, 'c'); + assert.deepEqual(actual, expected); + }); + }); it('builds cancel message', () => { - const actual = serialize.cancel(3, 4) - const expected = new BufferList().addInt16(1234).addInt16(5678).addInt32(3).addInt32(4).join(true) - assert.deepEqual(actual, expected) - }) -}) + const actual = serialize.cancel(3, 4); + const expected = new BufferList().addInt16(1234).addInt16(5678).addInt32(3).addInt32(4).join(true); + assert.deepEqual(actual, expected); + }); +}); diff --git a/packages/pg-protocol/src/parser.ts b/packages/pg-protocol/src/parser.ts index 14573e62..58de45e1 100644 --- a/packages/pg-protocol/src/parser.ts +++ b/packages/pg-protocol/src/parser.ts @@ -1,7 +1,32 @@ import { TransformOptions } from 'stream'; -import { Mode, bindComplete, parseComplete, closeComplete, noData, portalSuspended, copyDone, replicationStart, emptyQuery, ReadyForQueryMessage, CommandCompleteMessage, CopyDataMessage, CopyResponse, NotificationResponseMessage, RowDescriptionMessage, Field, DataRowMessage, ParameterStatusMessage, BackendKeyDataMessage, DatabaseError, BackendMessage, MessageName, AuthenticationMD5Password, NoticeMessage } from './messages'; +import { + Mode, + bindComplete, + parseComplete, + closeComplete, + noData, + portalSuspended, + copyDone, + replicationStart, + emptyQuery, + ReadyForQueryMessage, + CommandCompleteMessage, + CopyDataMessage, + CopyResponse, + NotificationResponseMessage, + RowDescriptionMessage, + Field, + DataRowMessage, + ParameterStatusMessage, + BackendKeyDataMessage, + DatabaseError, + BackendMessage, + MessageName, + AuthenticationMD5Password, + NoticeMessage, +} from './messages'; import { BufferReader } from './buffer-reader'; -import assert from 'assert' +import assert from 'assert'; // every message is prefixed with a single bye const CODE_LENGTH = 1; @@ -14,13 +39,13 @@ const HEADER_LENGTH = CODE_LENGTH + LEN_LENGTH; export type Packet = { code: number; packet: Buffer; -} +}; const emptyBuffer = Buffer.allocUnsafe(0); type StreamOptions = TransformOptions & { - mode: Mode -} + mode: Mode; +}; const enum MessageCodes { DataRow = 0x44, // D @@ -55,7 +80,7 @@ export class Parser { constructor(opts?: StreamOptions) { if (opts?.mode === 'binary') { - throw new Error('Binary mode not supported yet') + throw new Error('Binary mode not supported yet'); } this.mode = opts?.mode || 'text'; } @@ -64,11 +89,11 @@ export class Parser { let combinedBuffer = buffer; if (this.remainingBuffer.byteLength) { combinedBuffer = Buffer.allocUnsafe(this.remainingBuffer.byteLength + buffer.byteLength); - this.remainingBuffer.copy(combinedBuffer) - buffer.copy(combinedBuffer, this.remainingBuffer.byteLength) + this.remainingBuffer.copy(combinedBuffer); + buffer.copy(combinedBuffer, this.remainingBuffer.byteLength); } let offset = 0; - while ((offset + HEADER_LENGTH) <= combinedBuffer.byteLength) { + while (offset + HEADER_LENGTH <= combinedBuffer.byteLength) { // code is 1 byte long - it identifies the message type const code = combinedBuffer[offset]; @@ -79,7 +104,7 @@ export class Parser { if (fullMessageLength + offset <= combinedBuffer.byteLength) { const message = this.handlePacket(offset + HEADER_LENGTH, code, length, combinedBuffer); - callback(message) + callback(message); offset += fullMessageLength; } else { break; @@ -89,9 +114,8 @@ export class Parser { if (offset === combinedBuffer.byteLength) { this.remainingBuffer = emptyBuffer; } else { - this.remainingBuffer = combinedBuffer.slice(offset) + this.remainingBuffer = combinedBuffer.slice(offset); } - } private handlePacket(offset: number, code: number, length: number, bytes: Buffer): BackendMessage { @@ -139,14 +163,14 @@ export class Parser { case MessageCodes.CopyData: return this.parseCopyData(offset, length, bytes); default: - assert.fail(`unknown message code: ${code.toString(16)}`) + assert.fail(`unknown message code: ${code.toString(16)}`); } } private parseReadyForQueryMessage(offset: number, length: number, bytes: Buffer) { this.reader.setBuffer(offset, bytes); const status = this.reader.string(1); - return new ReadyForQueryMessage(length, status) + return new ReadyForQueryMessage(length, status); } private parseCommandCompleteMessage(offset: number, length: number, bytes: Buffer) { @@ -161,17 +185,17 @@ export class Parser { } private parseCopyInMessage(offset: number, length: number, bytes: Buffer) { - return this.parseCopyMessage(offset, length, bytes, MessageName.copyInResponse) + return this.parseCopyMessage(offset, length, bytes, MessageName.copyInResponse); } private parseCopyOutMessage(offset: number, length: number, bytes: Buffer) { - return this.parseCopyMessage(offset, length, bytes, MessageName.copyOutResponse) + return this.parseCopyMessage(offset, length, bytes, MessageName.copyOutResponse); } private parseCopyMessage(offset: number, length: number, bytes: Buffer, messageName: MessageName) { this.reader.setBuffer(offset, bytes); const isBinary = this.reader.byte() !== 0; - const columnCount = this.reader.int16() + const columnCount = this.reader.int16(); const message = new CopyResponse(length, messageName, isBinary, columnCount); for (let i = 0; i < columnCount; i++) { message.columnTypes[i] = this.reader.int16(); @@ -189,23 +213,23 @@ export class Parser { private parseRowDescriptionMessage(offset: number, length: number, bytes: Buffer) { this.reader.setBuffer(offset, bytes); - const fieldCount = this.reader.int16() + const fieldCount = this.reader.int16(); const message = new RowDescriptionMessage(length, fieldCount); for (let i = 0; i < fieldCount; i++) { - message.fields[i] = this.parseField() + message.fields[i] = this.parseField(); } return message; } private parseField(): Field { - const name = this.reader.cstring() - const tableID = this.reader.int32() - const columnID = this.reader.int16() - const dataTypeID = this.reader.int32() - const dataTypeSize = this.reader.int16() - const dataTypeModifier = this.reader.int32() + const name = this.reader.cstring(); + const tableID = this.reader.int32(); + const columnID = this.reader.int16(); + const dataTypeID = this.reader.int32(); + const dataTypeSize = this.reader.int16(); + const dataTypeModifier = this.reader.int32(); const mode = this.reader.int16() === 0 ? 'text' : 'binary'; - return new Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode) + return new Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode); } private parseDataRowMessage(offset: number, length: number, bytes: Buffer) { @@ -215,7 +239,7 @@ export class Parser { for (let i = 0; i < fieldCount; i++) { const len = this.reader.int32(); // a -1 for length means the value of the field is null - fields[i] = len === -1 ? null : this.reader.string(len) + fields[i] = len === -1 ? null : this.reader.string(len); } return new DataRowMessage(length, fields); } @@ -223,21 +247,20 @@ export class Parser { private parseParameterStatusMessage(offset: number, length: number, bytes: Buffer) { this.reader.setBuffer(offset, bytes); const name = this.reader.cstring(); - const value = this.reader.cstring() - return new ParameterStatusMessage(length, name, value) + const value = this.reader.cstring(); + return new ParameterStatusMessage(length, name, value); } private parseBackendKeyData(offset: number, length: number, bytes: Buffer) { this.reader.setBuffer(offset, bytes); - const processID = this.reader.int32() - const secretKey = this.reader.int32() - return new BackendKeyDataMessage(length, processID, secretKey) + const processID = this.reader.int32(); + const secretKey = this.reader.int32(); + return new BackendKeyDataMessage(length, processID, secretKey); } - public parseAuthenticationResponse(offset: number, length: number, bytes: Buffer) { this.reader.setBuffer(offset, bytes); - const code = this.reader.int32() + const code = this.reader.int32(); // TODO(bmc): maybe better types here const message: BackendMessage & any = { name: MessageName.authenticationOk, @@ -249,71 +272,74 @@ export class Parser { break; case 3: // AuthenticationCleartextPassword if (message.length === 8) { - message.name = MessageName.authenticationCleartextPassword + message.name = MessageName.authenticationCleartextPassword; } - break + break; case 5: // AuthenticationMD5Password if (message.length === 12) { - message.name = MessageName.authenticationMD5Password + message.name = MessageName.authenticationMD5Password; const salt = this.reader.bytes(4); return new AuthenticationMD5Password(length, salt); } - break + break; case 10: // AuthenticationSASL - message.name = MessageName.authenticationSASL - message.mechanisms = [] + message.name = MessageName.authenticationSASL; + message.mechanisms = []; let mechanism: string; do { - mechanism = this.reader.cstring() + mechanism = this.reader.cstring(); if (mechanism) { - message.mechanisms.push(mechanism) + message.mechanisms.push(mechanism); } - } while (mechanism) + } while (mechanism); break; case 11: // AuthenticationSASLContinue - message.name = MessageName.authenticationSASLContinue - message.data = this.reader.string(length - 4) + message.name = MessageName.authenticationSASLContinue; + message.data = this.reader.string(length - 4); break; case 12: // AuthenticationSASLFinal - message.name = MessageName.authenticationSASLFinal - message.data = this.reader.string(length - 4) + message.name = MessageName.authenticationSASLFinal; + message.data = this.reader.string(length - 4); break; default: - throw new Error('Unknown authenticationOk message type ' + code) + throw new Error('Unknown authenticationOk message type ' + code); } return message; } private parseErrorMessage(offset: number, length: number, bytes: Buffer, name: MessageName) { this.reader.setBuffer(offset, bytes); - const fields: Record = {} - let fieldType = this.reader.string(1) + const fields: Record = {}; + let fieldType = this.reader.string(1); while (fieldType !== '\0') { - fields[fieldType] = this.reader.cstring() - fieldType = this.reader.string(1) + fields[fieldType] = this.reader.cstring(); + fieldType = this.reader.string(1); } - const messageValue = fields.M + const messageValue = fields.M; - const message = name === MessageName.notice ? new NoticeMessage(length, messageValue) : new DatabaseError(messageValue, length, name) + const message = + name === MessageName.notice + ? new NoticeMessage(length, messageValue) + : new DatabaseError(messageValue, length, name); - message.severity = fields.S - message.code = fields.C - message.detail = fields.D - message.hint = fields.H - message.position = fields.P - message.internalPosition = fields.p - message.internalQuery = fields.q - message.where = fields.W - message.schema = fields.s - message.table = fields.t - message.column = fields.c - message.dataType = fields.d - message.constraint = fields.n - message.file = fields.F - message.line = fields.L - message.routine = fields.R + message.severity = fields.S; + message.code = fields.C; + message.detail = fields.D; + message.hint = fields.H; + message.position = fields.P; + message.internalPosition = fields.p; + message.internalQuery = fields.q; + message.where = fields.W; + message.schema = fields.s; + message.table = fields.t; + message.column = fields.c; + message.dataType = fields.d; + message.constraint = fields.n; + message.file = fields.F; + message.line = fields.L; + message.routine = fields.R; return message; } } diff --git a/packages/pg-protocol/src/serializer.ts b/packages/pg-protocol/src/serializer.ts index 71ac3c87..904875dd 100644 --- a/packages/pg-protocol/src/serializer.ts +++ b/packages/pg-protocol/src/serializer.ts @@ -1,4 +1,4 @@ -import { Writer } from './buffer-writer' +import { Writer } from './buffer-writer'; const enum code { startup = 0x70, @@ -13,67 +13,61 @@ const enum code { describe = 0x44, copyFromChunk = 0x64, copyDone = 0x63, - copyFail = 0x66 + copyFail = 0x66, } -const writer = new Writer() +const writer = new Writer(); const startup = (opts: Record): Buffer => { // protocol version - writer.addInt16(3).addInt16(0) + writer.addInt16(3).addInt16(0); for (const key of Object.keys(opts)) { - writer.addCString(key).addCString(opts[key]) + writer.addCString(key).addCString(opts[key]); } - writer.addCString('client_encoding').addCString("'utf-8'") + writer.addCString('client_encoding').addCString("'utf-8'"); - var bodyBuffer = writer.addCString('').flush() + var bodyBuffer = writer.addCString('').flush(); // this message is sent without a code - var length = bodyBuffer.length + 4 + var length = bodyBuffer.length + 4; - return new Writer() - .addInt32(length) - .add(bodyBuffer) - .flush() -} + return new Writer().addInt32(length).add(bodyBuffer).flush(); +}; const requestSsl = (): Buffer => { - const response = Buffer.allocUnsafe(8) + const response = Buffer.allocUnsafe(8); response.writeInt32BE(8, 0); - response.writeInt32BE(80877103, 4) - return response -} + response.writeInt32BE(80877103, 4); + return response; +}; const password = (password: string): Buffer => { - return writer.addCString(password).flush(code.startup) -} + return writer.addCString(password).flush(code.startup); +}; const sendSASLInitialResponseMessage = function (mechanism: string, initialResponse: string): Buffer { // 0x70 = 'p' - writer - .addCString(mechanism) - .addInt32(Buffer.byteLength(initialResponse)) - .addString(initialResponse) + writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse); - return writer.flush(code.startup) -} + return writer.flush(code.startup); +}; const sendSCRAMClientFinalMessage = function (additionalData: string): Buffer { - return writer.addString(additionalData).flush(code.startup) -} + return writer.addString(additionalData).flush(code.startup); +}; const query = (text: string): Buffer => { - return writer.addCString(text).flush(code.query) -} + return writer.addCString(text).flush(code.query); +}; type ParseOpts = { name?: string; types?: number[]; text: string; -} +}; -const emptyArray: any[] = [] +const emptyArray: any[] = []; const parse = (query: ParseOpts): Buffer => { // expect something like this: @@ -82,171 +76,169 @@ const parse = (query: ParseOpts): Buffer => { // types: ['int8', 'bool'] } // normalize missing query names to allow for null - const name = query.name || '' + const name = query.name || ''; if (name.length > 63) { /* eslint-disable no-console */ - console.error('Warning! Postgres only supports 63 characters for query names.') - console.error('You supplied %s (%s)', name, name.length) - console.error('This can cause conflicts and silent errors executing queries') + console.error('Warning! Postgres only supports 63 characters for query names.'); + console.error('You supplied %s (%s)', name, name.length); + console.error('This can cause conflicts and silent errors executing queries'); /* eslint-enable no-console */ } - const types = query.types || emptyArray + const types = query.types || emptyArray; - var len = types.length + var len = types.length; var buffer = writer .addCString(name) // name of query .addCString(query.text) // actual query text - .addInt16(len) + .addInt16(len); for (var i = 0; i < len; i++) { - buffer.addInt32(types[i]) + buffer.addInt32(types[i]); } - return writer.flush(code.parse) -} + return writer.flush(code.parse); +}; type BindOpts = { portal?: string; binary?: boolean; statement?: string; values?: any[]; -} +}; const bind = (config: BindOpts = {}): Buffer => { // normalize config - const portal = config.portal || '' - const statement = config.statement || '' - const binary = config.binary || false - var values = config.values || emptyArray - var len = values.length + const portal = config.portal || ''; + const statement = config.statement || ''; + const binary = config.binary || false; + var values = config.values || emptyArray; + var len = values.length; - var useBinary = false + var useBinary = false; // TODO(bmc): all the loops in here aren't nice, we can do better for (var j = 0; j < len; j++) { - useBinary = useBinary || values[j] instanceof Buffer + useBinary = useBinary || values[j] instanceof Buffer; } - var buffer = writer - .addCString(portal) - .addCString(statement) + var buffer = writer.addCString(portal).addCString(statement); if (!useBinary) { - buffer.addInt16(0) + buffer.addInt16(0); } else { - buffer.addInt16(len) + buffer.addInt16(len); for (j = 0; j < len; j++) { - buffer.addInt16(values[j] instanceof Buffer ? 1 : 0) + buffer.addInt16(values[j] instanceof Buffer ? 1 : 0); } } - buffer.addInt16(len) + buffer.addInt16(len); for (var i = 0; i < len; i++) { - var val = values[i] + var val = values[i]; if (val === null || typeof val === 'undefined') { - buffer.addInt32(-1) + buffer.addInt32(-1); } else if (val instanceof Buffer) { - buffer.addInt32(val.length) - buffer.add(val) + buffer.addInt32(val.length); + buffer.add(val); } else { - buffer.addInt32(Buffer.byteLength(val)) - buffer.addString(val) + buffer.addInt32(Buffer.byteLength(val)); + buffer.addString(val); } } if (binary) { - buffer.addInt16(1) // format codes to use binary - buffer.addInt16(1) + buffer.addInt16(1); // format codes to use binary + buffer.addInt16(1); } else { - buffer.addInt16(0) // format codes to use text + buffer.addInt16(0); // format codes to use text } - return writer.flush(code.bind) -} + return writer.flush(code.bind); +}; type ExecOpts = { portal?: string; rows?: number; -} +}; -const emptyExecute = Buffer.from([code.execute, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]) +const emptyExecute = Buffer.from([code.execute, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]); const execute = (config?: ExecOpts): Buffer => { // this is the happy path for most queries - if (!config || !config.portal && !config.rows) { + if (!config || (!config.portal && !config.rows)) { return emptyExecute; } - const portal = config.portal || '' - const rows = config.rows || 0 + const portal = config.portal || ''; + const rows = config.rows || 0; - const portalLength = Buffer.byteLength(portal) - const len = 4 + portalLength + 1 + 4 + const portalLength = Buffer.byteLength(portal); + const len = 4 + portalLength + 1 + 4; // one extra bit for code - const buff = Buffer.allocUnsafe(1 + len) - buff[0] = code.execute - buff.writeInt32BE(len, 1) - buff.write(portal, 5, 'utf-8') + const buff = Buffer.allocUnsafe(1 + len); + buff[0] = code.execute; + buff.writeInt32BE(len, 1); + buff.write(portal, 5, 'utf-8'); buff[portalLength + 5] = 0; // null terminate portal cString - buff.writeUInt32BE(rows, buff.length - 4) + buff.writeUInt32BE(rows, buff.length - 4); return buff; -} +}; const cancel = (processID: number, secretKey: number): Buffer => { - const buffer = Buffer.allocUnsafe(16) - buffer.writeInt32BE(16, 0) - buffer.writeInt16BE(1234, 4) - buffer.writeInt16BE(5678, 6) - buffer.writeInt32BE(processID, 8) - buffer.writeInt32BE(secretKey, 12) + const buffer = Buffer.allocUnsafe(16); + buffer.writeInt32BE(16, 0); + buffer.writeInt16BE(1234, 4); + buffer.writeInt16BE(5678, 6); + buffer.writeInt32BE(processID, 8); + buffer.writeInt32BE(secretKey, 12); return buffer; -} +}; type PortalOpts = { - type: 'S' | 'P', + type: 'S' | 'P'; name?: string; -} +}; const cstringMessage = (code: code, string: string): Buffer => { - const stringLen = Buffer.byteLength(string) - const len = 4 + stringLen + 1 + const stringLen = Buffer.byteLength(string); + const len = 4 + stringLen + 1; // one extra bit for code - const buffer = Buffer.allocUnsafe(1 + len) - buffer[0] = code - buffer.writeInt32BE(len, 1) - buffer.write(string, 5, 'utf-8') - buffer[len] = 0 // null terminate cString - return buffer -} + const buffer = Buffer.allocUnsafe(1 + len); + buffer[0] = code; + buffer.writeInt32BE(len, 1); + buffer.write(string, 5, 'utf-8'); + buffer[len] = 0; // null terminate cString + return buffer; +}; -const emptyDescribePortal = writer.addCString('P').flush(code.describe) -const emptyDescribeStatement = writer.addCString('S').flush(code.describe) +const emptyDescribePortal = writer.addCString('P').flush(code.describe); +const emptyDescribeStatement = writer.addCString('S').flush(code.describe); const describe = (msg: PortalOpts): Buffer => { - return msg.name ? - cstringMessage(code.describe,`${msg.type}${msg.name || ''}`) : - msg.type === 'P' ? - emptyDescribePortal : - emptyDescribeStatement; -} + return msg.name + ? cstringMessage(code.describe, `${msg.type}${msg.name || ''}`) + : msg.type === 'P' + ? emptyDescribePortal + : emptyDescribeStatement; +}; const close = (msg: PortalOpts): Buffer => { - const text = `${msg.type}${msg.name || ''}` - return cstringMessage(code.close, text) -} + const text = `${msg.type}${msg.name || ''}`; + return cstringMessage(code.close, text); +}; const copyData = (chunk: Buffer): Buffer => { - return writer.add(chunk).flush(code.copyFromChunk) -} + return writer.add(chunk).flush(code.copyFromChunk); +}; const copyFail = (message: string): Buffer => { return cstringMessage(code.copyFail, message); -} +}; -const codeOnlyBuffer = (code: code): Buffer => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]) +const codeOnlyBuffer = (code: code): Buffer => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]); -const flushBuffer = codeOnlyBuffer(code.flush) -const syncBuffer = codeOnlyBuffer(code.sync) -const endBuffer = codeOnlyBuffer(code.end) -const copyDoneBuffer = codeOnlyBuffer(code.copyDone) +const flushBuffer = codeOnlyBuffer(code.flush); +const syncBuffer = codeOnlyBuffer(code.sync); +const endBuffer = codeOnlyBuffer(code.end); +const copyDoneBuffer = codeOnlyBuffer(code.copyDone); const serialize = { startup, @@ -266,7 +258,7 @@ const serialize = { copyData, copyDone: () => copyDoneBuffer, copyFail, - cancel -} + cancel, +}; -export { serialize } +export { serialize }; diff --git a/packages/pg-protocol/src/testing/buffer-list.ts b/packages/pg-protocol/src/testing/buffer-list.ts index 51812bce..d7c7e457 100644 --- a/packages/pg-protocol/src/testing/buffer-list.ts +++ b/packages/pg-protocol/src/testing/buffer-list.ts @@ -1,79 +1,75 @@ export default class BufferList { - constructor(public buffers: Buffer[] = []) { - - } + constructor(public buffers: Buffer[] = []) {} public add(buffer: Buffer, front?: boolean) { - this.buffers[front ? 'unshift' : 'push'](buffer) - return this + this.buffers[front ? 'unshift' : 'push'](buffer); + return this; } public addInt16(val: number, front?: boolean) { - return this.add(Buffer.from([(val >>> 8), (val >>> 0)]), front) + return this.add(Buffer.from([val >>> 8, val >>> 0]), front); } public getByteLength(initial?: number) { return this.buffers.reduce(function (previous, current) { - return previous + current.length - }, initial || 0) + return previous + current.length; + }, initial || 0); } public addInt32(val: number, first?: boolean) { - return this.add(Buffer.from([ - (val >>> 24 & 0xFF), - (val >>> 16 & 0xFF), - (val >>> 8 & 0xFF), - (val >>> 0 & 0xFF) - ]), first) + return this.add( + Buffer.from([(val >>> 24) & 0xff, (val >>> 16) & 0xff, (val >>> 8) & 0xff, (val >>> 0) & 0xff]), + first + ); } public addCString(val: string, front?: boolean) { - var len = Buffer.byteLength(val) - var buffer = Buffer.alloc(len + 1) - buffer.write(val) - buffer[len] = 0 - return this.add(buffer, front) + var len = Buffer.byteLength(val); + var buffer = Buffer.alloc(len + 1); + buffer.write(val); + buffer[len] = 0; + return this.add(buffer, front); } public addString(val: string, front?: boolean) { - var len = Buffer.byteLength(val) - var buffer = Buffer.alloc(len) - buffer.write(val) - return this.add(buffer, front) + var len = Buffer.byteLength(val); + var buffer = Buffer.alloc(len); + buffer.write(val); + return this.add(buffer, front); } public addChar(char: string, first?: boolean) { - return this.add(Buffer.from(char, 'utf8'), first) + return this.add(Buffer.from(char, 'utf8'), first); } public addByte(byte: number) { - return this.add(Buffer.from([byte])) + return this.add(Buffer.from([byte])); } public join(appendLength?: boolean, char?: string): Buffer { - var length = this.getByteLength() + var length = this.getByteLength(); if (appendLength) { - this.addInt32(length + 4, true) - return this.join(false, char) + this.addInt32(length + 4, true); + return this.join(false, char); } if (char) { - this.addChar(char, true) - length++ + this.addChar(char, true); + length++; } - var result = Buffer.alloc(length) - var index = 0 + var result = Buffer.alloc(length); + var index = 0; this.buffers.forEach(function (buffer) { - buffer.copy(result, index, 0) - index += buffer.length - }) - return result + buffer.copy(result, index, 0); + index += buffer.length; + }); + return result; } public static concat(): Buffer { - var total = new BufferList() + var total = new BufferList(); for (var i = 0; i < arguments.length; i++) { - total.add(arguments[i]) + total.add(arguments[i]); } - return total.join() + return total.join(); } } diff --git a/packages/pg-protocol/src/testing/test-buffers.ts b/packages/pg-protocol/src/testing/test-buffers.ts index 0594eaad..32384976 100644 --- a/packages/pg-protocol/src/testing/test-buffers.ts +++ b/packages/pg-protocol/src/testing/test-buffers.ts @@ -1,150 +1,123 @@ // http://developer.postgresql.org/pgdocs/postgres/protocol-message-formats.html -import BufferList from './buffer-list' +import BufferList from './buffer-list'; const buffers = { readyForQuery: function () { - return new BufferList() - .add(Buffer.from('I')) - .join(true, 'Z') + return new BufferList().add(Buffer.from('I')).join(true, 'Z'); }, authenticationOk: function () { - return new BufferList() - .addInt32(0) - .join(true, 'R') + return new BufferList().addInt32(0).join(true, 'R'); }, authenticationCleartextPassword: function () { - return new BufferList() - .addInt32(3) - .join(true, 'R') + return new BufferList().addInt32(3).join(true, 'R'); }, authenticationMD5Password: function () { return new BufferList() .addInt32(5) .add(Buffer.from([1, 2, 3, 4])) - .join(true, 'R') + .join(true, 'R'); }, authenticationSASL: function () { - return new BufferList() - .addInt32(10) - .addCString('SCRAM-SHA-256') - .addCString('') - .join(true, 'R') + return new BufferList().addInt32(10).addCString('SCRAM-SHA-256').addCString('').join(true, 'R'); }, authenticationSASLContinue: function () { - return new BufferList() - .addInt32(11) - .addString('data') - .join(true, 'R') + return new BufferList().addInt32(11).addString('data').join(true, 'R'); }, authenticationSASLFinal: function () { - return new BufferList() - .addInt32(12) - .addString('data') - .join(true, 'R') + return new BufferList().addInt32(12).addString('data').join(true, 'R'); }, parameterStatus: function (name: string, value: string) { - return new BufferList() - .addCString(name) - .addCString(value) - .join(true, 'S') + return new BufferList().addCString(name).addCString(value).join(true, 'S'); }, backendKeyData: function (processID: number, secretKey: number) { - return new BufferList() - .addInt32(processID) - .addInt32(secretKey) - .join(true, 'K') + return new BufferList().addInt32(processID).addInt32(secretKey).join(true, 'K'); }, commandComplete: function (string: string) { - return new BufferList() - .addCString(string) - .join(true, 'C') + return new BufferList().addCString(string).join(true, 'C'); }, rowDescription: function (fields: any[]) { - fields = fields || [] - var buf = new BufferList() - buf.addInt16(fields.length) + fields = fields || []; + var buf = new BufferList(); + buf.addInt16(fields.length); fields.forEach(function (field) { - buf.addCString(field.name) + buf + .addCString(field.name) .addInt32(field.tableID || 0) .addInt16(field.attributeNumber || 0) .addInt32(field.dataTypeID || 0) .addInt16(field.dataTypeSize || 0) .addInt32(field.typeModifier || 0) - .addInt16(field.formatCode || 0) - }) - return buf.join(true, 'T') + .addInt16(field.formatCode || 0); + }); + return buf.join(true, 'T'); }, dataRow: function (columns: any[]) { - columns = columns || [] - var buf = new BufferList() - buf.addInt16(columns.length) + columns = columns || []; + var buf = new BufferList(); + buf.addInt16(columns.length); columns.forEach(function (col) { if (col == null) { - buf.addInt32(-1) + buf.addInt32(-1); } else { - var strBuf = Buffer.from(col, 'utf8') - buf.addInt32(strBuf.length) - buf.add(strBuf) + var strBuf = Buffer.from(col, 'utf8'); + buf.addInt32(strBuf.length); + buf.add(strBuf); } - }) - return buf.join(true, 'D') + }); + return buf.join(true, 'D'); }, error: function (fields: any) { - return buffers.errorOrNotice(fields).join(true, 'E') + return buffers.errorOrNotice(fields).join(true, 'E'); }, notice: function (fields: any) { - return buffers.errorOrNotice(fields).join(true, 'N') + return buffers.errorOrNotice(fields).join(true, 'N'); }, errorOrNotice: function (fields: any) { - fields = fields || [] - var buf = new BufferList() + fields = fields || []; + var buf = new BufferList(); fields.forEach(function (field: any) { - buf.addChar(field.type) - buf.addCString(field.value) - }) - return buf.add(Buffer.from([0]))// terminator + buf.addChar(field.type); + buf.addCString(field.value); + }); + return buf.add(Buffer.from([0])); // terminator }, parseComplete: function () { - return new BufferList().join(true, '1') + return new BufferList().join(true, '1'); }, bindComplete: function () { - return new BufferList().join(true, '2') + return new BufferList().join(true, '2'); }, notification: function (id: number, channel: string, payload: string) { - return new BufferList() - .addInt32(id) - .addCString(channel) - .addCString(payload) - .join(true, 'A') + return new BufferList().addInt32(id).addCString(channel).addCString(payload).join(true, 'A'); }, emptyQuery: function () { - return new BufferList().join(true, 'I') + return new BufferList().join(true, 'I'); }, portalSuspended: function () { - return new BufferList().join(true, 's') + return new BufferList().join(true, 's'); }, closeComplete: function () { - return new BufferList().join(true, '3') + return new BufferList().join(true, '3'); }, copyIn: function (cols: number) { @@ -156,7 +129,7 @@ const buffers = { for (let i = 0; i < cols; i++) { list.addInt16(i); } - return list.join(true, 'G') + return list.join(true, 'G'); }, copyOut: function (cols: number) { @@ -168,7 +141,7 @@ const buffers = { for (let i = 0; i < cols; i++) { list.addInt16(i); } - return list.join(true, 'H') + return list.join(true, 'H'); }, copyData: function (bytes: Buffer) { @@ -176,8 +149,8 @@ const buffers = { }, copyDone: function () { - return new BufferList().join(true, 'c') - } -} + return new BufferList().join(true, 'c'); + }, +}; -export default buffers +export default buffers; diff --git a/packages/pg-protocol/src/types/chunky.d.ts b/packages/pg-protocol/src/types/chunky.d.ts index 7389bda6..914ce06b 100644 --- a/packages/pg-protocol/src/types/chunky.d.ts +++ b/packages/pg-protocol/src/types/chunky.d.ts @@ -1 +1 @@ -declare module 'chunky' +declare module 'chunky'; diff --git a/packages/pg/Makefile b/packages/pg/Makefile index a5b0bc1d..e05edbf4 100644 --- a/packages/pg/Makefile +++ b/packages/pg/Makefile @@ -7,7 +7,7 @@ params := $(connectionString) node-command := xargs -n 1 -I file node file $(params) .PHONY : test test-connection test-integration bench test-native \ - lint publish test-missing-native update-npm + publish test-missing-native update-npm all: npm install @@ -17,7 +17,7 @@ help: test: test-unit -test-all: lint test-missing-native test-unit test-integration test-native +test-all: test-missing-native test-unit test-integration test-native update-npm: @@ -59,7 +59,3 @@ test-binary: test-connection test-pool: @find test/integration/connection-pool -name "*.js" | $(node-command) binary - -lint: - @echo "***Starting lint***" - node_modules/.bin/eslint lib diff --git a/yarn.lock b/yarn.lock index 812bf915..60f2b1bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -836,6 +836,11 @@ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.7.tgz#1c8c25cbf6e59ffa7d6b9652c78e547d9a41692d" integrity sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g== +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -890,6 +895,16 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" +"@typescript-eslint/parser@^2.27.0": + version "2.27.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.27.0.tgz#d91664335b2c46584294e42eb4ff35838c427287" + integrity sha512-HFUXZY+EdwrJXZo31DW4IS1ujQW3krzlRjBrFRrJcMDh0zCu107/nRfhk/uBasO8m0NVDbBF5WZKcIUMRO7vPg== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.27.0" + "@typescript-eslint/typescript-estree" "2.27.0" + eslint-visitor-keys "^1.1.0" + "@typescript-eslint/typescript-estree@2.27.0": version "2.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.27.0.tgz#a288e54605412da8b81f1660b56c8b2e42966ce8"