@m2k-5f/pgtx 2.6.11 → 2.7.1
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.
- package/README.md +244 -494
- package/dist/batch.d.ts +2 -1
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +8 -7
- package/dist/connection.d.ts +39 -150
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +199 -284
- package/dist/error.d.ts +4 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +4 -0
- package/dist/pool.d.ts +29 -181
- package/dist/pool.d.ts.map +1 -1
- package/dist/pool.js +49 -180
- package/dist/protocol/connection-request-writer.d.ts +1 -0
- package/dist/protocol/connection-request-writer.d.ts.map +1 -1
- package/dist/protocol/connection-request-writer.js +6 -0
- package/dist/protocol/connection-response-reader.d.ts +1 -0
- package/dist/protocol/connection-response-reader.d.ts.map +1 -1
- package/dist/protocol/connection-response-reader.js +3 -0
- package/dist/protocol/constants.d.ts +2 -0
- package/dist/protocol/constants.d.ts.map +1 -1
- package/dist/protocol/constants.js +3 -1
- package/dist/protocol/socket-authorization.d.ts +5 -4
- package/dist/protocol/socket-authorization.d.ts.map +1 -1
- package/dist/protocol/socket-authorization.js +167 -74
- package/dist/protocol/socket-connector.d.ts +5 -5
- package/dist/protocol/socket-connector.d.ts.map +1 -1
- package/dist/protocol/socket-connector.js +24 -15
- package/dist/query.d.ts +14 -17
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +20 -17
- package/dist/transaction.d.ts +9 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +11 -0
- package/dist/types.d.ts +14 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA6B,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC5G,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAOxC,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,KAAK,CAAI;IAEjB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAAI,MAAM;IAOlB,SAAS,IAAI,MAAM;IAOnB,SAAS,IAAI,MAAM;IAOnB,WAAW,IAAI,MAAM;IAQrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOrC,OAAO,IAAI,OAAO;IAKlB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO3C,cAAc,IAAI,IAAI;IAOtB,mBAAmB,IAAI,IAAI;IAO3B,cAAc,IAAI,MAAM;IAaxB,SAAS,CAAC,SAAS,EAAE,MAAM;IAK3B,aAAa,IAAI,OAAO;IAaxB,iBAAiB;IAIjB,QAAQ;IAMR,YAAY,IAAI,MAAM;IAOtB,SAAS;IAST,WAAW;IAOX,WAAW,IAAI,MAAM;IAMrB,QAAQ;IAWR,SAAS,CAAC,MAAM,EAAE,MAAM;IAQxB,aAAa,CAAC,MAAM,EAAE,MAAM;CAW/B;AAGD,qBAAa,wBAAwB;IAG7B,OAAO,CAAC,MAAM;IADlB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ;cACoC,YAAY;;;IAIxD,kBAAkB,IACoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAQnB,kBAAkB;;;;IAQlB,iBAAiB,IAAI,aAAa;IA4ClC,iBAAiB,IACoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAKvC,kBAAkB;IAsBlB,WAAW,CAAC,YAAY,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA0G1F,wBAAwB;;;;IASxB,mBAAmB,IAAI,MAAM;IAK7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAGjB,gBAAgB;IAGhB,wBAAwB;IAOxB,UAAU;
|
|
1
|
+
{"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA6B,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC5G,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAOxC,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,KAAK,CAAI;IAEjB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAAI,MAAM;IAOlB,SAAS,IAAI,MAAM;IAOnB,SAAS,IAAI,MAAM;IAOnB,WAAW,IAAI,MAAM;IAQrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOrC,OAAO,IAAI,OAAO;IAKlB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO3C,cAAc,IAAI,IAAI;IAOtB,mBAAmB,IAAI,IAAI;IAO3B,cAAc,IAAI,MAAM;IAaxB,SAAS,CAAC,SAAS,EAAE,MAAM;IAK3B,aAAa,IAAI,OAAO;IAaxB,iBAAiB;IAIjB,QAAQ;IAMR,YAAY,IAAI,MAAM;IAOtB,SAAS;IAST,WAAW;IAOX,WAAW,IAAI,MAAM;IAMrB,QAAQ;IAWR,SAAS,CAAC,MAAM,EAAE,MAAM;IAQxB,aAAa,CAAC,MAAM,EAAE,MAAM;CAW/B;AAGD,qBAAa,wBAAwB;IAG7B,OAAO,CAAC,MAAM;IADlB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ;cACoC,YAAY;;;IAIxD,kBAAkB,IACoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAQnB,kBAAkB;;;;IAQlB,iBAAiB,IAAI,aAAa;IA4ClC,iBAAiB,IACoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAKvC,kBAAkB;IAsBlB,WAAW,CAAC,YAAY,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA0G1F,wBAAwB;;;;IASxB,mBAAmB,IAAI,MAAM;IAK7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAGjB,gBAAgB;IAGhB,wBAAwB;IAOxB,UAAU;IAEV,IAAI,CAAC,KAAK,EAAE,MAAM;CAGrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/protocol/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAElC,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,YAAY,CAAC,CAAA;AAGtD,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/protocol/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAElC,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,YAAY,CAAC,CAAA;AAGtD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;CAmBhB,CAAA;AAGV,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDf,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAIzE,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,aAAa,CAAC,CAAA;AAGxD,eAAO,MAAM,mBAAmB;;;;;;;CAOtB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAGpE,eAAO,MAAM,mBAAmB;;;;CAItB,CAAA;AAEV,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Socket } from "node:net";
|
|
2
|
-
import { ConnectionRequestWriter } from "./connection-request-writer";
|
|
3
1
|
import { PostgresError } from "../error";
|
|
4
2
|
import { Future } from "fluent-future";
|
|
5
|
-
import {
|
|
6
|
-
|
|
3
|
+
import { Socket } from "node:net";
|
|
4
|
+
import { ConnectionConfig } from "../types";
|
|
5
|
+
export declare const createSocket: (config: ConnectionConfig) => Future<Socket, PostgresError>;
|
|
6
|
+
export declare const upgradeSocket: (socket: Socket, config: ConnectionConfig) => Future<Socket, PostgresError>;
|
|
7
|
+
export declare const authorizeSocket: (socket: Socket, config: ConnectionConfig) => Future<Socket, PostgresError>;
|
|
7
8
|
//# sourceMappingURL=socket-authorization.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-authorization.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-authorization.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"socket-authorization.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-authorization.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4J,aAAa,EAAE,MAAM,UAAU,CAAA;AAClM,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEtC,OAAO,EAAoB,MAAM,EAAE,MAAM,UAAU,CAAA;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAI3C,eAAO,MAAM,YAAY,GAAI,QAAQ,gBAAgB,kCASpD,CAAA;AAGD,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,QAAQ,gBAAgB,kCA+ErE,CAAA;AAGD,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,EAAE,QAAQ,gBAAgB,kCAyHvE,CAAA"}
|
|
@@ -1,93 +1,186 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConnectionRequestWriter } from "./connection-request-writer";
|
|
2
2
|
import { calculateScramAuth, generateNonce } from "../security/sasl";
|
|
3
3
|
import { SocketConnector } from "./socket-connector";
|
|
4
4
|
import { AuthenticationCodes, ResponseTypes } from "./constants";
|
|
5
5
|
import { encryptMd5 } from "../security/md5";
|
|
6
|
-
import { ErrNonceMismatch, ErrPasswordRequired, ErrSocketFailedDuringAuth } from "../error";
|
|
6
|
+
import { ErrCertificateFileNotFound, ErrDatabaseNotFound, ErrNonceMismatch, ErrPasswordRequired, ErrSocketFailedDuringAuth, ErrSSLDenied, ErrUntrustedCertificate } from "../error";
|
|
7
7
|
import { Future } from "fluent-future";
|
|
8
|
-
|
|
8
|
+
import { connect } from "node:tls";
|
|
9
|
+
import { createConnection } from "node:net";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
export const createSocket = (config) => {
|
|
12
|
+
const { future, resolve, reject } = Future.withResolvers();
|
|
13
|
+
const socket = createConnection({ host: config.host, port: config.port });
|
|
14
|
+
socket.once('connect', () => resolve(socket));
|
|
15
|
+
socket.once('error', () => reject(ErrDatabaseNotFound));
|
|
16
|
+
return future;
|
|
17
|
+
};
|
|
18
|
+
export const upgradeSocket = (socket, config) => {
|
|
19
|
+
if (config.ssl === 'disable')
|
|
20
|
+
return Future.resolve(socket);
|
|
21
|
+
const { future, reject, resolve } = Future.withResolvers();
|
|
22
|
+
const writer = ConnectionRequestWriter.new();
|
|
23
|
+
const onError = () => {
|
|
24
|
+
cleanup();
|
|
25
|
+
reject(ErrSocketFailedDuringAuth);
|
|
26
|
+
};
|
|
27
|
+
socket.once('error', onError);
|
|
28
|
+
const cleanup = () => {
|
|
29
|
+
socket.off('error', onError);
|
|
30
|
+
};
|
|
31
|
+
socket.once('data', (data) => {
|
|
32
|
+
cleanup();
|
|
33
|
+
const responseCode = data.toString('utf8', 0, 1);
|
|
34
|
+
if (responseCode === 'S') {
|
|
35
|
+
if (data.length > 1) {
|
|
36
|
+
socket.unshift(data.subarray(1));
|
|
37
|
+
}
|
|
38
|
+
let cert = undefined;
|
|
39
|
+
try {
|
|
40
|
+
cert = config.caPath ? readFileSync(config.caPath) : undefined;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
socket.destroy();
|
|
44
|
+
return reject(ErrCertificateFileNotFound);
|
|
45
|
+
}
|
|
46
|
+
const tls = connect({
|
|
47
|
+
socket: socket,
|
|
48
|
+
host: config.host,
|
|
49
|
+
rejectUnauthorized: config.ssl === 'require',
|
|
50
|
+
ca: cert
|
|
51
|
+
});
|
|
52
|
+
const tlsCleanup = () => {
|
|
53
|
+
tls.off('secureConnect', onSecureConnect);
|
|
54
|
+
tls.off('error', onTlsError);
|
|
55
|
+
};
|
|
56
|
+
const onSecureConnect = () => {
|
|
57
|
+
tlsCleanup();
|
|
58
|
+
resolve(tls);
|
|
59
|
+
};
|
|
60
|
+
const onTlsError = () => {
|
|
61
|
+
tlsCleanup();
|
|
62
|
+
tls.destroy();
|
|
63
|
+
reject(ErrUntrustedCertificate);
|
|
64
|
+
};
|
|
65
|
+
tls.once('secureConnect', onSecureConnect);
|
|
66
|
+
tls.once('error', onTlsError);
|
|
67
|
+
}
|
|
68
|
+
else if (responseCode === 'N') {
|
|
69
|
+
if (config.ssl === 'require') {
|
|
70
|
+
socket.destroy();
|
|
71
|
+
reject(ErrSSLDenied);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
resolve(socket);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
socket.destroy();
|
|
78
|
+
reject(ErrSocketFailedDuringAuth);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
socket.write(writer.writeSSLRequest().asBuffer());
|
|
82
|
+
writer.clear();
|
|
83
|
+
return future;
|
|
84
|
+
};
|
|
85
|
+
export const authorizeSocket = (socket, config) => {
|
|
9
86
|
const { future, reject, resolve } = Future.withResolvers();
|
|
87
|
+
const writer = ConnectionRequestWriter.new();
|
|
10
88
|
const nonce = generateNonce();
|
|
11
89
|
let clientMessage = '';
|
|
12
90
|
let serverMessage = '';
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
writer.clear();
|
|
91
|
+
const connector = new SocketConnector(socket, handle, () => reject(ErrSocketFailedDuringAuth));
|
|
92
|
+
function handle(type, length, reader) {
|
|
16
93
|
switch (type) {
|
|
17
|
-
case ResponseTypes.Authentication:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
case AuthenticationCodes.CleartextPassword: {
|
|
21
|
-
if (!params.password)
|
|
22
|
-
throw ErrPasswordRequired;
|
|
23
|
-
connector.write(writer.writePassword(params.password));
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
case AuthenticationCodes.MD5Password: {
|
|
27
|
-
const salt = reader.readMD5Salt();
|
|
28
|
-
if (!params.password)
|
|
29
|
-
throw ErrPasswordRequired;
|
|
30
|
-
const password = encryptMd5(params.password, params.user, salt);
|
|
31
|
-
connector.write(writer.writePassword(password));
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
case AuthenticationCodes.SASL: {
|
|
35
|
-
reader.readSaslMechanisms();
|
|
36
|
-
if (!params.password)
|
|
37
|
-
throw ErrPasswordRequired;
|
|
38
|
-
clientMessage = `n=${params.user},r=${nonce}`;
|
|
39
|
-
connector.write(writer.writeSaslInitial('SCRAM-SHA-256', `n,,${clientMessage}`));
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
case AuthenticationCodes.SASLContinue: {
|
|
43
|
-
serverMessage = reader.readSaslMessage(length);
|
|
44
|
-
if (!params.password)
|
|
45
|
-
throw ErrPasswordRequired;
|
|
46
|
-
const parts = Object.fromEntries(serverMessage.split(',').map(x => x.split('=')));
|
|
47
|
-
const serverNonce = parts.r;
|
|
48
|
-
const saltBase64 = parts.s;
|
|
49
|
-
const iterations = parseInt(parts.i, 10);
|
|
50
|
-
if (!serverNonce.startsWith(nonce)) {
|
|
51
|
-
connector.destroy();
|
|
52
|
-
return reject(ErrNonceMismatch);
|
|
53
|
-
}
|
|
54
|
-
const clientFinalMessageWithoutProof = `c=biws,r=${serverNonce}`;
|
|
55
|
-
const authMessage = `${clientMessage},${serverMessage},${clientFinalMessageWithoutProof}`;
|
|
56
|
-
const { clientProof } = calculateScramAuth(params.password, saltBase64, iterations, authMessage);
|
|
57
|
-
const clientFinalMessage = `${clientFinalMessageWithoutProof},p=${clientProof}`;
|
|
58
|
-
connector.write(writer.writeSaslResponse(clientFinalMessage));
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
case AuthenticationCodes.SASLFinal: {
|
|
62
|
-
reader.readSaslMessage(length);
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
94
|
+
case ResponseTypes.Authentication:
|
|
95
|
+
{
|
|
96
|
+
Authentication(length, reader);
|
|
65
97
|
}
|
|
66
98
|
break;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
case ResponseTypes.ParamaterStatus:
|
|
100
|
+
{
|
|
101
|
+
reader.readParameterStatus();
|
|
102
|
+
}
|
|
70
103
|
break;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
case ResponseTypes.ErrorResponse:
|
|
105
|
+
{
|
|
106
|
+
const error = reader.readErrorResponse();
|
|
107
|
+
connector.destroy();
|
|
108
|
+
reject(error);
|
|
109
|
+
}
|
|
76
110
|
return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
case ResponseTypes.BackendKeyData:
|
|
112
|
+
{
|
|
113
|
+
reader.readBackendKeyData();
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case ResponseTypes.ReadyForQuery:
|
|
117
|
+
{
|
|
118
|
+
reader.readReadyForQuery();
|
|
119
|
+
resolve(connector.unwrapSocket());
|
|
120
|
+
}
|
|
84
121
|
return;
|
|
85
|
-
}
|
|
86
122
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
123
|
+
}
|
|
124
|
+
function Authentication(length, reader) {
|
|
125
|
+
switch (reader.readAuthentication()) {
|
|
126
|
+
case AuthenticationCodes.Ok: break;
|
|
127
|
+
case AuthenticationCodes.CleartextPassword:
|
|
128
|
+
{
|
|
129
|
+
if (!config.password)
|
|
130
|
+
throw ErrPasswordRequired;
|
|
131
|
+
connector.write(writer.writePassword(config.password));
|
|
132
|
+
writer.clear();
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
case AuthenticationCodes.MD5Password:
|
|
136
|
+
{
|
|
137
|
+
const salt = reader.readMD5Salt();
|
|
138
|
+
if (!config.password)
|
|
139
|
+
throw ErrPasswordRequired;
|
|
140
|
+
const password = encryptMd5(config.password, config.user, salt);
|
|
141
|
+
connector.write(writer.writePassword(password));
|
|
142
|
+
writer.clear();
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
case AuthenticationCodes.SASL:
|
|
146
|
+
{
|
|
147
|
+
reader.readSaslMechanisms();
|
|
148
|
+
if (!config.password)
|
|
149
|
+
throw ErrPasswordRequired;
|
|
150
|
+
clientMessage = `n=${config.user},r=${nonce}`;
|
|
151
|
+
connector.write(writer.writeSaslInitial('SCRAM-SHA-256', `n,,${clientMessage}`));
|
|
152
|
+
writer.clear();
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case AuthenticationCodes.SASLContinue:
|
|
156
|
+
{
|
|
157
|
+
serverMessage = reader.readSaslMessage(length);
|
|
158
|
+
if (!config.password)
|
|
159
|
+
throw ErrPasswordRequired;
|
|
160
|
+
const parts = Object.fromEntries(serverMessage.split(',').map(x => x.split('=')));
|
|
161
|
+
const serverNonce = parts.r;
|
|
162
|
+
const saltBase64 = parts.s;
|
|
163
|
+
const iterations = parseInt(parts.i, 10);
|
|
164
|
+
if (!serverNonce.startsWith(nonce)) {
|
|
165
|
+
connector.destroy();
|
|
166
|
+
return reject(ErrNonceMismatch);
|
|
167
|
+
}
|
|
168
|
+
const clientFinalMessageWithoutProof = `c=biws,r=${serverNonce}`;
|
|
169
|
+
const authMessage = `${clientMessage},${serverMessage},${clientFinalMessageWithoutProof}`;
|
|
170
|
+
const { clientProof } = calculateScramAuth(config.password, saltBase64, iterations, authMessage);
|
|
171
|
+
const clientFinalMessage = `${clientFinalMessageWithoutProof},p=${clientProof}`;
|
|
172
|
+
connector.write(writer.writeSaslResponse(clientFinalMessage));
|
|
173
|
+
writer.clear();
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case AuthenticationCodes.SASLFinal:
|
|
177
|
+
{
|
|
178
|
+
reader.readSaslMessage(length);
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
connector.write(writer.writeStartup(config.user, config.database));
|
|
91
184
|
writer.clear();
|
|
92
185
|
return future;
|
|
93
186
|
};
|
|
@@ -4,14 +4,14 @@ import { ConnectionResponseReader } from '../protocol/connection-response-reader
|
|
|
4
4
|
import { ConnectionRequestWriter } from '../protocol/connection-request-writer';
|
|
5
5
|
export declare class SocketConnector {
|
|
6
6
|
private _socket;
|
|
7
|
-
private _onData;
|
|
8
|
-
private _onError;
|
|
9
7
|
private residualBuffer;
|
|
10
|
-
private
|
|
11
|
-
|
|
8
|
+
private _onError;
|
|
9
|
+
private _onClose;
|
|
10
|
+
private _onData;
|
|
11
|
+
private _destroyed;
|
|
12
|
+
constructor(_socket: Socket, onData: (type: ResponseType, length: number, reader: ConnectionResponseReader) => void, onError: (error: unknown) => void);
|
|
12
13
|
write(writer: ConnectionRequestWriter): void;
|
|
13
14
|
unwrapSocket(): Socket;
|
|
14
15
|
destroy(): void;
|
|
15
|
-
get isDestroyed(): boolean;
|
|
16
16
|
}
|
|
17
17
|
//# sourceMappingURL=socket-connector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-connector.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAE/E,qBAAa,eAAe;
|
|
1
|
+
{"version":3,"file":"socket-connector.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAE/E,qBAAa,eAAe;IAQpB,OAAO,CAAC,OAAO;IAPnB,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAQ;gBAGd,OAAO,EAAE,MAAM,EACvB,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,wBAAwB,KAAK,IAAI,EACtF,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IA4CrC,KAAK,CAAC,MAAM,EAAE,uBAAuB;IAKrC,YAAY;IASZ,OAAO;CAGV"}
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { ConnectionResponseReader } from '../protocol/connection-response-reader';
|
|
2
2
|
export class SocketConnector {
|
|
3
|
-
constructor(_socket,
|
|
3
|
+
constructor(_socket, onData, onError) {
|
|
4
4
|
this._socket = _socket;
|
|
5
|
-
this._onData = _onData;
|
|
6
|
-
this._onError = _onError;
|
|
7
5
|
this.residualBuffer = null;
|
|
8
|
-
this.
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
this._destroyed = false;
|
|
7
|
+
this._onError = (err) => {
|
|
8
|
+
if (this._destroyed)
|
|
9
|
+
return;
|
|
10
|
+
this._destroyed = true;
|
|
11
|
+
onError(err);
|
|
12
|
+
this.destroy();
|
|
13
|
+
};
|
|
14
|
+
this._onClose = () => {
|
|
15
|
+
if (this._destroyed)
|
|
16
|
+
return;
|
|
17
|
+
this._destroyed = true;
|
|
18
|
+
onError(new Error("Socket closed"));
|
|
19
|
+
this.destroy();
|
|
20
|
+
};
|
|
21
|
+
this._onData = buffer => {
|
|
11
22
|
const currentBuffer = this.residualBuffer
|
|
12
23
|
? Buffer.concat([this.residualBuffer, buffer])
|
|
13
24
|
: buffer;
|
|
@@ -19,14 +30,13 @@ export class SocketConnector {
|
|
|
19
30
|
return;
|
|
20
31
|
}
|
|
21
32
|
const { type, length } = reader.readType();
|
|
22
|
-
|
|
33
|
+
onData(type, length, reader);
|
|
23
34
|
}
|
|
24
|
-
}
|
|
25
|
-
_socket.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
35
|
+
};
|
|
36
|
+
_socket.setKeepAlive(true, 10000);
|
|
37
|
+
_socket.on('data', this._onData);
|
|
38
|
+
_socket.on('error', this._onError);
|
|
39
|
+
_socket.on('close', this._onClose);
|
|
30
40
|
}
|
|
31
41
|
write(writer) {
|
|
32
42
|
this._socket.write(writer.asBuffer());
|
|
@@ -34,11 +44,10 @@ export class SocketConnector {
|
|
|
34
44
|
unwrapSocket() {
|
|
35
45
|
this._socket.off('error', this._onError);
|
|
36
46
|
this._socket.off("data", this._onData);
|
|
47
|
+
this._socket.off('close', this._onClose);
|
|
37
48
|
return this._socket;
|
|
38
49
|
}
|
|
39
50
|
destroy() {
|
|
40
|
-
this._isDestroyed = true;
|
|
41
51
|
this._socket.destroy();
|
|
42
52
|
}
|
|
43
|
-
get isDestroyed() { return this._isDestroyed; }
|
|
44
53
|
}
|
package/dist/query.d.ts
CHANGED
|
@@ -1,44 +1,41 @@
|
|
|
1
1
|
import { Future } from "fluent-future";
|
|
2
|
-
import { ColumnDescription,
|
|
2
|
+
import { ColumnDescription, QueryText, Row, StatementName } from "./types";
|
|
3
3
|
import { PostgresError } from "./error";
|
|
4
4
|
export declare abstract class Query {
|
|
5
5
|
statement: StatementName;
|
|
6
|
+
text: QueryText;
|
|
7
|
+
args: (string | null)[];
|
|
6
8
|
protected _timer?: NodeJS.Timeout;
|
|
7
|
-
constructor(statement: StatementName, timeout: number);
|
|
9
|
+
constructor(statement: StatementName, text: QueryText, args: (string | null)[], timeout: number);
|
|
8
10
|
abstract reject(cause: PostgresError): void;
|
|
9
11
|
abstract resolve(...args: any[]): void;
|
|
10
12
|
}
|
|
11
|
-
export declare class
|
|
12
|
-
|
|
13
|
-
args: (string | null)[];
|
|
14
|
-
columns: ColumnDescription[];
|
|
13
|
+
export declare class CollectQuery<T extends Row> extends Query {
|
|
14
|
+
columns: ColumnDescription[] | null;
|
|
15
15
|
future: Future<T[], PostgresError>;
|
|
16
16
|
private _resolve;
|
|
17
17
|
private _reject;
|
|
18
18
|
private _rows;
|
|
19
|
-
constructor(statement: StatementName, text: QueryText, args: (string | null)[], columns: ColumnDescription[], timeout: number);
|
|
19
|
+
constructor(statement: StatementName, text: QueryText, args: (string | null)[], columns: ColumnDescription[] | null, timeout: number);
|
|
20
20
|
push(value: T): void;
|
|
21
21
|
reject(cause: PostgresError): void;
|
|
22
22
|
resolve(): void;
|
|
23
23
|
}
|
|
24
|
-
export declare class
|
|
25
|
-
|
|
26
|
-
future: Future<QueryMeta, PostgresError>;
|
|
24
|
+
export declare class ExecuteQuery extends Query {
|
|
25
|
+
future: Future<void, PostgresError>;
|
|
27
26
|
private _resolve;
|
|
28
27
|
private _reject;
|
|
29
|
-
constructor(statement: StatementName, text: QueryText, timeout: number);
|
|
28
|
+
constructor(statement: StatementName, text: QueryText, args: (string | null)[], timeout: number);
|
|
30
29
|
reject(cause: PostgresError): void;
|
|
31
|
-
resolve(
|
|
30
|
+
resolve(): void;
|
|
32
31
|
}
|
|
33
32
|
export declare class StreamQuery<T> extends Query {
|
|
34
|
-
text: QueryText;
|
|
35
|
-
args: (string | null)[];
|
|
36
33
|
controller: ReadableStreamDefaultController<T>;
|
|
37
|
-
columns: ColumnDescription[];
|
|
38
|
-
constructor(statement: StatementName, text: QueryText, args: (string | null)[], controller: ReadableStreamDefaultController<T>, columns: ColumnDescription[], timeout: number);
|
|
34
|
+
columns: ColumnDescription[] | null;
|
|
35
|
+
constructor(statement: StatementName, text: QueryText, args: (string | null)[], controller: ReadableStreamDefaultController<T>, columns: ColumnDescription[] | null, timeout: number);
|
|
39
36
|
push(value: T): void;
|
|
40
37
|
reject(cause: PostgresError): void;
|
|
41
38
|
resolve(): void;
|
|
42
39
|
}
|
|
43
|
-
export type PostgresQuery =
|
|
40
|
+
export type PostgresQuery = CollectQuery<any> | StreamQuery<any> | ExecuteQuery;
|
|
44
41
|
//# sourceMappingURL=query.d.ts.map
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtF,OAAO,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAC;AAGzD,8BAAsB,KAAK;IAIZ,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IALlC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGtB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAC9B,OAAO,EAAE,MAAM;IAQnB,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAE3C,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;CACzC;AAGD,qBAAa,YAAY,CAAC,CAAC,SAAS,GAAG,CAAE,SAAQ,KAAK;IAUvC,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI;IATvC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,CAAA;IACzC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,KAAK,CAAU;gBAGnB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAChB,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAC1C,OAAO,EAAE,MAAM;IAWnB,IAAI,CAAC,KAAK,EAAE,CAAC;IAKb,MAAM,CAAC,KAAK,EAAE,aAAa;IAM3B,OAAO;CAIV;AAGD,qBAAa,YAAa,SAAQ,KAAK;IAC5B,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;IAC1C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,OAAO,CAAiC;gBAE5C,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EACvB,OAAO,EAAE,MAAM;IASnB,MAAM,CAAC,KAAK,EAAE,aAAa;IAM3B,OAAO;CAIV;AAGD,qBAAa,WAAW,CAAC,CAAC,CAAE,SAAQ,KAAK;IAK1B,UAAU,EAAE,+BAA+B,CAAC,CAAC,CAAC;IAC9C,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI;gBAJ1C,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAChB,UAAU,EAAE,+BAA+B,CAAC,CAAC,CAAC,EAC9C,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAC1C,OAAO,EAAE,MAAM;IAMnB,IAAI,CAAC,KAAK,EAAE,CAAC;IAOb,MAAM,CAAC,KAAK,EAAE,aAAa;IAM3B,OAAO;CAMV;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,YAAY,CAAA"}
|
package/dist/query.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Future } from "fluent-future";
|
|
2
2
|
import { ErrQueryTimeout } from "./error";
|
|
3
3
|
export class Query {
|
|
4
|
-
constructor(statement, timeout) {
|
|
4
|
+
constructor(statement, text, args, timeout) {
|
|
5
5
|
this.statement = statement;
|
|
6
|
+
this.text = text;
|
|
7
|
+
this.args = args;
|
|
6
8
|
this._timer = setTimeout(() => {
|
|
7
9
|
this.reject(ErrQueryTimeout);
|
|
8
10
|
}, timeout);
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
|
-
export class
|
|
13
|
+
export class CollectQuery extends Query {
|
|
12
14
|
constructor(statement, text, args, columns, timeout) {
|
|
13
|
-
super(statement, timeout);
|
|
14
|
-
this.text = text;
|
|
15
|
-
this.args = args;
|
|
15
|
+
super(statement, text, args, timeout);
|
|
16
16
|
this.columns = columns;
|
|
17
17
|
this._rows = [];
|
|
18
18
|
const { future, reject, resolve } = Future.withResolvers();
|
|
@@ -32,34 +32,34 @@ export class SimpleQuery extends Query {
|
|
|
32
32
|
this._resolve(this._rows);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
export class
|
|
36
|
-
constructor(statement, text, timeout) {
|
|
37
|
-
super(statement, timeout);
|
|
38
|
-
this.text = text;
|
|
35
|
+
export class ExecuteQuery extends Query {
|
|
36
|
+
constructor(statement, text, args, timeout) {
|
|
37
|
+
super(statement, text, args, timeout);
|
|
39
38
|
const { future, reject, resolve } = Future.withResolvers();
|
|
40
39
|
this.future = future;
|
|
41
|
-
this._resolve = resolve;
|
|
42
40
|
this._reject = reject;
|
|
41
|
+
this._resolve = resolve;
|
|
43
42
|
}
|
|
44
43
|
reject(cause) {
|
|
45
44
|
clearTimeout(this._timer);
|
|
46
45
|
this._reject(cause);
|
|
47
46
|
}
|
|
48
|
-
resolve(
|
|
47
|
+
resolve() {
|
|
49
48
|
clearTimeout(this._timer);
|
|
50
|
-
this._resolve(
|
|
49
|
+
this._resolve();
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
export class StreamQuery extends Query {
|
|
54
53
|
constructor(statement, text, args, controller, columns, timeout) {
|
|
55
|
-
super(statement, timeout);
|
|
56
|
-
this.text = text;
|
|
57
|
-
this.args = args;
|
|
54
|
+
super(statement, text, args, timeout);
|
|
58
55
|
this.controller = controller;
|
|
59
56
|
this.columns = columns;
|
|
60
57
|
}
|
|
61
58
|
push(value) {
|
|
62
|
-
|
|
59
|
+
try {
|
|
60
|
+
this.controller.enqueue(value);
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
63
|
}
|
|
64
64
|
reject(cause) {
|
|
65
65
|
clearTimeout(this._timer);
|
|
@@ -67,6 +67,9 @@ export class StreamQuery extends Query {
|
|
|
67
67
|
}
|
|
68
68
|
resolve() {
|
|
69
69
|
clearTimeout(this._timer);
|
|
70
|
-
|
|
70
|
+
try {
|
|
71
|
+
this.controller.close();
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
71
74
|
}
|
|
72
75
|
}
|
package/dist/transaction.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Future } from "fluent-future";
|
|
2
2
|
import { Connection } from "./connection";
|
|
3
3
|
import { PostgresError } from "./error";
|
|
4
|
+
import { Row } from "./types";
|
|
4
5
|
/**
|
|
5
6
|
* Represents an active SQL transaction.
|
|
6
7
|
* All queries are executed on a single dedicated connection.
|
|
@@ -24,7 +25,14 @@ export declare class Transaction {
|
|
|
24
25
|
/**
|
|
25
26
|
* Executes a query within the current transaction.
|
|
26
27
|
*/
|
|
27
|
-
query<T extends
|
|
28
|
+
query<T extends Row>(strings: TemplateStringsArray, ...values: any[]): Future<T[], PostgresError>;
|
|
29
|
+
/**
|
|
30
|
+
* Like {@link query}, but for statements that don't return rows (INSERT/UPDATE/DDL/etc).
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* await tx.execute`UPDATE users SET name = ${name} WHERE id = ${id}`
|
|
34
|
+
*/
|
|
35
|
+
execute(strings: TemplateStringsArray, ...params: any[]): Future<void, PostgresError>;
|
|
28
36
|
/**
|
|
29
37
|
* Creates a sub-transaction using PostgreSQL SAVEPOINT.
|
|
30
38
|
* If the callback throws, only the actions within this savepoint are rolled back.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,eAAe,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAwB,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,eAAe,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAwB,aAAa,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAE7B;;;GAGG;AACH,qBAAa,WAAW;IAIhB,QAAQ,CAAC,IAAI,EAAE,UAAU;IAH7B,OAAO,CAAC,UAAU,CAAiB;gBAGtB,IAAI,EAAE,UAAU;IAG7B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;OAEG;IACI,MAAM;IAQb;;OAEG;IACI,QAAQ;IAQf;;OAEG;IACI,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAM3E;;;;;OAKG;IACI,OAAO,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAM9D;;;;;;;;;OASG;IACI,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;CAW9E"}
|
package/dist/transaction.js
CHANGED
|
@@ -44,6 +44,17 @@ export class Transaction {
|
|
|
44
44
|
return Future.reject(ErrTransactionClosed);
|
|
45
45
|
return this.conn.query(strings, ...values);
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Like {@link query}, but for statements that don't return rows (INSERT/UPDATE/DDL/etc).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* await tx.execute`UPDATE users SET name = ${name} WHERE id = ${id}`
|
|
52
|
+
*/
|
|
53
|
+
execute(strings, ...params) {
|
|
54
|
+
if (this.isFinished)
|
|
55
|
+
return Future.reject(ErrTransactionClosed);
|
|
56
|
+
return this.conn.execute(strings, ...params);
|
|
57
|
+
}
|
|
47
58
|
/**
|
|
48
59
|
* Creates a sub-transaction using PostgreSQL SAVEPOINT.
|
|
49
60
|
* If the callback throws, only the actions within this savepoint are rolled back.
|