postgrejs 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +814 -0
  2. package/LICENSE +21 -0
  3. package/README.md +200 -0
  4. package/cjs/connection/bind-param.js +10 -0
  5. package/cjs/connection/connection.js +279 -0
  6. package/cjs/connection/cursor.js +93 -0
  7. package/cjs/connection/intl-connection.js +243 -0
  8. package/cjs/connection/pool.js +226 -0
  9. package/cjs/connection/portal.js +148 -0
  10. package/cjs/connection/prepared-statement.js +217 -0
  11. package/cjs/constants.js +250 -0
  12. package/cjs/data-type-map.js +119 -0
  13. package/cjs/data-types/bool-type.js +27 -0
  14. package/cjs/data-types/box-type.js +52 -0
  15. package/cjs/data-types/bytea-type.js +29 -0
  16. package/cjs/data-types/char-type.js +27 -0
  17. package/cjs/data-types/circle-type.js +48 -0
  18. package/cjs/data-types/date-type.js +67 -0
  19. package/cjs/data-types/float4-type.js +25 -0
  20. package/cjs/data-types/float8-type.js +25 -0
  21. package/cjs/data-types/int2-type.js +26 -0
  22. package/cjs/data-types/int2-vector-type.js +38 -0
  23. package/cjs/data-types/int4-type.js +26 -0
  24. package/cjs/data-types/int8-type.js +31 -0
  25. package/cjs/data-types/json-type.js +38 -0
  26. package/cjs/data-types/jsonb-type.js +42 -0
  27. package/cjs/data-types/lseg-type.js +52 -0
  28. package/cjs/data-types/numeric-type.js +125 -0
  29. package/cjs/data-types/oid-type.js +32 -0
  30. package/cjs/data-types/point-type.js +39 -0
  31. package/cjs/data-types/time-type.js +54 -0
  32. package/cjs/data-types/timestamp-type.js +81 -0
  33. package/cjs/data-types/timestamptz-type.js +73 -0
  34. package/cjs/data-types/uuid-type.js +39 -0
  35. package/cjs/data-types/varchar-type.js +27 -0
  36. package/cjs/index.js +41 -0
  37. package/cjs/interfaces/command-result.js +2 -0
  38. package/cjs/interfaces/data-mapping-options.js +2 -0
  39. package/cjs/interfaces/data-type.js +2 -0
  40. package/cjs/interfaces/database-connection-params.js +2 -0
  41. package/cjs/interfaces/field-info.js +2 -0
  42. package/cjs/interfaces/query-options.js +2 -0
  43. package/cjs/interfaces/query-result.js +2 -0
  44. package/cjs/interfaces/script-execute-options.js +2 -0
  45. package/cjs/interfaces/script-result.js +2 -0
  46. package/cjs/interfaces/statement-prepare-options.js +2 -0
  47. package/cjs/package.json +3 -0
  48. package/cjs/protocol/backend.js +256 -0
  49. package/cjs/protocol/buffer-reader.js +78 -0
  50. package/cjs/protocol/database-error.js +17 -0
  51. package/cjs/protocol/frontend.js +218 -0
  52. package/cjs/protocol/pg-socket.js +345 -0
  53. package/cjs/protocol/protocol.js +71 -0
  54. package/cjs/protocol/sasl.js +108 -0
  55. package/cjs/protocol/smart-buffer.js +195 -0
  56. package/cjs/safe-event-emitter.js +17 -0
  57. package/cjs/types.js +2 -0
  58. package/cjs/util/array-calculatedim.js +19 -0
  59. package/cjs/util/bigint-methods.js +42 -0
  60. package/cjs/util/config-from-env.js +30 -0
  61. package/cjs/util/connection-config.js +65 -0
  62. package/cjs/util/convert-row-to-object.js +13 -0
  63. package/cjs/util/decode-binaryarray.js +39 -0
  64. package/cjs/util/encode-binaryarray.js +45 -0
  65. package/cjs/util/escape-literal.js +27 -0
  66. package/cjs/util/fast-parseint.js +32 -0
  67. package/cjs/util/get-parsers.js +42 -0
  68. package/cjs/util/parse-array.js +61 -0
  69. package/cjs/util/parse-datetime.js +38 -0
  70. package/cjs/util/parse-row.js +11 -0
  71. package/cjs/util/parse-time.js +29 -0
  72. package/cjs/util/stringify-arrayliteral.js +38 -0
  73. package/cjs/util/stringify-for-sql.js +30 -0
  74. package/cjs/util/wrap-row-description.js +35 -0
  75. package/esm/connection/bind-param.js +6 -0
  76. package/esm/connection/connection.js +275 -0
  77. package/esm/connection/cursor.js +86 -0
  78. package/esm/connection/intl-connection.js +238 -0
  79. package/esm/connection/pool.js +222 -0
  80. package/esm/connection/portal.js +144 -0
  81. package/esm/connection/prepared-statement.js +213 -0
  82. package/esm/constants.js +247 -0
  83. package/esm/data-type-map.js +115 -0
  84. package/esm/data-types/bool-type.js +24 -0
  85. package/esm/data-types/box-type.js +49 -0
  86. package/esm/data-types/bytea-type.js +23 -0
  87. package/esm/data-types/char-type.js +24 -0
  88. package/esm/data-types/circle-type.js +45 -0
  89. package/esm/data-types/date-type.js +64 -0
  90. package/esm/data-types/float4-type.js +22 -0
  91. package/esm/data-types/float8-type.js +22 -0
  92. package/esm/data-types/int2-type.js +23 -0
  93. package/esm/data-types/int2-vector-type.js +35 -0
  94. package/esm/data-types/int4-type.js +23 -0
  95. package/esm/data-types/int8-type.js +28 -0
  96. package/esm/data-types/json-type.js +35 -0
  97. package/esm/data-types/jsonb-type.js +39 -0
  98. package/esm/data-types/lseg-type.js +49 -0
  99. package/esm/data-types/numeric-type.js +121 -0
  100. package/esm/data-types/oid-type.js +29 -0
  101. package/esm/data-types/point-type.js +36 -0
  102. package/esm/data-types/time-type.js +51 -0
  103. package/esm/data-types/timestamp-type.js +78 -0
  104. package/esm/data-types/timestamptz-type.js +70 -0
  105. package/esm/data-types/uuid-type.js +36 -0
  106. package/esm/data-types/varchar-type.js +24 -0
  107. package/esm/index.js +25 -0
  108. package/esm/interfaces/command-result.js +1 -0
  109. package/esm/interfaces/data-mapping-options.js +1 -0
  110. package/esm/interfaces/data-type.js +1 -0
  111. package/esm/interfaces/database-connection-params.js +1 -0
  112. package/esm/interfaces/field-info.js +1 -0
  113. package/esm/interfaces/query-options.js +1 -0
  114. package/esm/interfaces/query-result.js +1 -0
  115. package/esm/interfaces/script-execute-options.js +1 -0
  116. package/esm/interfaces/script-result.js +1 -0
  117. package/esm/interfaces/statement-prepare-options.js +1 -0
  118. package/esm/protocol/backend.js +252 -0
  119. package/esm/protocol/buffer-reader.js +74 -0
  120. package/esm/protocol/database-error.js +13 -0
  121. package/esm/protocol/frontend.js +214 -0
  122. package/esm/protocol/pg-socket.js +338 -0
  123. package/esm/protocol/protocol.js +68 -0
  124. package/esm/protocol/sasl.js +102 -0
  125. package/esm/protocol/smart-buffer.js +168 -0
  126. package/esm/safe-event-emitter.js +13 -0
  127. package/esm/types.js +1 -0
  128. package/esm/util/array-calculatedim.js +15 -0
  129. package/esm/util/bigint-methods.js +37 -0
  130. package/esm/util/config-from-env.js +26 -0
  131. package/esm/util/connection-config.js +57 -0
  132. package/esm/util/convert-row-to-object.js +9 -0
  133. package/esm/util/decode-binaryarray.js +35 -0
  134. package/esm/util/encode-binaryarray.js +41 -0
  135. package/esm/util/escape-literal.js +23 -0
  136. package/esm/util/fast-parseint.js +28 -0
  137. package/esm/util/get-parsers.js +38 -0
  138. package/esm/util/parse-array.js +57 -0
  139. package/esm/util/parse-datetime.js +34 -0
  140. package/esm/util/parse-row.js +7 -0
  141. package/esm/util/parse-time.js +25 -0
  142. package/esm/util/stringify-arrayliteral.js +34 -0
  143. package/esm/util/stringify-for-sql.js +25 -0
  144. package/esm/util/wrap-row-description.js +31 -0
  145. package/package.json +54 -0
  146. package/typings/connection/bind-param.d.ts +6 -0
  147. package/typings/connection/connection.d.ts +110 -0
  148. package/typings/connection/cursor.d.ts +25 -0
  149. package/typings/connection/intl-connection.d.ts +40 -0
  150. package/typings/connection/pool.d.ts +52 -0
  151. package/typings/connection/portal.d.ts +23 -0
  152. package/typings/connection/prepared-statement.d.ts +27 -0
  153. package/typings/constants.d.ts +130 -0
  154. package/typings/data-type-map.d.ts +11 -0
  155. package/typings/data-types/bool-type.d.ts +3 -0
  156. package/typings/data-types/box-type.d.ts +3 -0
  157. package/typings/data-types/bytea-type.d.ts +3 -0
  158. package/typings/data-types/char-type.d.ts +3 -0
  159. package/typings/data-types/circle-type.d.ts +3 -0
  160. package/typings/data-types/date-type.d.ts +3 -0
  161. package/typings/data-types/float4-type.d.ts +3 -0
  162. package/typings/data-types/float8-type.d.ts +3 -0
  163. package/typings/data-types/int2-type.d.ts +3 -0
  164. package/typings/data-types/int2-vector-type.d.ts +3 -0
  165. package/typings/data-types/int4-type.d.ts +3 -0
  166. package/typings/data-types/int8-type.d.ts +3 -0
  167. package/typings/data-types/json-type.d.ts +3 -0
  168. package/typings/data-types/jsonb-type.d.ts +3 -0
  169. package/typings/data-types/lseg-type.d.ts +3 -0
  170. package/typings/data-types/numeric-type.d.ts +12 -0
  171. package/typings/data-types/oid-type.d.ts +4 -0
  172. package/typings/data-types/point-type.d.ts +3 -0
  173. package/typings/data-types/time-type.d.ts +3 -0
  174. package/typings/data-types/timestamp-type.d.ts +3 -0
  175. package/typings/data-types/timestamptz-type.d.ts +3 -0
  176. package/typings/data-types/uuid-type.d.ts +3 -0
  177. package/typings/data-types/varchar-type.d.ts +3 -0
  178. package/typings/index.d.ts +25 -0
  179. package/typings/interfaces/command-result.d.ts +28 -0
  180. package/typings/interfaces/data-mapping-options.d.ts +9 -0
  181. package/typings/interfaces/data-type.d.ts +31 -0
  182. package/typings/interfaces/database-connection-params.d.ts +38 -0
  183. package/typings/interfaces/field-info.d.ts +46 -0
  184. package/typings/interfaces/query-options.d.ts +46 -0
  185. package/typings/interfaces/query-result.d.ts +8 -0
  186. package/typings/interfaces/script-execute-options.d.ts +26 -0
  187. package/typings/interfaces/script-result.d.ts +15 -0
  188. package/typings/interfaces/statement-prepare-options.d.ts +13 -0
  189. package/typings/protocol/backend.d.ts +9 -0
  190. package/typings/protocol/buffer-reader.d.ts +18 -0
  191. package/typings/protocol/database-error.d.ts +20 -0
  192. package/typings/protocol/frontend.d.ts +59 -0
  193. package/typings/protocol/pg-socket.d.ts +51 -0
  194. package/typings/protocol/protocol.d.ts +180 -0
  195. package/typings/protocol/sasl.d.ts +13 -0
  196. package/typings/protocol/smart-buffer.d.ts +39 -0
  197. package/typings/safe-event-emitter.d.ts +5 -0
  198. package/typings/types.d.ts +15 -0
  199. package/typings/util/array-calculatedim.d.ts +1 -0
  200. package/typings/util/bigint-methods.d.ts +3 -0
  201. package/typings/util/config-from-env.d.ts +2 -0
  202. package/typings/util/connection-config.d.ts +3 -0
  203. package/typings/util/convert-row-to-object.d.ts +2 -0
  204. package/typings/util/decode-binaryarray.d.ts +4 -0
  205. package/typings/util/encode-binaryarray.d.ts +5 -0
  206. package/typings/util/escape-literal.d.ts +1 -0
  207. package/typings/util/fast-parseint.d.ts +1 -0
  208. package/typings/util/get-parsers.d.ts +4 -0
  209. package/typings/util/parse-array.d.ts +5 -0
  210. package/typings/util/parse-datetime.d.ts +1 -0
  211. package/typings/util/parse-row.d.ts +3 -0
  212. package/typings/util/parse-time.d.ts +3 -0
  213. package/typings/util/stringify-arrayliteral.d.ts +3 -0
  214. package/typings/util/stringify-for-sql.d.ts +4 -0
  215. package/typings/util/wrap-row-description.d.ts +5 -0
@@ -0,0 +1,214 @@
1
+ import { DEFAULT_COLUMN_FORMAT } from '../constants.js';
2
+ import { encodeBinaryArray } from '../util/encode-binaryarray.js';
3
+ import { stringifyArrayLiteral } from '../util/stringify-arrayliteral.js';
4
+ import { Protocol } from './protocol.js';
5
+ import { SmartBuffer } from './smart-buffer.js';
6
+ const DataFormat = Protocol.DataFormat;
7
+ const StaticFlushBuffer = Buffer.from([Protocol.FrontendMessageCode.Flush, 0x00, 0x00, 0x00, 0x04]);
8
+ const StaticTerminateBuffer = Buffer.from([Protocol.FrontendMessageCode.Terminate, 0x00, 0x00, 0x00, 0x04]);
9
+ const StaticSyncBuffer = Buffer.from([Protocol.FrontendMessageCode.Sync, 0x00, 0x00, 0x00, 0x04]);
10
+ export class Frontend {
11
+ constructor(options) {
12
+ this._io = new SmartBuffer(options?.buffer);
13
+ }
14
+ getSSLRequestMessage() {
15
+ return this._io
16
+ .start()
17
+ .writeUInt32BE(8) // Length of message contents in bytes, including self.
18
+ .writeUInt16BE(1234)
19
+ .writeUInt16BE(5679)
20
+ .flush();
21
+ }
22
+ getStartupMessage(args) {
23
+ const io = this._io
24
+ .start()
25
+ .writeInt32BE(0) // Preserve length
26
+ .writeInt16BE(Protocol.VERSION_MAJOR)
27
+ .writeInt16BE(Protocol.VERSION_MINOR);
28
+ for (const [k, v] of Object.entries(args)) {
29
+ if (k !== 'client_encoding')
30
+ io.writeCString(k, 'utf8').writeCString(v, 'utf8');
31
+ }
32
+ io.writeCString('client_encoding', 'utf8').writeCString('UTF8', 'utf8').writeUInt8(0);
33
+ return setLengthAndFlush(io, 0);
34
+ }
35
+ getPasswordMessage(password) {
36
+ const io = this._io
37
+ .start()
38
+ .writeInt8(Protocol.FrontendMessageCode.PasswordMessage)
39
+ .writeInt32BE(0) // Preserve header
40
+ .writeCString(password, 'utf8');
41
+ return setLengthAndFlush(io, 1);
42
+ }
43
+ getSASLMessage(sasl) {
44
+ const io = this._io
45
+ .start()
46
+ .writeInt8(Protocol.FrontendMessageCode.PasswordMessage)
47
+ .writeInt32BE(0) // Preserve header
48
+ .writeCString(sasl.mechanism, 'utf8')
49
+ .writeLString(sasl.clientFirstMessage);
50
+ return setLengthAndFlush(io, 1);
51
+ }
52
+ getSASLFinalMessage(session) {
53
+ const io = this._io
54
+ .start()
55
+ .writeInt8(Protocol.FrontendMessageCode.PasswordMessage)
56
+ .writeInt32BE(0) // Preserve header
57
+ .writeString(session.clientFinalMessage);
58
+ return setLengthAndFlush(io, 1);
59
+ }
60
+ getParseMessage(args) {
61
+ if (args.statement && args.statement.length > 63)
62
+ throw new Error('Query name length must be lower than 63');
63
+ const io = this._io
64
+ .start()
65
+ .writeInt8(Protocol.FrontendMessageCode.Parse)
66
+ .writeInt32BE(0) // Preserve header
67
+ .writeCString(args.statement || '', 'utf8')
68
+ .writeCString(args.sql, 'utf8')
69
+ .writeUInt16BE(args.paramTypes ? args.paramTypes.length : 0);
70
+ if (args.paramTypes) {
71
+ for (const t of args.paramTypes) {
72
+ io.writeUInt32BE(t || 0);
73
+ }
74
+ }
75
+ return setLengthAndFlush(io, 1);
76
+ }
77
+ getBindMessage(args) {
78
+ if (args.portal && args.portal.length > 63)
79
+ throw new Error('Portal name length must be lower than 63');
80
+ if (args.statement && args.statement.length > 63)
81
+ throw new Error('Query name length must be lower than 63');
82
+ const io = this._io
83
+ .start()
84
+ .writeInt8(Protocol.FrontendMessageCode.Bind)
85
+ .writeInt32BE(0) // Preserve header
86
+ .writeCString(args.portal || '', 'utf8')
87
+ .writeCString(args.statement || '', 'utf8');
88
+ const { params, paramTypes, queryOptions } = args;
89
+ const columnFormat = queryOptions.columnFormat != null ? queryOptions.columnFormat : DEFAULT_COLUMN_FORMAT;
90
+ if (params && params.length) {
91
+ io.writeInt16BE(params.length);
92
+ const formatOffset = io.offset;
93
+ for (let i = 0; i < params.length; i++) {
94
+ io.writeInt16BE(0); // Preserve
95
+ }
96
+ // Write parameter values
97
+ io.writeUInt16BE(params.length);
98
+ for (let i = 0; i < params?.length; i++) {
99
+ let v = params[i];
100
+ if (v == null) {
101
+ io.writeInt32BE(-1);
102
+ continue;
103
+ }
104
+ const dataTypeOid = paramTypes ? paramTypes[i] : undefined;
105
+ const dt = dataTypeOid ? args.typeMap.get(dataTypeOid) : undefined;
106
+ if (dt) {
107
+ if (typeof dt.encodeBinary === 'function') {
108
+ // Set param format to binary
109
+ io.buffer.writeInt16BE(Protocol.DataFormat.binary, formatOffset + i * 2);
110
+ // Preserve data length
111
+ io.writeInt32BE(0);
112
+ const dataOffset = io.offset;
113
+ if (dt.elementsOID) {
114
+ // If data type is array
115
+ v = Array.isArray(v) ? v : [v];
116
+ encodeBinaryArray(io, v, dt.elementsOID, queryOptions, dt.encodeBinary, dt.encodeCalculateDim);
117
+ }
118
+ else {
119
+ dt.encodeBinary(io, v, queryOptions);
120
+ }
121
+ io.buffer.writeInt32BE(io.length - dataOffset, dataOffset - 4); // Update length
122
+ }
123
+ else if (typeof dt.encodeText === 'function') {
124
+ v = dt.elementsOID ? stringifyArrayLiteral(v, queryOptions, dt.encodeText) : dt.encodeText(v, queryOptions);
125
+ io.writeLString(v, 'utf8');
126
+ }
127
+ }
128
+ else if (Buffer.isBuffer(v)) {
129
+ // Set param format to binary
130
+ io.buffer.writeInt16BE(Protocol.DataFormat.binary, formatOffset + i * 2);
131
+ // Preserve data length
132
+ io.writeInt32BE(0);
133
+ const dataOffset = io.offset;
134
+ io.writeBuffer(v);
135
+ io.buffer.writeInt32BE(io.length - dataOffset, dataOffset - 4); // Update length
136
+ }
137
+ else {
138
+ io.writeLString('' + v, 'utf8');
139
+ }
140
+ }
141
+ }
142
+ else {
143
+ io.writeUInt16BE(0);
144
+ io.writeUInt16BE(0);
145
+ }
146
+ if (Array.isArray(columnFormat)) {
147
+ io.writeUInt16BE(columnFormat.length);
148
+ for (let i = 0; i < columnFormat.length; i++) {
149
+ io.writeUInt16BE(columnFormat[i]);
150
+ }
151
+ }
152
+ else if (columnFormat === DataFormat.binary) {
153
+ io.writeUInt16BE(1);
154
+ io.writeUInt16BE(DataFormat.binary);
155
+ }
156
+ else
157
+ io.writeUInt16BE(0);
158
+ return setLengthAndFlush(io, 1);
159
+ }
160
+ getDescribeMessage(args) {
161
+ if (args.name && args.name.length > 63)
162
+ throw new Error(args.type === 'P' ? 'Portal' : 'Statement' + 'name length must be lower than 63');
163
+ const io = this._io
164
+ .start()
165
+ .writeInt8(Protocol.FrontendMessageCode.Describe)
166
+ .writeInt32BE(0) // Preserve header
167
+ .writeUInt8(args.type.charCodeAt(0))
168
+ .writeCString(args.name || '', 'utf8');
169
+ return setLengthAndFlush(io, 1);
170
+ }
171
+ getExecuteMessage(args) {
172
+ if (args.fetchCount && (args.fetchCount < 0 || args.fetchCount > 4294967295))
173
+ throw new Error('fetchCount can be between 0 and 4294967295');
174
+ const io = this._io
175
+ .start()
176
+ .writeInt8(Protocol.FrontendMessageCode.Execute)
177
+ .writeInt32BE(0) // Preserve header
178
+ .writeCString(args.portal || '', 'utf8')
179
+ .writeUInt32BE(args.fetchCount || 0);
180
+ return setLengthAndFlush(io, 1);
181
+ }
182
+ getCloseMessage(args) {
183
+ if (args.name && args.name.length > 63)
184
+ throw new Error(args.type === 'P' ? 'Portal' : 'Statement' + 'name length must be lower than 63');
185
+ const io = this._io
186
+ .start()
187
+ .writeInt8(Protocol.FrontendMessageCode.Close)
188
+ .writeInt32BE(0) // Preserve header
189
+ .writeUInt8(args.type.charCodeAt(0))
190
+ .writeCString(args.name || '', 'utf8');
191
+ return setLengthAndFlush(io, 1);
192
+ }
193
+ getQueryMessage(sql) {
194
+ const io = this._io
195
+ .start()
196
+ .writeInt8(Protocol.FrontendMessageCode.Query)
197
+ .writeInt32BE(0) // Preserve header
198
+ .writeCString(sql || '', 'utf8');
199
+ return setLengthAndFlush(io, 1);
200
+ }
201
+ getFlushMessage() {
202
+ return StaticFlushBuffer;
203
+ }
204
+ getTerminateMessage() {
205
+ return StaticTerminateBuffer;
206
+ }
207
+ getSyncMessage() {
208
+ return StaticSyncBuffer;
209
+ }
210
+ }
211
+ function setLengthAndFlush(io, lengthOffset) {
212
+ io.buffer.writeUInt32BE(io.length - lengthOffset, lengthOffset);
213
+ return io.flush();
214
+ }
@@ -0,0 +1,338 @@
1
+ import crypto from 'crypto';
2
+ import net from 'net';
3
+ import promisify from 'putil-promisify';
4
+ import tls from 'tls';
5
+ import { ConnectionState } from '../constants.js';
6
+ import { SafeEventEmitter } from '../safe-event-emitter.js';
7
+ import { Backend } from './backend.js';
8
+ import { DatabaseError } from './database-error.js';
9
+ import { Frontend } from './frontend.js';
10
+ import { Protocol } from './protocol.js';
11
+ import { SASL } from './sasl.js';
12
+ const DEFAULT_PORT_NUMBER = 5432;
13
+ const COMMAND_RESULT_PATTERN = /^([^\d]+)(?: (\d+)(?: (\d+))?)?$/;
14
+ export class PgSocket extends SafeEventEmitter {
15
+ constructor(options) {
16
+ super();
17
+ this.options = options;
18
+ this._state = ConnectionState.CLOSED;
19
+ this._backend = new Backend();
20
+ this._sessionParameters = {};
21
+ this._frontend = new Frontend({ buffer: options.buffer });
22
+ this.setMaxListeners(99);
23
+ }
24
+ get state() {
25
+ if (!this._socket || this._socket.destroyed)
26
+ this._state = ConnectionState.CLOSED;
27
+ return this._state;
28
+ }
29
+ get processID() {
30
+ return this._processID;
31
+ }
32
+ get secretKey() {
33
+ return this._secretKey;
34
+ }
35
+ get sessionParameters() {
36
+ return this._sessionParameters;
37
+ }
38
+ connect() {
39
+ if (this._socket)
40
+ return;
41
+ this._state = ConnectionState.CONNECTING;
42
+ const options = this.options;
43
+ const socket = (this._socket = new net.Socket());
44
+ const errorHandler = err => {
45
+ this._state = ConnectionState.CLOSED;
46
+ this._removeListeners();
47
+ this._reset();
48
+ socket.destroy();
49
+ this._socket = undefined;
50
+ this.emit('error', err);
51
+ };
52
+ const connectHandler = () => {
53
+ socket.setTimeout(0);
54
+ if (this.options.keepAlive || this.options.keepAlive == null)
55
+ socket.setKeepAlive(true);
56
+ if (options.ssl) {
57
+ socket.write(this._frontend.getSSLRequestMessage());
58
+ socket.once('data', x => {
59
+ this._removeListeners();
60
+ if (x.toString() === 'S') {
61
+ const tslOptions = { ...options.ssl, socket };
62
+ if (options.host && net.isIP(options.host) === 0)
63
+ tslOptions.servername = options.host;
64
+ const tlsSocket = (this._socket = tls.connect(tslOptions));
65
+ tlsSocket.once('error', errorHandler);
66
+ tlsSocket.once('secureConnect', () => {
67
+ this._removeListeners();
68
+ this._handleConnect();
69
+ });
70
+ return;
71
+ }
72
+ if (x.toString() === 'N')
73
+ return errorHandler(new Error('Server does not support SSL connections'));
74
+ return errorHandler(new Error('There was an error establishing an SSL connection'));
75
+ });
76
+ }
77
+ else {
78
+ this._handleConnect();
79
+ }
80
+ };
81
+ socket.setNoDelay(true);
82
+ socket.setTimeout(options.connectTimeoutMs || 30000, () => errorHandler(new Error('Connection timed out')));
83
+ socket.once('error', errorHandler);
84
+ socket.once('connect', connectHandler);
85
+ this.emit('connecting');
86
+ if (options.host && options.host.startsWith('/'))
87
+ socket.connect(options.host);
88
+ else
89
+ socket.connect(options.port || DEFAULT_PORT_NUMBER, options.host || 'localhost');
90
+ }
91
+ close() {
92
+ if (!this._socket || this._socket.destroyed) {
93
+ this._state = ConnectionState.CLOSED;
94
+ this._socket = undefined;
95
+ this._reset();
96
+ return;
97
+ }
98
+ if (this._state === ConnectionState.CLOSING)
99
+ return;
100
+ const socket = this._socket;
101
+ this._state = ConnectionState.CLOSING;
102
+ this._removeListeners();
103
+ socket.once('close', () => this._handleClose());
104
+ socket.destroy();
105
+ }
106
+ sendParseMessage(args, cb) {
107
+ if (this.listenerCount('debug'))
108
+ this.emit('debug', { location: 'PgSocket.sendParseMessage', args });
109
+ this._send(this._frontend.getParseMessage(args), cb);
110
+ }
111
+ sendBindMessage(args, cb) {
112
+ if (this.listenerCount('debug'))
113
+ this.emit('debug', { location: 'PgSocket.sendBindMessage', args });
114
+ this._send(this._frontend.getBindMessage(args), cb);
115
+ }
116
+ sendDescribeMessage(args, cb) {
117
+ if (this.listenerCount('debug'))
118
+ this.emit('debug', { location: 'PgSocket.sendDescribeMessage', args });
119
+ this._send(this._frontend.getDescribeMessage(args), cb);
120
+ }
121
+ sendExecuteMessage(args, cb) {
122
+ if (this.listenerCount('debug'))
123
+ this.emit('debug', { location: 'PgSocket.sendDescribeMessage', args });
124
+ this._send(this._frontend.getExecuteMessage(args), cb);
125
+ }
126
+ sendCloseMessage(args, cb) {
127
+ if (this.listenerCount('debug'))
128
+ this.emit('debug', { location: 'PgSocket.sendCloseMessage', args });
129
+ this._send(this._frontend.getCloseMessage(args), cb);
130
+ }
131
+ sendQueryMessage(sql, cb) {
132
+ if (this.listenerCount('debug'))
133
+ this.emit('debug', { location: 'PgSocket.sendQueryMessage', sql });
134
+ this._send(this._frontend.getQueryMessage(sql), cb);
135
+ }
136
+ sendFlushMessage(cb) {
137
+ if (this.listenerCount('debug'))
138
+ this.emit('debug', { location: 'PgSocket.sendFlushMessage' });
139
+ this._send(this._frontend.getFlushMessage(), cb);
140
+ }
141
+ sendTerminateMessage(cb) {
142
+ if (this.listenerCount('debug'))
143
+ this.emit('debug', { location: 'PgSocket.sendTerminateMessage' });
144
+ this._send(this._frontend.getTerminateMessage(), cb);
145
+ }
146
+ sendSyncMessage() {
147
+ if (this.listenerCount('debug'))
148
+ this.emit('debug', { location: 'PgSocket.sendSyncMessage' });
149
+ this._send(this._frontend.getSyncMessage());
150
+ }
151
+ capture(callback) {
152
+ return new Promise((resolve, reject) => {
153
+ const done = (err, result) => {
154
+ this.removeListener('error', errorHandler);
155
+ this.removeListener('message', msgHandler);
156
+ if (err)
157
+ reject(err);
158
+ else
159
+ resolve(result);
160
+ };
161
+ const errorHandler = (err) => {
162
+ this.removeListener('message', msgHandler);
163
+ reject(err);
164
+ };
165
+ const msgHandler = (code, msg) => {
166
+ const x = callback(code, msg, done);
167
+ if (promisify.isPromise(x))
168
+ x.catch(err => done(err));
169
+ };
170
+ this.once('error', errorHandler);
171
+ this.on('message', msgHandler);
172
+ });
173
+ }
174
+ _removeListeners() {
175
+ if (!this._socket)
176
+ return;
177
+ this._socket.removeAllListeners('error');
178
+ this._socket.removeAllListeners('connect');
179
+ this._socket.removeAllListeners('data');
180
+ this._socket.removeAllListeners('close');
181
+ }
182
+ _reset() {
183
+ this._backend.reset();
184
+ this._sessionParameters = {};
185
+ this._processID = undefined;
186
+ this._secretKey = undefined;
187
+ this._saslSession = undefined;
188
+ }
189
+ _handleConnect() {
190
+ const socket = this._socket;
191
+ if (!socket)
192
+ return;
193
+ this._state = ConnectionState.AUTHORIZING;
194
+ this._reset();
195
+ socket.on('data', (data) => this._handleData(data));
196
+ socket.on('error', (err) => this._handleError(err));
197
+ socket.on('close', () => this._handleClose());
198
+ this._send(this._frontend.getStartupMessage({
199
+ user: this.options.user || 'postgres',
200
+ database: this.options.database || '',
201
+ }));
202
+ }
203
+ _handleClose() {
204
+ this._reset();
205
+ this._socket = undefined;
206
+ this._state = ConnectionState.CLOSED;
207
+ this.emit('close');
208
+ }
209
+ _handleError(err) {
210
+ if (this._state !== ConnectionState.READY) {
211
+ this._socket?.end();
212
+ }
213
+ this.emit('error', err);
214
+ }
215
+ _handleData(data) {
216
+ this._backend.parse(data, (code, payload) => {
217
+ try {
218
+ switch (code) {
219
+ case Protocol.BackendMessageCode.Authentication:
220
+ this._handleAuthenticationMessage(payload);
221
+ break;
222
+ case Protocol.BackendMessageCode.ErrorResponse:
223
+ this.emit('error', new DatabaseError(payload));
224
+ break;
225
+ case Protocol.BackendMessageCode.NoticeResponse:
226
+ this.emit('notice', payload);
227
+ break;
228
+ case Protocol.BackendMessageCode.NotificationResponse:
229
+ this.emit('notification', payload);
230
+ break;
231
+ case Protocol.BackendMessageCode.ParameterStatus:
232
+ this._handleParameterStatus(payload);
233
+ break;
234
+ case Protocol.BackendMessageCode.BackendKeyData:
235
+ this._handleBackendKeyData(payload);
236
+ break;
237
+ case Protocol.BackendMessageCode.ReadyForQuery:
238
+ if (this._state !== ConnectionState.READY) {
239
+ this._state = ConnectionState.READY;
240
+ this.emit('ready');
241
+ }
242
+ else
243
+ this.emit('message', code, payload);
244
+ break;
245
+ case Protocol.BackendMessageCode.CommandComplete: {
246
+ const msg = this._handleCommandComplete(payload);
247
+ this.emit('message', code, msg);
248
+ break;
249
+ }
250
+ default:
251
+ this.emit('message', code, payload);
252
+ }
253
+ }
254
+ catch (e) {
255
+ this._handleError(e);
256
+ }
257
+ });
258
+ }
259
+ _resolvePassword(cb) {
260
+ (async () => {
261
+ const pass = typeof this.options.password === 'function' ? await this.options.password() : this.options.password;
262
+ cb(pass || '');
263
+ })().catch(err => this._handleError(err));
264
+ }
265
+ _handleAuthenticationMessage(msg) {
266
+ if (!msg) {
267
+ this.emit('authenticate');
268
+ return;
269
+ }
270
+ switch (msg.kind) {
271
+ case Protocol.AuthenticationMessageKind.CleartextPassword:
272
+ this._resolvePassword(password => {
273
+ this._send(this._frontend.getPasswordMessage(password));
274
+ });
275
+ break;
276
+ case Protocol.AuthenticationMessageKind.MD5Password:
277
+ this._resolvePassword(password => {
278
+ const md5 = (x) => crypto.createHash('md5').update(x, 'utf8').digest('hex');
279
+ const l = md5(password + this.options.user);
280
+ const r = md5(Buffer.concat([Buffer.from(l), msg.salt]));
281
+ const pass = 'md5' + r;
282
+ this._send(this._frontend.getPasswordMessage(pass));
283
+ });
284
+ break;
285
+ case Protocol.AuthenticationMessageKind.SASL: {
286
+ if (!msg.mechanisms.includes('SCRAM-SHA-256'))
287
+ throw new Error('SASL: Only mechanism SCRAM-SHA-256 is currently supported');
288
+ const saslSession = (this._saslSession = SASL.createSession(this.options.user || '', 'SCRAM-SHA-256'));
289
+ this._send(this._frontend.getSASLMessage(saslSession));
290
+ break;
291
+ }
292
+ case Protocol.AuthenticationMessageKind.SASLContinue: {
293
+ const saslSession = this._saslSession;
294
+ if (!saslSession)
295
+ throw new Error('SASL: Session not started yet');
296
+ this._resolvePassword(password => {
297
+ SASL.continueSession(saslSession, password, msg.data);
298
+ const buf = this._frontend.getSASLFinalMessage(saslSession);
299
+ this._send(buf);
300
+ });
301
+ break;
302
+ }
303
+ case Protocol.AuthenticationMessageKind.SASLFinal: {
304
+ const session = this._saslSession;
305
+ if (!session)
306
+ throw new Error('SASL: Session not started yet');
307
+ SASL.finalizeSession(session, msg.data);
308
+ this._saslSession = undefined;
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ _handleParameterStatus(msg) {
314
+ this._sessionParameters[msg.name] = msg.value;
315
+ }
316
+ _handleBackendKeyData(msg) {
317
+ this._processID = msg.processID;
318
+ this._secretKey = msg.secretKey;
319
+ }
320
+ _handleCommandComplete(msg) {
321
+ const m = msg.command && msg.command.match(COMMAND_RESULT_PATTERN);
322
+ const result = {
323
+ command: m[1],
324
+ };
325
+ if (m[3] != null) {
326
+ result.oid = parseInt(m[2], 10);
327
+ result.rowCount = parseInt(m[3], 10);
328
+ }
329
+ else if (m[2])
330
+ result.rowCount = parseInt(m[2], 10);
331
+ return result;
332
+ }
333
+ _send(data, cb) {
334
+ if (this._socket && this._socket.writable) {
335
+ this._socket.write(data, cb);
336
+ }
337
+ }
338
+ }
@@ -0,0 +1,68 @@
1
+ export var Protocol;
2
+ (function (Protocol) {
3
+ Protocol.VERSION_MAJOR = 3;
4
+ Protocol.VERSION_MINOR = 0;
5
+ // https://www.postgresql.org/docs/9.3/protocol-message-formats.html
6
+ let BackendMessageCode;
7
+ (function (BackendMessageCode) {
8
+ BackendMessageCode[BackendMessageCode["Authentication"] = 82] = "Authentication";
9
+ BackendMessageCode[BackendMessageCode["BackendKeyData"] = 75] = "BackendKeyData";
10
+ BackendMessageCode[BackendMessageCode["BindComplete"] = 50] = "BindComplete";
11
+ BackendMessageCode[BackendMessageCode["CloseComplete"] = 51] = "CloseComplete";
12
+ BackendMessageCode[BackendMessageCode["CommandComplete"] = 67] = "CommandComplete";
13
+ BackendMessageCode[BackendMessageCode["CopyData"] = 100] = "CopyData";
14
+ BackendMessageCode[BackendMessageCode["CopyDone"] = 99] = "CopyDone";
15
+ BackendMessageCode[BackendMessageCode["CopyInResponse"] = 103] = "CopyInResponse";
16
+ BackendMessageCode[BackendMessageCode["CopyOutResponse"] = 72] = "CopyOutResponse";
17
+ BackendMessageCode[BackendMessageCode["CopyBothResponse"] = 87] = "CopyBothResponse";
18
+ BackendMessageCode[BackendMessageCode["DataRow"] = 68] = "DataRow";
19
+ BackendMessageCode[BackendMessageCode["EmptyQueryResponse"] = 73] = "EmptyQueryResponse";
20
+ BackendMessageCode[BackendMessageCode["ErrorResponse"] = 69] = "ErrorResponse";
21
+ BackendMessageCode[BackendMessageCode["FunctionCallResponse"] = 86] = "FunctionCallResponse";
22
+ BackendMessageCode[BackendMessageCode["NegotiateProtocolVersion"] = 118] = "NegotiateProtocolVersion";
23
+ BackendMessageCode[BackendMessageCode["NoData"] = 110] = "NoData";
24
+ BackendMessageCode[BackendMessageCode["NoticeResponse"] = 78] = "NoticeResponse";
25
+ BackendMessageCode[BackendMessageCode["NotificationResponse"] = 65] = "NotificationResponse";
26
+ BackendMessageCode[BackendMessageCode["ParameterDescription"] = 116] = "ParameterDescription";
27
+ BackendMessageCode[BackendMessageCode["ParameterStatus"] = 83] = "ParameterStatus";
28
+ BackendMessageCode[BackendMessageCode["ParseComplete"] = 49] = "ParseComplete";
29
+ BackendMessageCode[BackendMessageCode["PortalSuspended"] = 115] = "PortalSuspended";
30
+ BackendMessageCode[BackendMessageCode["ReadyForQuery"] = 90] = "ReadyForQuery";
31
+ BackendMessageCode[BackendMessageCode["RowDescription"] = 84] = "RowDescription";
32
+ })(BackendMessageCode = Protocol.BackendMessageCode || (Protocol.BackendMessageCode = {}));
33
+ let FrontendMessageCode;
34
+ (function (FrontendMessageCode) {
35
+ FrontendMessageCode[FrontendMessageCode["Bind"] = 66] = "Bind";
36
+ FrontendMessageCode[FrontendMessageCode["Close"] = 67] = "Close";
37
+ FrontendMessageCode[FrontendMessageCode["CopyData"] = 100] = "CopyData";
38
+ FrontendMessageCode[FrontendMessageCode["CopyDone"] = 99] = "CopyDone";
39
+ FrontendMessageCode[FrontendMessageCode["CopyFail"] = 102] = "CopyFail";
40
+ FrontendMessageCode[FrontendMessageCode["Describe"] = 68] = "Describe";
41
+ FrontendMessageCode[FrontendMessageCode["Execute"] = 69] = "Execute";
42
+ FrontendMessageCode[FrontendMessageCode["Flush"] = 72] = "Flush";
43
+ FrontendMessageCode[FrontendMessageCode["FunctionCall"] = 70] = "FunctionCall";
44
+ FrontendMessageCode[FrontendMessageCode["Parse"] = 80] = "Parse";
45
+ FrontendMessageCode[FrontendMessageCode["PasswordMessage"] = 112] = "PasswordMessage";
46
+ FrontendMessageCode[FrontendMessageCode["Query"] = 81] = "Query";
47
+ FrontendMessageCode[FrontendMessageCode["Sync"] = 83] = "Sync";
48
+ FrontendMessageCode[FrontendMessageCode["Terminate"] = 88] = "Terminate";
49
+ })(FrontendMessageCode = Protocol.FrontendMessageCode || (Protocol.FrontendMessageCode = {}));
50
+ let AuthenticationMessageKind;
51
+ (function (AuthenticationMessageKind) {
52
+ AuthenticationMessageKind["KerberosV5"] = "KerberosV5";
53
+ AuthenticationMessageKind["CleartextPassword"] = "CleartextPassword";
54
+ AuthenticationMessageKind["MD5Password"] = "MD5Password";
55
+ AuthenticationMessageKind["SCMCredential"] = "SCMCredential";
56
+ AuthenticationMessageKind["GSS"] = "GSS";
57
+ AuthenticationMessageKind["SSPI"] = "SSPI";
58
+ AuthenticationMessageKind["GSSContinue"] = "GSSContinue";
59
+ AuthenticationMessageKind["SASL"] = "SASL";
60
+ AuthenticationMessageKind["SASLContinue"] = "SASLContinue";
61
+ AuthenticationMessageKind["SASLFinal"] = "SASLFinal";
62
+ })(AuthenticationMessageKind = Protocol.AuthenticationMessageKind || (Protocol.AuthenticationMessageKind = {}));
63
+ let DataFormat;
64
+ (function (DataFormat) {
65
+ DataFormat[DataFormat["text"] = 0] = "text";
66
+ DataFormat[DataFormat["binary"] = 1] = "binary";
67
+ })(DataFormat = Protocol.DataFormat || (Protocol.DataFormat = {}));
68
+ })(Protocol || (Protocol = {}));