@oliphaunt/wasix-ts 0.1.0 → 0.2.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/ARCHITECTURE.md +55 -32
- package/CHANGELOG.md +38 -0
- package/README.md +19 -387
- package/THIRD_PARTY_NOTICES.md +5 -5
- package/lib/browser.d.ts +2 -0
- package/lib/browser.js +6 -0
- package/lib/{database.d.ts → core/database.d.ts} +4 -4
- package/lib/{database.js → core/database.js} +1 -1
- package/lib/core/open-config.d.ts +5 -0
- package/lib/core/open-config.js +28 -0
- package/lib/{public.d.ts → core/public.d.ts} +3 -3
- package/lib/{public.js → core/public.js} +2 -2
- package/lib/{types.d.ts → core/types.d.ts} +19 -43
- package/lib/direct.node.d.ts +2 -2
- package/lib/direct.node.js +2 -2
- package/lib/host/index.d.mts +1 -1
- package/lib/host/index.mjs +1 -1
- package/lib/host/provenance.json +1 -1
- package/lib/host/wasmer_js_bg.wasm +0 -0
- package/lib/hosts/browser/browser-public.d.ts +8 -0
- package/lib/hosts/browser/browser-public.js +1 -0
- package/lib/{client-common.d.ts → hosts/browser/client-common.d.ts} +3 -3
- package/lib/hosts/browser/client-common.js +17 -0
- package/lib/{client.d.ts → hosts/browser/client.d.ts} +1 -1
- package/lib/{client.js → hosts/browser/client.js} +5 -3
- package/lib/{direct-client-common.d.ts → hosts/browser/direct-client-common.d.ts} +9 -8
- package/lib/{direct-client-common.js → hosts/browser/direct-client-common.js} +17 -31
- package/lib/hosts/browser/initialize.d.ts +5 -0
- package/lib/hosts/browser/initialize.js +67 -0
- package/lib/{internal-common.d.ts → hosts/browser/internal-common.d.ts} +3 -3
- package/lib/{internal-common.js → hosts/browser/internal-common.js} +3 -3
- package/lib/{tool-worker-common.d.ts → hosts/browser/tool-worker-common.d.ts} +3 -3
- package/lib/{tool-worker-common.js → hosts/browser/tool-worker-common.js} +2 -2
- package/lib/{tool-worker.js → hosts/browser/tool-worker.js} +1 -1
- package/lib/{wasix-runtime.d.ts → hosts/browser/wasix-runtime.d.ts} +3 -3
- package/lib/{wasix-runtime.js → hosts/browser/wasix-runtime.js} +41 -25
- package/lib/{worker-client.d.ts → hosts/browser/worker-client.d.ts} +1 -1
- package/lib/{worker-client.js → hosts/browser/worker-client.js} +5 -4
- package/lib/{worker.js → hosts/browser/worker.js} +2 -2
- package/lib/{direct-client.d.ts → hosts/node-api/direct-client.d.ts} +1 -1
- package/lib/{direct-client.js → hosts/node-api/direct-client.js} +1 -1
- package/lib/{native-addon.d.ts → hosts/node-api/native-addon.d.ts} +28 -6
- package/lib/{native-addon.js → hosts/node-api/native-addon.js} +4 -5
- package/lib/hosts/node-api/native-public.d.ts +8 -0
- package/lib/hosts/node-api/native-public.js +1 -0
- package/lib/{native-server.d.ts → hosts/node-api/native-server.d.ts} +1 -1
- package/lib/{native-server.js → hosts/node-api/native-server.js} +3 -2
- package/lib/{native-session.d.ts → hosts/node-api/native-session.d.ts} +14 -4
- package/lib/{native-session.js → hosts/node-api/native-session.js} +93 -108
- package/lib/{node-actor.d.ts → hosts/node-api/node-actor.d.ts} +2 -2
- package/lib/{node-actor.js → hosts/node-api/node-actor.js} +1 -1
- package/lib/{node-client-common.d.ts → hosts/node-api/node-client-common.d.ts} +3 -3
- package/lib/{node-client-common.js → hosts/node-api/node-client-common.js} +4 -4
- package/lib/{node-client.d.ts → hosts/node-api/node-client.d.ts} +1 -1
- package/lib/{node-client.js → hosts/node-api/node-client.js} +1 -1
- package/lib/{node-direct.d.ts → hosts/node-api/node-direct.d.ts} +2 -2
- package/lib/{node-direct.js → hosts/node-api/node-direct.js} +1 -1
- package/lib/{node-worker-port.d.ts → hosts/node-api/node-worker-port.d.ts} +1 -1
- package/lib/{node-worker.js → hosts/node-api/node-worker.js} +1 -1
- package/lib/{worker-node-client.d.ts → hosts/node-api/worker-node-client.d.ts} +1 -1
- package/lib/{worker-node-client.js → hosts/node-api/worker-node-client.js} +5 -18
- package/lib/index.bun.d.ts +2 -2
- package/lib/index.bun.js +2 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.deno.d.ts +2 -2
- package/lib/index.deno.js +2 -2
- package/lib/index.js +2 -2
- package/lib/index.node.d.ts +2 -2
- package/lib/index.node.js +2 -2
- package/lib/internal.d.ts +4 -4
- package/lib/internal.js +24 -4
- package/lib/internal.node.d.ts +4 -4
- package/lib/internal.node.js +2 -2
- package/lib/native-only.d.ts +1 -0
- package/lib/native-only.js +2 -0
- package/lib/protocol/protocol.d.ts +1 -0
- package/lib/protocol/protocol.js +1 -0
- package/lib/protocol/query.d.ts +1 -0
- package/lib/protocol/query.js +1 -0
- package/lib/{archive.d.ts → resources/archive.d.ts} +1 -1
- package/lib/{descriptor-validation.d.ts → resources/descriptor-validation.d.ts} +2 -2
- package/lib/{descriptor-validation.js → resources/descriptor-validation.js} +1 -1
- package/lib/{extension-descriptor.d.ts → resources/extension-descriptor.d.ts} +2 -2
- package/lib/{extensions.d.ts → resources/extensions.d.ts} +13 -6
- package/lib/{extensions.js → resources/extensions.js} +80 -155
- package/lib/resources/icu-descriptor.d.ts +3 -0
- package/lib/resources/icu-descriptor.js +16 -0
- package/lib/{runtime-descriptor.d.ts → resources/runtime-descriptor.d.ts} +1 -1
- package/lib/{runtime-descriptor.js → resources/runtime-descriptor.js} +1 -24
- package/lib/{tool-runtime.d.ts → resources/tool-runtime.d.ts} +8 -2
- package/lib/server.node.d.ts +1 -1
- package/lib/server.node.js +1 -1
- package/lib/{database-root.d.ts → storage/database-root.d.ts} +8 -0
- package/lib/{database-root.js → storage/database-root.js} +6 -0
- package/lib/storage/incremental-storage.d.ts +3 -3
- package/lib/storage/incremental-storage.js +2 -2
- package/lib/storage/indexed-db-provider.d.ts +2 -2
- package/lib/storage/indexed-db-provider.js +3 -3
- package/lib/storage/indexed-db.d.ts +2 -2
- package/lib/storage/indexed-db.js +1 -1
- package/lib/storage/node.d.ts +2 -2
- package/lib/storage/node.js +1 -1
- package/lib/storage/opfs-pool.d.ts +2 -2
- package/lib/storage/opfs-pool.js +4 -4
- package/lib/storage/opfs-provider.d.ts +2 -2
- package/lib/storage/opfs-provider.js +1 -1
- package/lib/storage/opfs.d.ts +2 -2
- package/lib/storage/opfs.js +1 -1
- package/lib/{physical-archive.js → storage/physical-archive.js} +4 -4
- package/lib/storage/restore-cleanup.d.ts +1 -1
- package/lib/storage/restore-cleanup.js +1 -1
- package/lib/{storage-provider.d.ts → storage/storage-provider.d.ts} +5 -11
- package/lib/{storage-provider.js → storage/storage-provider.js} +8 -13
- package/lib/{storage-snapshot.d.ts → storage/storage-snapshot.d.ts} +2 -2
- package/lib/{storage.d.ts → storage/types.d.ts} +8 -7
- package/lib/storage/web-lock.js +1 -1
- package/lib/worker-entry.bun.d.ts +2 -2
- package/lib/worker-entry.bun.js +2 -2
- package/lib/worker-entry.d.ts +2 -2
- package/lib/worker-entry.deno.d.ts +2 -2
- package/lib/worker-entry.deno.js +2 -2
- package/lib/worker-entry.js +2 -2
- package/lib/worker-entry.node.d.ts +2 -2
- package/lib/worker-entry.node.js +2 -2
- package/lib/{rpc.d.ts → workers/rpc.d.ts} +14 -34
- package/lib/{rpc.js → workers/rpc.js} +4 -4
- package/lib/{worker-dispatch.d.ts → workers/worker-dispatch.d.ts} +1 -1
- package/lib/{worker-dispatch.js → workers/worker-dispatch.js} +2 -2
- package/lib/{worker-rpc.d.ts → workers/worker-rpc.d.ts} +3 -3
- package/lib/{worker-rpc.js → workers/worker-rpc.js} +8 -12
- package/package.json +66 -30
- package/lib/client-common.js +0 -36
- package/lib/icu-descriptor.d.ts +0 -3
- package/lib/icu-descriptor.js +0 -92
- package/lib/protocol.d.ts +0 -1
- package/lib/protocol.js +0 -1
- package/lib/query.d.ts +0 -1
- package/lib/query.js +0 -1
- package/node_modules/@oliphaunt/js-core/README.md +0 -7
- package/node_modules/@oliphaunt/js-core/dist/commonjs/protocol.d.ts +0 -1
- package/node_modules/@oliphaunt/js-core/dist/commonjs/protocol.js +0 -22
- package/node_modules/@oliphaunt/js-core/dist/commonjs/query.d.ts +0 -255
- package/node_modules/@oliphaunt/js-core/dist/commonjs/query.js +0 -2068
- package/node_modules/@oliphaunt/js-core/dist/module/package.json +0 -3
- package/node_modules/@oliphaunt/js-core/dist/module/protocol.d.ts +0 -1
- package/node_modules/@oliphaunt/js-core/dist/module/protocol.js +0 -19
- package/node_modules/@oliphaunt/js-core/dist/module/query.d.ts +0 -255
- package/node_modules/@oliphaunt/js-core/dist/module/query.js +0 -2039
- package/node_modules/@oliphaunt/js-core/package.json +0 -21
- /package/lib/{errors.d.ts → core/errors.d.ts} +0 -0
- /package/lib/{errors.js → core/errors.js} +0 -0
- /package/lib/{startup-config.d.ts → core/startup-config.d.ts} +0 -0
- /package/lib/{startup-config.js → core/startup-config.js} +0 -0
- /package/lib/{types.js → core/types.js} +0 -0
- /package/lib/{tool-worker.d.ts → hosts/browser/tool-worker.d.ts} +0 -0
- /package/lib/{worker.d.ts → hosts/browser/worker.d.ts} +0 -0
- /package/lib/{host-runtime.d.ts → hosts/node-api/host-runtime.d.ts} +0 -0
- /package/lib/{host-runtime.js → hosts/node-api/host-runtime.js} +0 -0
- /package/lib/{node-worker-options.d.ts → hosts/node-api/node-worker-options.d.ts} +0 -0
- /package/lib/{node-worker-options.js → hosts/node-api/node-worker-options.js} +0 -0
- /package/lib/{node-worker-port.js → hosts/node-api/node-worker-port.js} +0 -0
- /package/lib/{node-worker.d.ts → hosts/node-api/node-worker.d.ts} +0 -0
- /package/lib/{byte-channel.d.ts → protocol/byte-channel.d.ts} +0 -0
- /package/lib/{byte-channel.js → protocol/byte-channel.js} +0 -0
- /package/lib/{pgwire-connection.d.ts → protocol/pgwire-connection.d.ts} +0 -0
- /package/lib/{pgwire-connection.js → protocol/pgwire-connection.js} +0 -0
- /package/lib/{pgwire.d.ts → protocol/pgwire.d.ts} +0 -0
- /package/lib/{pgwire.js → protocol/pgwire.js} +0 -0
- /package/lib/{archive.js → resources/archive.js} +0 -0
- /package/lib/{asset-source.d.ts → resources/asset-source.d.ts} +0 -0
- /package/lib/{asset-source.js → resources/asset-source.js} +0 -0
- /package/lib/{extension-descriptor.js → resources/extension-descriptor.js} +0 -0
- /package/lib/{tool-runtime.js → resources/tool-runtime.js} +0 -0
- /package/lib/{zstd.d.ts → resources/zstd.d.ts} +0 -0
- /package/lib/{zstd.js → resources/zstd.js} +0 -0
- /package/lib/{physical-archive.d.ts → storage/physical-archive.d.ts} +0 -0
- /package/lib/{storage-snapshot.js → storage/storage-snapshot.js} +0 -0
- /package/lib/{storage.js → storage/types.js} +0 -0
- /package/lib/{worker-transfer.d.ts → workers/worker-transfer.d.ts} +0 -0
- /package/lib/{worker-transfer.js → workers/worker-transfer.js} +0 -0
|
@@ -1,2068 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PostgresError = exports.simpleQuery = exports.postgresOids = void 0;
|
|
4
|
-
exports.errorWithNotices = errorWithNotices;
|
|
5
|
-
exports.text = text;
|
|
6
|
-
exports.binary = binary;
|
|
7
|
-
exports.typedNull = typedNull;
|
|
8
|
-
exports.json = json;
|
|
9
|
-
exports.array = array;
|
|
10
|
-
exports.planQuery = planQuery;
|
|
11
|
-
exports.extendedQuery = extendedQuery;
|
|
12
|
-
exports.describeQuery = describeQuery;
|
|
13
|
-
exports.responseTransactionStatus = responseTransactionStatus;
|
|
14
|
-
exports.inspectReadyForQuery = inspectReadyForQuery;
|
|
15
|
-
exports.inspectManagedTransactionResponse = inspectManagedTransactionResponse;
|
|
16
|
-
exports.parseQueryRawResponse = parseQueryRawResponse;
|
|
17
|
-
exports.parseSimpleQueryRawResponse = parseSimpleQueryRawResponse;
|
|
18
|
-
exports.decodeQueryResult = decodeQueryResult;
|
|
19
|
-
exports.parseExecResponse = parseExecResponse;
|
|
20
|
-
exports.parseCommandResponse = parseCommandResponse;
|
|
21
|
-
exports.parseDescribeResponse = parseDescribeResponse;
|
|
22
|
-
exports.assertSuccessfulQueryResponse = assertSuccessfulQueryResponse;
|
|
23
|
-
exports.assertNoTopLevelCopy = assertNoTopLevelCopy;
|
|
24
|
-
exports.assertNoTransactionChain = assertNoTransactionChain;
|
|
25
|
-
exports.structuredSimpleQuery = structuredSimpleQuery;
|
|
26
|
-
exports.containsTopLevelCopy = containsTopLevelCopy;
|
|
27
|
-
exports.containsTransactionChain = containsTransactionChain;
|
|
28
|
-
exports.toUint8Array = toUint8Array;
|
|
29
|
-
const protocol_js_1 = require("./protocol.js");
|
|
30
|
-
Object.defineProperty(exports, "simpleQuery", { enumerable: true, get: function () { return protocol_js_1.simpleQuery; } });
|
|
31
|
-
const utf8Encoder = new TextEncoder();
|
|
32
|
-
const utf8Decoder = new TextDecoder('utf-8', { fatal: true });
|
|
33
|
-
/** Stable PostgreSQL OIDs used by the built-in JavaScript codecs. */
|
|
34
|
-
exports.postgresOids = Object.freeze({
|
|
35
|
-
bool: 16,
|
|
36
|
-
bytea: 17,
|
|
37
|
-
char: 18,
|
|
38
|
-
name: 19,
|
|
39
|
-
int8: 20,
|
|
40
|
-
int2: 21,
|
|
41
|
-
int4: 23,
|
|
42
|
-
text: 25,
|
|
43
|
-
oid: 26,
|
|
44
|
-
json: 114,
|
|
45
|
-
xml: 142,
|
|
46
|
-
float4: 700,
|
|
47
|
-
float8: 701,
|
|
48
|
-
unknown: 705,
|
|
49
|
-
bpchar: 1042,
|
|
50
|
-
varchar: 1043,
|
|
51
|
-
date: 1082,
|
|
52
|
-
time: 1083,
|
|
53
|
-
timestamp: 1114,
|
|
54
|
-
timestamptz: 1184,
|
|
55
|
-
interval: 1186,
|
|
56
|
-
timetz: 1266,
|
|
57
|
-
numeric: 1700,
|
|
58
|
-
uuid: 2950,
|
|
59
|
-
jsonb: 3802,
|
|
60
|
-
boolArray: 1000,
|
|
61
|
-
byteaArray: 1001,
|
|
62
|
-
charArray: 1002,
|
|
63
|
-
nameArray: 1003,
|
|
64
|
-
int2Array: 1005,
|
|
65
|
-
int4Array: 1007,
|
|
66
|
-
textArray: 1009,
|
|
67
|
-
bpcharArray: 1014,
|
|
68
|
-
varcharArray: 1015,
|
|
69
|
-
int8Array: 1016,
|
|
70
|
-
float4Array: 1021,
|
|
71
|
-
float8Array: 1022,
|
|
72
|
-
oidArray: 1028,
|
|
73
|
-
dateArray: 1182,
|
|
74
|
-
timeArray: 1183,
|
|
75
|
-
timestampArray: 1115,
|
|
76
|
-
timestamptzArray: 1185,
|
|
77
|
-
intervalArray: 1187,
|
|
78
|
-
numericArray: 1231,
|
|
79
|
-
timetzArray: 1270,
|
|
80
|
-
jsonArray: 199,
|
|
81
|
-
xmlArray: 143,
|
|
82
|
-
uuidArray: 2951,
|
|
83
|
-
jsonbArray: 3807,
|
|
84
|
-
});
|
|
85
|
-
class ParsedRawQueryRow {
|
|
86
|
-
values;
|
|
87
|
-
constructor(values) {
|
|
88
|
-
this.values = values;
|
|
89
|
-
}
|
|
90
|
-
text(column) {
|
|
91
|
-
if (column < 0 || column >= this.values.length) {
|
|
92
|
-
throw new Error(`query row has no column at index ${column}`);
|
|
93
|
-
}
|
|
94
|
-
const value = this.values[column];
|
|
95
|
-
return value === null ? null : decodeUtf8Strict(value, 'query value');
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
class PostgresError extends Error {
|
|
99
|
-
severity;
|
|
100
|
-
localizedSeverity;
|
|
101
|
-
nonlocalizedSeverity;
|
|
102
|
-
sqlstate;
|
|
103
|
-
detail;
|
|
104
|
-
hint;
|
|
105
|
-
position;
|
|
106
|
-
internalPosition;
|
|
107
|
-
internalQuery;
|
|
108
|
-
whereText;
|
|
109
|
-
schemaName;
|
|
110
|
-
tableName;
|
|
111
|
-
columnName;
|
|
112
|
-
dataTypeName;
|
|
113
|
-
constraintName;
|
|
114
|
-
file;
|
|
115
|
-
line;
|
|
116
|
-
routine;
|
|
117
|
-
fields;
|
|
118
|
-
notices;
|
|
119
|
-
constructor(fields, notices = []) {
|
|
120
|
-
const severity = fieldValue(fields, 0x53) ?? fieldValue(fields, 0x56);
|
|
121
|
-
const sqlstate = fieldValue(fields, 0x43);
|
|
122
|
-
super(fieldValue(fields, 0x4d) ?? 'PostgreSQL ErrorResponse');
|
|
123
|
-
this.name = 'PostgresError';
|
|
124
|
-
this.severity = severity;
|
|
125
|
-
this.localizedSeverity = fieldValue(fields, 0x53);
|
|
126
|
-
this.nonlocalizedSeverity = fieldValue(fields, 0x56);
|
|
127
|
-
this.sqlstate = sqlstate;
|
|
128
|
-
this.detail = fieldValue(fields, 0x44);
|
|
129
|
-
this.hint = fieldValue(fields, 0x48);
|
|
130
|
-
this.position = fieldValue(fields, 0x50);
|
|
131
|
-
this.internalPosition = fieldValue(fields, 0x70);
|
|
132
|
-
this.internalQuery = fieldValue(fields, 0x71);
|
|
133
|
-
this.whereText = fieldValue(fields, 0x57);
|
|
134
|
-
this.schemaName = fieldValue(fields, 0x73);
|
|
135
|
-
this.tableName = fieldValue(fields, 0x74);
|
|
136
|
-
this.columnName = fieldValue(fields, 0x63);
|
|
137
|
-
this.dataTypeName = fieldValue(fields, 0x64);
|
|
138
|
-
this.constraintName = fieldValue(fields, 0x6e);
|
|
139
|
-
this.file = fieldValue(fields, 0x46);
|
|
140
|
-
this.line = fieldValue(fields, 0x4c);
|
|
141
|
-
this.routine = fieldValue(fields, 0x52);
|
|
142
|
-
this.fields = fields;
|
|
143
|
-
this.notices = notices;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
exports.PostgresError = PostgresError;
|
|
147
|
-
/** @internal Preserve query-scoped notices on caller codec failures. */
|
|
148
|
-
function errorWithNotices(error, notices) {
|
|
149
|
-
if (notices.length === 0 && isObjectLike(error))
|
|
150
|
-
return error;
|
|
151
|
-
if (isObjectLike(error) && tryAttachNotices(error, notices))
|
|
152
|
-
return error;
|
|
153
|
-
const wrapped = withCause(new Error(codecFailureMessage(error)), error);
|
|
154
|
-
if (notices.length > 0) {
|
|
155
|
-
Object.defineProperty(wrapped, 'notices', {
|
|
156
|
-
value: [...notices],
|
|
157
|
-
configurable: true,
|
|
158
|
-
enumerable: true,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
return wrapped;
|
|
162
|
-
}
|
|
163
|
-
function tryAttachNotices(error, notices) {
|
|
164
|
-
try {
|
|
165
|
-
const existing = error.notices;
|
|
166
|
-
if (Array.isArray(existing)) {
|
|
167
|
-
try {
|
|
168
|
-
existing.unshift(...notices);
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
// Fall through to replacing a configurable property.
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (!Object.isExtensible(error))
|
|
176
|
-
return false;
|
|
177
|
-
Object.defineProperty(error, 'notices', {
|
|
178
|
-
value: [...notices],
|
|
179
|
-
configurable: true,
|
|
180
|
-
enumerable: true,
|
|
181
|
-
});
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
function codecFailureMessage(error) {
|
|
189
|
-
if (error instanceof Error && error.message.length > 0)
|
|
190
|
-
return error.message;
|
|
191
|
-
if (typeof error === 'string' && error.length > 0)
|
|
192
|
-
return error;
|
|
193
|
-
if (isObjectLike(error)) {
|
|
194
|
-
try {
|
|
195
|
-
const message = error.message;
|
|
196
|
-
if (typeof message === 'string' && message.length > 0)
|
|
197
|
-
return message;
|
|
198
|
-
}
|
|
199
|
-
catch {
|
|
200
|
-
// Use the stable fallback for hostile thrown objects.
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return 'query codec failed';
|
|
204
|
-
}
|
|
205
|
-
function isObjectLike(value) {
|
|
206
|
-
return (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
207
|
-
}
|
|
208
|
-
const parameterMetadata = new WeakMap();
|
|
209
|
-
function text(value, typeOid) {
|
|
210
|
-
const encoded = scalarText(value);
|
|
211
|
-
return parameterWrapper({
|
|
212
|
-
format: 'text',
|
|
213
|
-
value: encoded,
|
|
214
|
-
typeOid,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
function binary(value, typeOid) {
|
|
218
|
-
if (!isQueryBinaryInput(value)) {
|
|
219
|
-
throw new TypeError('binary() requires an ArrayBuffer or ArrayBuffer view');
|
|
220
|
-
}
|
|
221
|
-
return parameterWrapper({
|
|
222
|
-
format: 'binary',
|
|
223
|
-
value,
|
|
224
|
-
typeOid,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
function typedNull(typeOid) {
|
|
228
|
-
validateTypeOid(typeOid, false);
|
|
229
|
-
return parameterWrapper({ format: 'null', typeOid });
|
|
230
|
-
}
|
|
231
|
-
function json(value, typeOid = exports.postgresOids.jsonb) {
|
|
232
|
-
validateTypeOid(typeOid, false);
|
|
233
|
-
let encoded;
|
|
234
|
-
try {
|
|
235
|
-
encoded = JSON.stringify(value);
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
throw withCause(new TypeError('json() value must be acyclic and JSON-serializable'), error);
|
|
239
|
-
}
|
|
240
|
-
if (encoded === undefined) {
|
|
241
|
-
throw new TypeError('json() value must be JSON-serializable');
|
|
242
|
-
}
|
|
243
|
-
return parameterWrapper({
|
|
244
|
-
format: 'text',
|
|
245
|
-
value: encoded,
|
|
246
|
-
typeOid,
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
function array(values, typeOid) {
|
|
250
|
-
if (!Array.isArray(values)) {
|
|
251
|
-
throw new TypeError('array() requires a JavaScript array');
|
|
252
|
-
}
|
|
253
|
-
const elementTypeOid = typeOid === undefined ? undefined : arrayElementTypeOid(typeOid);
|
|
254
|
-
if (typeOid !== undefined && elementTypeOid === undefined) {
|
|
255
|
-
throw new TypeError('array() type OID ' + typeOid + ' is not a supported PostgreSQL array OID');
|
|
256
|
-
}
|
|
257
|
-
return parameterWrapper({
|
|
258
|
-
format: 'text',
|
|
259
|
-
value: encodeArrayLiteral(values, elementTypeOid),
|
|
260
|
-
typeOid,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
function planQuery(sql, parameters = [], options = {}) {
|
|
264
|
-
validateStatementInput(sql, parameters);
|
|
265
|
-
assertNoTopLevelCopy(sql);
|
|
266
|
-
const snapshot = Array.from(parameters, snapshotQueryParam);
|
|
267
|
-
const encoders = options.encoders === undefined ? undefined : Object.freeze({ ...options.encoders });
|
|
268
|
-
const declaredTypeOids = snapshot.map(parameterDeclaredTypeOid);
|
|
269
|
-
if (declaredTypeOids.every((typeOid) => typeOid !== 0)) {
|
|
270
|
-
const normalized = snapshot.map((parameter, index) => normalizeQueryParam(parameter, declaredTypeOids[index], encoders));
|
|
271
|
-
return {
|
|
272
|
-
kind: 'complete',
|
|
273
|
-
input: encodeParseBindExecute(sql, declaredTypeOids, normalized),
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
kind: 'describe',
|
|
278
|
-
input: describeQuery(sql, declaredTypeOids),
|
|
279
|
-
bind(parameterTypeOids) {
|
|
280
|
-
if (parameterTypeOids.length !== snapshot.length) {
|
|
281
|
-
throw new Error('PostgreSQL described ' +
|
|
282
|
-
parameterTypeOids.length +
|
|
283
|
-
' parameters, expected ' +
|
|
284
|
-
snapshot.length);
|
|
285
|
-
}
|
|
286
|
-
const normalized = snapshot.map((parameter, index) => normalizeQueryParam(parameter, parameterTypeOids[index], encoders));
|
|
287
|
-
return encodeParseBindExecute(sql, parameterTypeOids, normalized);
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
/** Encode a one-exchange query. Untyped values require planQuery() instead. */
|
|
292
|
-
function extendedQuery(sql, parameters, options = {}) {
|
|
293
|
-
const plan = planQuery(sql, parameters, options);
|
|
294
|
-
if (plan.kind === 'describe') {
|
|
295
|
-
throw new Error('extended query parameters require PostgreSQL type inference; use planQuery()');
|
|
296
|
-
}
|
|
297
|
-
return plan.input;
|
|
298
|
-
}
|
|
299
|
-
function describeQuery(sql, parameterTypeOids = []) {
|
|
300
|
-
validateStatementInput(sql, parameterTypeOids);
|
|
301
|
-
for (const typeOid of parameterTypeOids)
|
|
302
|
-
validateTypeOid(typeOid, true);
|
|
303
|
-
const sqlBytes = utf8Encoder.encode(sql);
|
|
304
|
-
const parseBodyLength = sqlBytes.length + 4 + parameterTypeOids.length * 4;
|
|
305
|
-
const packet = new ByteWriter(parseBodyLength + 17);
|
|
306
|
-
writeParse(packet, sqlBytes, parameterTypeOids);
|
|
307
|
-
packet.message(0x44, 2);
|
|
308
|
-
packet.u8(0x53);
|
|
309
|
-
packet.u8(0);
|
|
310
|
-
packet.message(0x53, 0);
|
|
311
|
-
return packet.finish();
|
|
312
|
-
}
|
|
313
|
-
function encodeParseBindExecute(sql, parameterTypeOids, parameters) {
|
|
314
|
-
const sqlBytes = utf8Encoder.encode(sql);
|
|
315
|
-
const parseBodyLength = sqlBytes.length + 4 + parameterTypeOids.length * 4;
|
|
316
|
-
const bindBodyLength = bindLength(parameters);
|
|
317
|
-
const packet = new ByteWriter(parseBodyLength + bindBodyLength + 32);
|
|
318
|
-
writeParse(packet, sqlBytes, parameterTypeOids);
|
|
319
|
-
writeBindExecute(packet, parameters);
|
|
320
|
-
return packet.finish();
|
|
321
|
-
}
|
|
322
|
-
function writeParse(packet, sqlBytes, parameterTypeOids) {
|
|
323
|
-
const parseBodyLength = sqlBytes.length + 4 + parameterTypeOids.length * 4;
|
|
324
|
-
packet.message(0x50, parseBodyLength);
|
|
325
|
-
packet.u8(0);
|
|
326
|
-
packet.bytes(sqlBytes);
|
|
327
|
-
packet.u8(0);
|
|
328
|
-
packet.i16(parameterTypeOids.length);
|
|
329
|
-
for (const typeOid of parameterTypeOids)
|
|
330
|
-
packet.i32(typeOid);
|
|
331
|
-
}
|
|
332
|
-
function writeBindExecute(packet, parameters) {
|
|
333
|
-
packet.message(0x42, bindLength(parameters));
|
|
334
|
-
packet.u8(0);
|
|
335
|
-
packet.u8(0);
|
|
336
|
-
packet.i16(parameters.length);
|
|
337
|
-
for (const parameter of parameters)
|
|
338
|
-
packet.i16(parameter.kind === 'binary' ? 1 : 0);
|
|
339
|
-
packet.i16(parameters.length);
|
|
340
|
-
for (const parameter of parameters) {
|
|
341
|
-
if (parameter.kind === 'null') {
|
|
342
|
-
packet.i32(-1);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
packet.i32(parameter.value.length);
|
|
346
|
-
packet.bytes(parameter.value);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
packet.i16(1);
|
|
350
|
-
packet.i16(0);
|
|
351
|
-
packet.message(0x44, 2);
|
|
352
|
-
packet.u8(0x50);
|
|
353
|
-
packet.u8(0);
|
|
354
|
-
packet.message(0x45, 5);
|
|
355
|
-
packet.u8(0);
|
|
356
|
-
packet.i32(0);
|
|
357
|
-
packet.message(0x53, 0);
|
|
358
|
-
}
|
|
359
|
-
function bindLength(parameters) {
|
|
360
|
-
let length = 10 + parameters.length * 2;
|
|
361
|
-
for (const parameter of parameters) {
|
|
362
|
-
length += 4 + (parameter.kind === 'null' ? 0 : parameter.value.length);
|
|
363
|
-
}
|
|
364
|
-
return length;
|
|
365
|
-
}
|
|
366
|
-
const transactionStatuses = new WeakMap();
|
|
367
|
-
function responseTransactionStatus(value) {
|
|
368
|
-
return transactionStatuses.get(value);
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* Validate the complete backend framing and return the one terminal
|
|
372
|
-
* ReadyForQuery status without interpreting result values.
|
|
373
|
-
*
|
|
374
|
-
* Structured clients call this immediately after transport completion so a
|
|
375
|
-
* custom decoder or higher-level result assertion cannot obscure whether the
|
|
376
|
-
* physical PostgreSQL session reached a reusable boundary.
|
|
377
|
-
*/
|
|
378
|
-
function inspectReadyForQuery(bytes) {
|
|
379
|
-
return inspectResponseBoundary(bytes, false).status;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Validate a structured callback-transaction response before high-level
|
|
383
|
-
* parsing can discard earlier command tags after a later ErrorResponse.
|
|
384
|
-
*/
|
|
385
|
-
function inspectManagedTransactionResponse(bytes) {
|
|
386
|
-
const boundary = inspectResponseBoundary(bytes, true);
|
|
387
|
-
if (boundary.status === 'idle') {
|
|
388
|
-
throw new Error('structured callback transaction operation ended PostgreSQL transaction ownership; close the database');
|
|
389
|
-
}
|
|
390
|
-
for (const rawTag of boundary.commandTags) {
|
|
391
|
-
const tag = rawTag;
|
|
392
|
-
if (tag === 'BEGIN' ||
|
|
393
|
-
tag === 'START TRANSACTION' ||
|
|
394
|
-
tag === 'COMMIT' ||
|
|
395
|
-
tag === 'PREPARE TRANSACTION' ||
|
|
396
|
-
tag === 'COMMIT PREPARED' ||
|
|
397
|
-
tag === 'ROLLBACK PREPARED') {
|
|
398
|
-
throw new Error(`PostgreSQL command tag ${tag} violated callback transaction ownership; close the database`);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return boundary.status;
|
|
402
|
-
}
|
|
403
|
-
function inspectResponseBoundary(bytes, collectCommandTags) {
|
|
404
|
-
const cursor = new ByteCursor(bytes);
|
|
405
|
-
let status;
|
|
406
|
-
const commandTags = [];
|
|
407
|
-
while (!cursor.isAtEnd()) {
|
|
408
|
-
if (status !== undefined) {
|
|
409
|
-
throw new Error('backend returned bytes after ReadyForQuery');
|
|
410
|
-
}
|
|
411
|
-
const tag = cursor.readU8('backend message tag');
|
|
412
|
-
const length = cursor.readI32('backend message length');
|
|
413
|
-
if (length < 4)
|
|
414
|
-
throw new Error('invalid backend message length ' + length);
|
|
415
|
-
const bodyBytes = cursor.readBytes(length - 4, 'backend message body');
|
|
416
|
-
if (tag === 0x43 && !collectCommandTags) {
|
|
417
|
-
validateCStringBody(bodyBytes, 'CommandComplete tag', 'CommandComplete');
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
const body = new ByteCursor(bodyBytes);
|
|
421
|
-
if (tag === 0x43) {
|
|
422
|
-
commandTags.push(body.readCString('CommandComplete tag'));
|
|
423
|
-
body.requireEnd('CommandComplete');
|
|
424
|
-
}
|
|
425
|
-
else if (tag === 0x5a) {
|
|
426
|
-
status = parseReadyForQuery(body);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
if (status === undefined) {
|
|
430
|
-
throw new Error('backend response ended before ReadyForQuery');
|
|
431
|
-
}
|
|
432
|
-
return { status, commandTags };
|
|
433
|
-
}
|
|
434
|
-
function parseQueryRawResponse(bytes) {
|
|
435
|
-
return singleRawResult(parseRawOperation(bytes, 'extended-single'));
|
|
436
|
-
}
|
|
437
|
-
function parseSimpleQueryRawResponse(bytes) {
|
|
438
|
-
return singleRawResult(parseRawOperation(bytes, 'simple-single'));
|
|
439
|
-
}
|
|
440
|
-
function singleRawResult(operation) {
|
|
441
|
-
const result = operation.statements[0] ?? rawResult('command', [], [], undefined, operation.notices);
|
|
442
|
-
if (operation.statements.length === 1)
|
|
443
|
-
result.notices = operation.notices;
|
|
444
|
-
transactionStatuses.set(result, operation.transactionStatus);
|
|
445
|
-
return result;
|
|
446
|
-
}
|
|
447
|
-
function decodeQueryResult(raw, options = {}) {
|
|
448
|
-
let stableOptions;
|
|
449
|
-
let rows;
|
|
450
|
-
try {
|
|
451
|
-
stableOptions = stabilizeQueryOptions(options);
|
|
452
|
-
rows = decodeRows(raw.rows, raw.fields, stableOptions, false);
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
const failure = errorWithNotices(error, raw.notices);
|
|
456
|
-
const status = responseTransactionStatus(raw);
|
|
457
|
-
if (status !== undefined && isObjectLike(failure)) {
|
|
458
|
-
transactionStatuses.set(failure, status);
|
|
459
|
-
}
|
|
460
|
-
throw failure;
|
|
461
|
-
}
|
|
462
|
-
const result = {
|
|
463
|
-
kind: raw.kind,
|
|
464
|
-
fields: raw.fields,
|
|
465
|
-
rows,
|
|
466
|
-
commandTag: raw.commandTag,
|
|
467
|
-
rowCount: raw.rowCount,
|
|
468
|
-
notices: raw.notices,
|
|
469
|
-
};
|
|
470
|
-
const status = responseTransactionStatus(raw);
|
|
471
|
-
if (status !== undefined)
|
|
472
|
-
transactionStatuses.set(result, status);
|
|
473
|
-
return result;
|
|
474
|
-
}
|
|
475
|
-
function parseExecResponse(bytes, options = {}) {
|
|
476
|
-
const operation = parseOperation(bytes, 'simple-exec', pendingExecResult);
|
|
477
|
-
let result;
|
|
478
|
-
try {
|
|
479
|
-
if (operation.statements.length > 0) {
|
|
480
|
-
const stableOptions = stabilizeQueryOptions(options);
|
|
481
|
-
for (const statement of operation.statements) {
|
|
482
|
-
materializePendingExecResult(statement, stableOptions);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
result = {
|
|
486
|
-
statements: operation.statements,
|
|
487
|
-
notices: operation.notices,
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
catch (error) {
|
|
491
|
-
const failure = errorWithNotices(error, operation.notices);
|
|
492
|
-
if (isObjectLike(failure)) {
|
|
493
|
-
transactionStatuses.set(failure, operation.transactionStatus);
|
|
494
|
-
}
|
|
495
|
-
throw failure;
|
|
496
|
-
}
|
|
497
|
-
transactionStatuses.set(result, operation.transactionStatus);
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
function parseCommandResponse(bytes) {
|
|
501
|
-
const raw = parseQueryRawResponse(bytes);
|
|
502
|
-
const status = responseTransactionStatus(raw);
|
|
503
|
-
if (raw.kind === 'rows') {
|
|
504
|
-
throwWithStatus(new Error('execute() received rows; use query() for row results'), status);
|
|
505
|
-
}
|
|
506
|
-
const result = {
|
|
507
|
-
commandTag: raw.commandTag,
|
|
508
|
-
rowCount: raw.rowCount,
|
|
509
|
-
notices: raw.notices,
|
|
510
|
-
};
|
|
511
|
-
if (status !== undefined)
|
|
512
|
-
transactionStatuses.set(result, status);
|
|
513
|
-
return result;
|
|
514
|
-
}
|
|
515
|
-
function parseDescribeResponse(bytes) {
|
|
516
|
-
const cursor = new ByteCursor(bytes);
|
|
517
|
-
const notices = [];
|
|
518
|
-
let parameterTypeOids;
|
|
519
|
-
let fields;
|
|
520
|
-
let sawParseComplete = false;
|
|
521
|
-
let sawNoData = false;
|
|
522
|
-
let failure;
|
|
523
|
-
let recoveryFailure;
|
|
524
|
-
let sawErrorResponse = false;
|
|
525
|
-
let status;
|
|
526
|
-
while (!cursor.isAtEnd()) {
|
|
527
|
-
const tag = cursor.readU8('backend message tag');
|
|
528
|
-
const length = cursor.readI32('backend message length');
|
|
529
|
-
if (length < 4)
|
|
530
|
-
throw new Error('invalid backend message length ' + length);
|
|
531
|
-
const body = new ByteCursor(cursor.readBytes(length - 4, 'backend message body'));
|
|
532
|
-
if (sawErrorResponse && tag !== 0x4e && tag !== 0x53 && tag !== 0x41 && tag !== 0x5a) {
|
|
533
|
-
recoveryFailure ??= withCause(new Error('describe response contained ' + hexBackendTag(tag) + ' after ErrorResponse'), failure);
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
try {
|
|
537
|
-
switch (tag) {
|
|
538
|
-
case 0x31:
|
|
539
|
-
if (sawParseComplete)
|
|
540
|
-
throw new Error('duplicate ParseComplete');
|
|
541
|
-
body.requireEnd('ParseComplete');
|
|
542
|
-
sawParseComplete = true;
|
|
543
|
-
break;
|
|
544
|
-
case 0x74:
|
|
545
|
-
if (!sawParseComplete)
|
|
546
|
-
throw new Error('ParameterDescription arrived before ParseComplete');
|
|
547
|
-
if (parameterTypeOids !== undefined)
|
|
548
|
-
throw new Error('duplicate ParameterDescription');
|
|
549
|
-
parameterTypeOids = parseParameterDescription(body);
|
|
550
|
-
body.requireEnd('ParameterDescription');
|
|
551
|
-
break;
|
|
552
|
-
case 0x54:
|
|
553
|
-
if (parameterTypeOids === undefined)
|
|
554
|
-
throw new Error('RowDescription arrived before ParameterDescription');
|
|
555
|
-
if (fields !== undefined || sawNoData)
|
|
556
|
-
throw new Error('duplicate result description');
|
|
557
|
-
fields = parseRowDescription(body);
|
|
558
|
-
body.requireEnd('RowDescription');
|
|
559
|
-
break;
|
|
560
|
-
case 0x6e:
|
|
561
|
-
if (parameterTypeOids === undefined)
|
|
562
|
-
throw new Error('NoData arrived before ParameterDescription');
|
|
563
|
-
if (fields !== undefined || sawNoData)
|
|
564
|
-
throw new Error('duplicate result description');
|
|
565
|
-
sawNoData = true;
|
|
566
|
-
body.requireEnd('NoData');
|
|
567
|
-
break;
|
|
568
|
-
case 0x45: {
|
|
569
|
-
const postgresFailure = parseErrorResponse(body, notices);
|
|
570
|
-
if (sawParseComplete &&
|
|
571
|
-
parameterTypeOids !== undefined &&
|
|
572
|
-
(fields !== undefined || sawNoData)) {
|
|
573
|
-
failure ??= withCause(new Error('ErrorResponse arrived after describe completion'), postgresFailure);
|
|
574
|
-
}
|
|
575
|
-
else {
|
|
576
|
-
failure ??= postgresFailure;
|
|
577
|
-
}
|
|
578
|
-
sawErrorResponse = true;
|
|
579
|
-
break;
|
|
580
|
-
}
|
|
581
|
-
case 0x4e:
|
|
582
|
-
notices.push(parseNoticeResponse(body));
|
|
583
|
-
break;
|
|
584
|
-
case 0x53:
|
|
585
|
-
validateParameterStatus(body);
|
|
586
|
-
break;
|
|
587
|
-
case 0x41:
|
|
588
|
-
validateNotificationResponse(body);
|
|
589
|
-
break;
|
|
590
|
-
case 0x5a:
|
|
591
|
-
status = parseReadyForQuery(body);
|
|
592
|
-
if (!cursor.isAtEnd()) {
|
|
593
|
-
failure ??= new Error('backend returned bytes after ReadyForQuery');
|
|
594
|
-
cursor.discardRemaining();
|
|
595
|
-
}
|
|
596
|
-
break;
|
|
597
|
-
default:
|
|
598
|
-
failure ??= new Error('describe() received unexpected backend message tag ' + hexBackendTag(tag));
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
failure ??= asError(error);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
if (status === undefined) {
|
|
606
|
-
if (failure !== undefined)
|
|
607
|
-
throw failure;
|
|
608
|
-
throw new Error('describe response ended before ReadyForQuery');
|
|
609
|
-
}
|
|
610
|
-
if (recoveryFailure !== undefined)
|
|
611
|
-
throwWithStatus(recoveryFailure, status);
|
|
612
|
-
if (failure !== undefined)
|
|
613
|
-
throwWithStatus(failure, status);
|
|
614
|
-
if (!sawParseComplete) {
|
|
615
|
-
throwWithStatus(new Error('describe response omitted ParseComplete'), status);
|
|
616
|
-
}
|
|
617
|
-
if (parameterTypeOids === undefined) {
|
|
618
|
-
throwWithStatus(new Error('describe response omitted ParameterDescription'), status);
|
|
619
|
-
}
|
|
620
|
-
if (fields === undefined && !sawNoData) {
|
|
621
|
-
throwWithStatus(new Error('describe response omitted RowDescription or NoData'), status);
|
|
622
|
-
}
|
|
623
|
-
const result = {
|
|
624
|
-
parameterTypeOids,
|
|
625
|
-
...(fields === undefined ? {} : { fields }),
|
|
626
|
-
notices,
|
|
627
|
-
};
|
|
628
|
-
transactionStatuses.set(result, status);
|
|
629
|
-
return result;
|
|
630
|
-
}
|
|
631
|
-
function assertSuccessfulQueryResponse(bytes) {
|
|
632
|
-
const raw = singleRawResult(parseRawOperation(bytes, 'simple-single'));
|
|
633
|
-
if (raw.kind === 'rows') {
|
|
634
|
-
throwWithStatus(new Error('command response unexpectedly contained rows'), responseTransactionStatus(raw));
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
function parseRawOperation(bytes, mode) {
|
|
638
|
-
return parseOperation(bytes, mode, rawResult);
|
|
639
|
-
}
|
|
640
|
-
function parseOperation(bytes, mode, statementFactory) {
|
|
641
|
-
const cursor = new ByteCursor(bytes);
|
|
642
|
-
const statements = [];
|
|
643
|
-
const notices = [];
|
|
644
|
-
let statementNotices = [];
|
|
645
|
-
let fields;
|
|
646
|
-
let rows = [];
|
|
647
|
-
let failure;
|
|
648
|
-
let recoveryFailure;
|
|
649
|
-
let sawErrorResponse = false;
|
|
650
|
-
let completionCount = 0;
|
|
651
|
-
let extendedStage = 'start';
|
|
652
|
-
let status;
|
|
653
|
-
while (!cursor.isAtEnd()) {
|
|
654
|
-
const tag = cursor.readU8('backend message tag');
|
|
655
|
-
const length = cursor.readI32('backend message length');
|
|
656
|
-
if (length < 4)
|
|
657
|
-
throw new Error('invalid backend message length ' + length);
|
|
658
|
-
const body = new ByteCursor(cursor.readBytes(length - 4, 'backend message body'));
|
|
659
|
-
if (sawErrorResponse && tag !== 0x4e && tag !== 0x53 && tag !== 0x41 && tag !== 0x5a) {
|
|
660
|
-
recoveryFailure ??= withCause(new Error('query response contained ' + hexBackendTag(tag) + ' after ErrorResponse'), failure);
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
|
-
try {
|
|
664
|
-
switch (tag) {
|
|
665
|
-
case 0x54:
|
|
666
|
-
if (mode !== 'simple-exec' && completionCount > 0)
|
|
667
|
-
throw new Error('RowDescription arrived after statement completion');
|
|
668
|
-
if (mode === 'extended-single') {
|
|
669
|
-
if (extendedStage !== 'bound') {
|
|
670
|
-
throw new Error('RowDescription arrived before ParseComplete and BindComplete');
|
|
671
|
-
}
|
|
672
|
-
extendedStage = 'described';
|
|
673
|
-
}
|
|
674
|
-
if (fields !== undefined)
|
|
675
|
-
failure ??= new Error('result received two RowDescriptions');
|
|
676
|
-
fields = parseRowDescription(body);
|
|
677
|
-
body.requireEnd('RowDescription');
|
|
678
|
-
break;
|
|
679
|
-
case 0x44:
|
|
680
|
-
if (completionCount > 0 && fields === undefined)
|
|
681
|
-
throw new Error('DataRow arrived after statement completion');
|
|
682
|
-
if (fields === undefined)
|
|
683
|
-
throw new Error('DataRow arrived before RowDescription');
|
|
684
|
-
if (mode === 'extended-single' && extendedStage !== 'described') {
|
|
685
|
-
throw new Error('DataRow arrived before the result description');
|
|
686
|
-
}
|
|
687
|
-
rows.push(parseDataRow(body, fields.length));
|
|
688
|
-
body.requireEnd('DataRow');
|
|
689
|
-
break;
|
|
690
|
-
case 0x43: {
|
|
691
|
-
if (mode !== 'simple-exec' && completionCount > 0) {
|
|
692
|
-
throw new Error('queryRaw() received multiple result completions; use exec() for multi-statement SQL');
|
|
693
|
-
}
|
|
694
|
-
if (mode === 'extended-single' && extendedStage !== 'described') {
|
|
695
|
-
throw new Error('CommandComplete arrived before the extended-query result description');
|
|
696
|
-
}
|
|
697
|
-
const commandTag = body.readCString('CommandComplete tag');
|
|
698
|
-
body.requireEnd('CommandComplete');
|
|
699
|
-
statements.push(statementFactory(fields === undefined ? 'command' : 'rows', fields ?? [], rows, commandTag, statementNotices));
|
|
700
|
-
fields = undefined;
|
|
701
|
-
rows = [];
|
|
702
|
-
statementNotices = [];
|
|
703
|
-
completionCount += 1;
|
|
704
|
-
if (mode === 'extended-single') {
|
|
705
|
-
extendedStage = 'completed';
|
|
706
|
-
}
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
case 0x45: {
|
|
710
|
-
const postgresFailure = parseErrorResponse(body, notices);
|
|
711
|
-
if (mode !== 'simple-exec' && completionCount > 0) {
|
|
712
|
-
failure ??= withCause(new Error('ErrorResponse arrived after statement completion'), postgresFailure);
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
failure ??= postgresFailure;
|
|
716
|
-
}
|
|
717
|
-
sawErrorResponse = true;
|
|
718
|
-
break;
|
|
719
|
-
}
|
|
720
|
-
case 0x47:
|
|
721
|
-
case 0x48:
|
|
722
|
-
case 0x57:
|
|
723
|
-
case 0x64:
|
|
724
|
-
case 0x63:
|
|
725
|
-
failure ??= new Error('query() does not support COPY protocol responses; use a raw protocol API for COPY traffic');
|
|
726
|
-
break;
|
|
727
|
-
case 0x5a:
|
|
728
|
-
status = parseReadyForQuery(body);
|
|
729
|
-
if (!cursor.isAtEnd()) {
|
|
730
|
-
failure ??= new Error('backend returned bytes after ReadyForQuery');
|
|
731
|
-
cursor.discardRemaining();
|
|
732
|
-
}
|
|
733
|
-
break;
|
|
734
|
-
case 0x31:
|
|
735
|
-
if (mode !== 'extended-single') {
|
|
736
|
-
throw new Error('simple-query response contained ParseComplete');
|
|
737
|
-
}
|
|
738
|
-
if (extendedStage !== 'start') {
|
|
739
|
-
throw new Error('ParseComplete arrived out of order');
|
|
740
|
-
}
|
|
741
|
-
body.requireEnd('ParseComplete');
|
|
742
|
-
extendedStage = 'parsed';
|
|
743
|
-
break;
|
|
744
|
-
case 0x32:
|
|
745
|
-
if (mode !== 'extended-single') {
|
|
746
|
-
throw new Error('simple-query response contained BindComplete');
|
|
747
|
-
}
|
|
748
|
-
if (extendedStage !== 'parsed') {
|
|
749
|
-
throw new Error('BindComplete arrived before ParseComplete or out of order');
|
|
750
|
-
}
|
|
751
|
-
body.requireEnd('BindComplete');
|
|
752
|
-
extendedStage = 'bound';
|
|
753
|
-
break;
|
|
754
|
-
case 0x33:
|
|
755
|
-
throw new Error('unsolicited CloseComplete in query response');
|
|
756
|
-
case 0x49:
|
|
757
|
-
if (fields !== undefined || rows.length !== 0)
|
|
758
|
-
throw new Error('EmptyQueryResponse arrived during a row result');
|
|
759
|
-
if (mode !== 'simple-exec' && completionCount > 0) {
|
|
760
|
-
throw new Error('queryRaw() received multiple result completions; use exec() for multi-statement SQL');
|
|
761
|
-
}
|
|
762
|
-
if (mode === 'extended-single' && extendedStage !== 'described') {
|
|
763
|
-
throw new Error('EmptyQueryResponse arrived before the extended-query result description');
|
|
764
|
-
}
|
|
765
|
-
body.requireEnd('EmptyQueryResponse');
|
|
766
|
-
statementNotices = [];
|
|
767
|
-
completionCount += 1;
|
|
768
|
-
if (mode === 'extended-single') {
|
|
769
|
-
extendedStage = 'completed';
|
|
770
|
-
}
|
|
771
|
-
break;
|
|
772
|
-
case 0x6e:
|
|
773
|
-
if (mode !== 'extended-single') {
|
|
774
|
-
throw new Error('simple-query response contained NoData');
|
|
775
|
-
}
|
|
776
|
-
if (extendedStage !== 'bound') {
|
|
777
|
-
throw new Error('NoData arrived before ParseComplete and BindComplete');
|
|
778
|
-
}
|
|
779
|
-
body.requireEnd('NoData');
|
|
780
|
-
extendedStage = 'described';
|
|
781
|
-
break;
|
|
782
|
-
case 0x53:
|
|
783
|
-
validateParameterStatus(body);
|
|
784
|
-
break;
|
|
785
|
-
case 0x4e: {
|
|
786
|
-
const notice = parseNoticeResponse(body);
|
|
787
|
-
notices.push(notice);
|
|
788
|
-
statementNotices.push(notice);
|
|
789
|
-
break;
|
|
790
|
-
}
|
|
791
|
-
case 0x41:
|
|
792
|
-
validateNotificationResponse(body);
|
|
793
|
-
break;
|
|
794
|
-
default:
|
|
795
|
-
failure ??= new Error('unexpected backend message tag ' + hexBackendTag(tag));
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
catch (error) {
|
|
799
|
-
failure ??= asError(error);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
if (status === undefined) {
|
|
803
|
-
if (failure !== undefined)
|
|
804
|
-
throw failure;
|
|
805
|
-
throw new Error('query response ended before ReadyForQuery');
|
|
806
|
-
}
|
|
807
|
-
if (recoveryFailure !== undefined)
|
|
808
|
-
throwWithStatus(recoveryFailure, status);
|
|
809
|
-
if (fields !== undefined || rows.length !== 0) {
|
|
810
|
-
failure ??= new Error('query response ended before CommandComplete');
|
|
811
|
-
}
|
|
812
|
-
if (!sawErrorResponse && completionCount === 0) {
|
|
813
|
-
failure ??= new Error('query response omitted CommandComplete or EmptyQueryResponse');
|
|
814
|
-
}
|
|
815
|
-
if (!sawErrorResponse && mode === 'extended-single' && extendedStage !== 'completed') {
|
|
816
|
-
failure ??= new Error('extended-query response omitted ParseComplete, BindComplete, result description, or completion');
|
|
817
|
-
}
|
|
818
|
-
if (failure !== undefined)
|
|
819
|
-
throwWithStatus(failure, status);
|
|
820
|
-
return { statements, notices, transactionStatus: status };
|
|
821
|
-
}
|
|
822
|
-
function pendingExecResult(kind, fields, rows, commandTag, notices) {
|
|
823
|
-
return {
|
|
824
|
-
kind,
|
|
825
|
-
fields,
|
|
826
|
-
rows,
|
|
827
|
-
commandTag,
|
|
828
|
-
rowCount: commandTagRowCount(commandTag),
|
|
829
|
-
notices,
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
function rawResult(kind, fields, rows, commandTag, notices) {
|
|
833
|
-
return {
|
|
834
|
-
kind,
|
|
835
|
-
fields,
|
|
836
|
-
rows,
|
|
837
|
-
commandTag,
|
|
838
|
-
rowCount: commandTagRowCount(commandTag),
|
|
839
|
-
notices,
|
|
840
|
-
getText(row, column) {
|
|
841
|
-
const columnIndex = resolveFieldIndex(fields, column);
|
|
842
|
-
const queryRow = rows[row];
|
|
843
|
-
if (queryRow === undefined)
|
|
844
|
-
throw new Error('query result has no row at index ' + row);
|
|
845
|
-
return queryRow.text(columnIndex);
|
|
846
|
-
},
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
function resolveFieldIndex(fields, name) {
|
|
850
|
-
let match;
|
|
851
|
-
for (let index = 0; index < fields.length; index += 1) {
|
|
852
|
-
if (fields[index].name !== name)
|
|
853
|
-
continue;
|
|
854
|
-
if (match !== undefined) {
|
|
855
|
-
throw new Error('query result has more than one column named ' +
|
|
856
|
-
JSON.stringify(name) +
|
|
857
|
-
'; use array row mode or a positional raw-row index');
|
|
858
|
-
}
|
|
859
|
-
match = index;
|
|
860
|
-
}
|
|
861
|
-
if (match === undefined) {
|
|
862
|
-
throw new Error('query result has no column named ' + JSON.stringify(name));
|
|
863
|
-
}
|
|
864
|
-
return match;
|
|
865
|
-
}
|
|
866
|
-
function stabilizeQueryOptions(options) {
|
|
867
|
-
const rowMode = options.rowMode;
|
|
868
|
-
const valueMode = options.valueMode;
|
|
869
|
-
const decoders = options.decoders;
|
|
870
|
-
return {
|
|
871
|
-
rowMode,
|
|
872
|
-
valueMode,
|
|
873
|
-
decoders: decoders === undefined ? undefined : Object.freeze({ ...decoders }),
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
function decodeRows(rawRows, fields, options, reuseEmpty) {
|
|
877
|
-
if (options.rowMode !== 'array' && fields.length > 1)
|
|
878
|
-
assertUniqueObjectRowFields(fields);
|
|
879
|
-
if (reuseEmpty && rawRows.length === 0) {
|
|
880
|
-
return rawRows;
|
|
881
|
-
}
|
|
882
|
-
return rawRows.map((row) => materializeRow(row, fields, options));
|
|
883
|
-
}
|
|
884
|
-
function materializePendingExecResult(pending, options) {
|
|
885
|
-
const rows = decodeRows(pending.rows, pending.fields, options, true);
|
|
886
|
-
pending.rows = rows;
|
|
887
|
-
}
|
|
888
|
-
function assertUniqueObjectRowFields(fields) {
|
|
889
|
-
const names = new Set();
|
|
890
|
-
for (const field of fields) {
|
|
891
|
-
if (names.has(field.name)) {
|
|
892
|
-
throw new Error('decoded object rows cannot represent more than one column named ' +
|
|
893
|
-
JSON.stringify(field.name) +
|
|
894
|
-
"; use { rowMode: 'array' } or queryRaw()");
|
|
895
|
-
}
|
|
896
|
-
names.add(field.name);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
function materializeRow(row, fields, options) {
|
|
900
|
-
const values = row.values.map((value, index) => decodeValue(value, fields[index], options));
|
|
901
|
-
if (options.rowMode === 'array')
|
|
902
|
-
return values;
|
|
903
|
-
const object = {};
|
|
904
|
-
for (let index = 0; index < fields.length; index += 1) {
|
|
905
|
-
Object.defineProperty(object, fields[index].name, {
|
|
906
|
-
value: values[index],
|
|
907
|
-
enumerable: true,
|
|
908
|
-
configurable: true,
|
|
909
|
-
writable: true,
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
return object;
|
|
913
|
-
}
|
|
914
|
-
function decodeValue(value, field, options) {
|
|
915
|
-
if (value === null)
|
|
916
|
-
return null;
|
|
917
|
-
if (field.format !== 'text')
|
|
918
|
-
return value;
|
|
919
|
-
const decodedText = decodeUtf8Strict(value, 'query value');
|
|
920
|
-
const decoder = options.decoders?.[field.typeOid];
|
|
921
|
-
if (decoder !== undefined)
|
|
922
|
-
return decoder(decodedText, field);
|
|
923
|
-
if (options.valueMode === 'text')
|
|
924
|
-
return decodedText;
|
|
925
|
-
return decodeBuiltInText(decodedText, field.typeOid);
|
|
926
|
-
}
|
|
927
|
-
function commandTagRowCount(commandTag) {
|
|
928
|
-
if (commandTag === undefined) {
|
|
929
|
-
return null;
|
|
930
|
-
}
|
|
931
|
-
let start = 0;
|
|
932
|
-
let end = commandTag.length;
|
|
933
|
-
while (start < end && isEcmaWhitespace(commandTag.charCodeAt(start)))
|
|
934
|
-
start += 1;
|
|
935
|
-
while (end > start && isEcmaWhitespace(commandTag.charCodeAt(end - 1)))
|
|
936
|
-
end -= 1;
|
|
937
|
-
if (start === end)
|
|
938
|
-
return null;
|
|
939
|
-
let commandEnd = start;
|
|
940
|
-
while (commandEnd < end && !isEcmaWhitespace(commandTag.charCodeAt(commandEnd))) {
|
|
941
|
-
commandEnd += 1;
|
|
942
|
-
}
|
|
943
|
-
if (!hasRowCountCommand(commandTag, start, commandEnd))
|
|
944
|
-
return null;
|
|
945
|
-
let countStart = end;
|
|
946
|
-
while (countStart > start && !isEcmaWhitespace(commandTag.charCodeAt(countStart - 1))) {
|
|
947
|
-
countStart -= 1;
|
|
948
|
-
}
|
|
949
|
-
if (countStart === start)
|
|
950
|
-
return null;
|
|
951
|
-
let value = 0;
|
|
952
|
-
for (let index = countStart; index < end; index += 1) {
|
|
953
|
-
const digit = commandTag.charCodeAt(index) - 0x30;
|
|
954
|
-
if (digit < 0 || digit > 9)
|
|
955
|
-
return null;
|
|
956
|
-
value = value * 10 + digit;
|
|
957
|
-
if (!Number.isSafeInteger(value))
|
|
958
|
-
return null;
|
|
959
|
-
}
|
|
960
|
-
return value;
|
|
961
|
-
}
|
|
962
|
-
function hasRowCountCommand(value, start, end) {
|
|
963
|
-
const length = end - start;
|
|
964
|
-
if (length === 4) {
|
|
965
|
-
return value.startsWith('MOVE', start) || value.startsWith('COPY', start);
|
|
966
|
-
}
|
|
967
|
-
if (length === 5) {
|
|
968
|
-
return value.startsWith('MERGE', start) || value.startsWith('FETCH', start);
|
|
969
|
-
}
|
|
970
|
-
if (length === 6) {
|
|
971
|
-
return (value.startsWith('SELECT', start) ||
|
|
972
|
-
value.startsWith('INSERT', start) ||
|
|
973
|
-
value.startsWith('UPDATE', start) ||
|
|
974
|
-
value.startsWith('DELETE', start));
|
|
975
|
-
}
|
|
976
|
-
return false;
|
|
977
|
-
}
|
|
978
|
-
function isEcmaWhitespace(code) {
|
|
979
|
-
if ((code >= 0x09 && code <= 0x0d) || code === 0x20 || code === 0xa0 || code === 0x1680) {
|
|
980
|
-
return true;
|
|
981
|
-
}
|
|
982
|
-
if (code >= 0x2000 && code <= 0x200a)
|
|
983
|
-
return true;
|
|
984
|
-
switch (code) {
|
|
985
|
-
case 0x2028:
|
|
986
|
-
case 0x2029:
|
|
987
|
-
case 0x202f:
|
|
988
|
-
case 0x205f:
|
|
989
|
-
case 0x3000:
|
|
990
|
-
case 0xfeff:
|
|
991
|
-
return true;
|
|
992
|
-
default:
|
|
993
|
-
return false;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
function normalizeQueryParam(parameter, typeOid, encoders) {
|
|
997
|
-
validateTypeOid(typeOid, false);
|
|
998
|
-
const wrapper = parameter !== null && typeof parameter === 'object'
|
|
999
|
-
? parameterMetadata.get(parameter)
|
|
1000
|
-
: undefined;
|
|
1001
|
-
if (wrapper !== undefined)
|
|
1002
|
-
return normalizeWrapper(wrapper, typeOid);
|
|
1003
|
-
const custom = encoders?.[typeOid];
|
|
1004
|
-
if (custom !== undefined) {
|
|
1005
|
-
const encoded = custom(parameter, typeOid);
|
|
1006
|
-
if (encoded === null || typeof encoded !== 'object') {
|
|
1007
|
-
throw new TypeError('query encoder for OID ' + typeOid + ' must return a parameter helper');
|
|
1008
|
-
}
|
|
1009
|
-
const encodedMetadata = parameterMetadata.get(encoded);
|
|
1010
|
-
if (encodedMetadata === undefined) {
|
|
1011
|
-
throw new TypeError('query encoder for OID ' + typeOid + ' must use text(), binary(), or typedNull()');
|
|
1012
|
-
}
|
|
1013
|
-
return normalizeWrapper(encodedMetadata, typeOid);
|
|
1014
|
-
}
|
|
1015
|
-
if (parameter === null)
|
|
1016
|
-
return { kind: 'null' };
|
|
1017
|
-
if (typeof parameter === 'string') {
|
|
1018
|
-
return { kind: 'text', value: utf8Encoder.encode(parameter) };
|
|
1019
|
-
}
|
|
1020
|
-
if (typeof parameter === 'number') {
|
|
1021
|
-
if (!isNumericOrTextOid(typeOid))
|
|
1022
|
-
throw unsupportedParameter(parameter, typeOid);
|
|
1023
|
-
return { kind: 'text', value: utf8Encoder.encode(scalarText(parameter)) };
|
|
1024
|
-
}
|
|
1025
|
-
if (typeof parameter === 'bigint') {
|
|
1026
|
-
if (!isNumericOrTextOid(typeOid))
|
|
1027
|
-
throw unsupportedParameter(parameter, typeOid);
|
|
1028
|
-
return { kind: 'text', value: utf8Encoder.encode(parameter.toString()) };
|
|
1029
|
-
}
|
|
1030
|
-
if (typeof parameter === 'boolean') {
|
|
1031
|
-
if (typeOid !== exports.postgresOids.bool && !isTextOid(typeOid)) {
|
|
1032
|
-
throw unsupportedParameter(parameter, typeOid);
|
|
1033
|
-
}
|
|
1034
|
-
return {
|
|
1035
|
-
kind: 'text',
|
|
1036
|
-
value: utf8Encoder.encode(parameter ? 'true' : 'false'),
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
if (parameter instanceof Date) {
|
|
1040
|
-
return {
|
|
1041
|
-
kind: 'text',
|
|
1042
|
-
value: utf8Encoder.encode(dateText(parameter, typeOid)),
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
if (isQueryBinaryInput(parameter)) {
|
|
1046
|
-
if (typeOid !== exports.postgresOids.bytea)
|
|
1047
|
-
throw unsupportedParameter(parameter, typeOid);
|
|
1048
|
-
return { kind: 'binary', value: toUint8Array(parameter) };
|
|
1049
|
-
}
|
|
1050
|
-
if (Array.isArray(parameter)) {
|
|
1051
|
-
const elementTypeOid = arrayElementTypeOid(typeOid);
|
|
1052
|
-
if (elementTypeOid === undefined)
|
|
1053
|
-
throw unsupportedParameter(parameter, typeOid);
|
|
1054
|
-
return {
|
|
1055
|
-
kind: 'text',
|
|
1056
|
-
value: utf8Encoder.encode(encodeArrayLiteral(parameter, elementTypeOid)),
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
if (isPlainRecord(parameter)) {
|
|
1060
|
-
if (typeOid !== exports.postgresOids.json && typeOid !== exports.postgresOids.jsonb) {
|
|
1061
|
-
throw unsupportedParameter(parameter, typeOid);
|
|
1062
|
-
}
|
|
1063
|
-
return { kind: 'text', value: utf8Encoder.encode(json(parameter).value) };
|
|
1064
|
-
}
|
|
1065
|
-
throw new TypeError('query parameter is unsupported; use an explicit parameter helper');
|
|
1066
|
-
}
|
|
1067
|
-
function normalizeWrapper(wrapper, typeOid) {
|
|
1068
|
-
if (wrapper.typeOid !== undefined && wrapper.typeOid !== typeOid) {
|
|
1069
|
-
throw new Error('parameter declares PostgreSQL OID ' +
|
|
1070
|
-
wrapper.typeOid +
|
|
1071
|
-
', but PostgreSQL described OID ' +
|
|
1072
|
-
typeOid);
|
|
1073
|
-
}
|
|
1074
|
-
if (wrapper.format === 'null')
|
|
1075
|
-
return { kind: 'null' };
|
|
1076
|
-
if (wrapper.format === 'text') {
|
|
1077
|
-
return { kind: 'text', value: utf8Encoder.encode(wrapper.value) };
|
|
1078
|
-
}
|
|
1079
|
-
return { kind: 'binary', value: toUint8Array(wrapper.value) };
|
|
1080
|
-
}
|
|
1081
|
-
function parameterDeclaredTypeOid(parameter) {
|
|
1082
|
-
if (parameter !== null && typeof parameter === 'object') {
|
|
1083
|
-
const wrapper = parameterMetadata.get(parameter);
|
|
1084
|
-
if (wrapper?.typeOid !== undefined) {
|
|
1085
|
-
validateTypeOid(wrapper.typeOid, false);
|
|
1086
|
-
return wrapper.typeOid;
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
return 0;
|
|
1090
|
-
}
|
|
1091
|
-
function snapshotQueryParam(parameter) {
|
|
1092
|
-
if (parameter === null ||
|
|
1093
|
-
typeof parameter === 'string' ||
|
|
1094
|
-
typeof parameter === 'number' ||
|
|
1095
|
-
typeof parameter === 'bigint' ||
|
|
1096
|
-
typeof parameter === 'boolean') {
|
|
1097
|
-
return parameter;
|
|
1098
|
-
}
|
|
1099
|
-
if (parameter === undefined)
|
|
1100
|
-
throw new TypeError('query parameters must not be undefined');
|
|
1101
|
-
const wrapper = typeof parameter === 'object' ? parameterMetadata.get(parameter) : undefined;
|
|
1102
|
-
if (wrapper !== undefined) {
|
|
1103
|
-
if (wrapper.format === 'null')
|
|
1104
|
-
return typedNull(wrapper.typeOid);
|
|
1105
|
-
if (wrapper.format === 'binary') {
|
|
1106
|
-
return binary(toUint8Array(wrapper.value).slice(), wrapper.typeOid);
|
|
1107
|
-
}
|
|
1108
|
-
return text(wrapper.value, wrapper.typeOid);
|
|
1109
|
-
}
|
|
1110
|
-
if (parameter instanceof Date)
|
|
1111
|
-
return new Date(parameter.getTime());
|
|
1112
|
-
if (isQueryBinaryInput(parameter))
|
|
1113
|
-
return toUint8Array(parameter).slice();
|
|
1114
|
-
if (Array.isArray(parameter))
|
|
1115
|
-
return Array.from(parameter, (value) => snapshotQueryParam(value));
|
|
1116
|
-
if (isPlainRecord(parameter)) {
|
|
1117
|
-
let encoded;
|
|
1118
|
-
try {
|
|
1119
|
-
encoded = JSON.stringify(parameter);
|
|
1120
|
-
}
|
|
1121
|
-
catch (error) {
|
|
1122
|
-
throw withCause(new TypeError('plain-object query parameters must be JSON-serializable'), error);
|
|
1123
|
-
}
|
|
1124
|
-
if (encoded === undefined)
|
|
1125
|
-
throw new TypeError('plain-object query parameters must be JSON-serializable');
|
|
1126
|
-
return JSON.parse(encoded);
|
|
1127
|
-
}
|
|
1128
|
-
throw new TypeError('query parameter is unsupported; use an explicit parameter helper');
|
|
1129
|
-
}
|
|
1130
|
-
function parameterWrapper(metadata) {
|
|
1131
|
-
if (metadata.typeOid !== undefined)
|
|
1132
|
-
validateTypeOid(metadata.typeOid, false);
|
|
1133
|
-
const visible = metadata.format === 'null'
|
|
1134
|
-
? { format: 'null', typeOid: metadata.typeOid }
|
|
1135
|
-
: {
|
|
1136
|
-
format: metadata.format,
|
|
1137
|
-
value: metadata.value,
|
|
1138
|
-
...(metadata.typeOid === undefined ? {} : { typeOid: metadata.typeOid }),
|
|
1139
|
-
};
|
|
1140
|
-
const wrapper = Object.freeze(visible);
|
|
1141
|
-
parameterMetadata.set(wrapper, metadata);
|
|
1142
|
-
return wrapper;
|
|
1143
|
-
}
|
|
1144
|
-
function isQueryBinaryInput(value) {
|
|
1145
|
-
return value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
1146
|
-
}
|
|
1147
|
-
function validateStatementInput(sql, parameters) {
|
|
1148
|
-
if (parameters.length > 0x7fff) {
|
|
1149
|
-
throw new Error('extended query supports at most 32767 parameters, got ' + parameters.length);
|
|
1150
|
-
}
|
|
1151
|
-
if (sql.includes('\0'))
|
|
1152
|
-
throw new Error('extended query SQL must not contain NUL bytes');
|
|
1153
|
-
}
|
|
1154
|
-
function validateTypeOid(typeOid, allowZero) {
|
|
1155
|
-
if (!Number.isInteger(typeOid) || typeOid < (allowZero ? 0 : 1) || typeOid > 0xffffffff) {
|
|
1156
|
-
throw new TypeError('PostgreSQL type OID must be ' + (allowZero ? 'zero or ' : '') + 'a positive uint32');
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
function scalarText(value) {
|
|
1160
|
-
if (typeof value === 'number') {
|
|
1161
|
-
if (!Number.isFinite(value))
|
|
1162
|
-
throw new TypeError('number query parameters must be finite');
|
|
1163
|
-
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
1164
|
-
throw new TypeError('integer query parameters must be safe integers; use bigint instead');
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
if (value instanceof Date) {
|
|
1168
|
-
if (Number.isNaN(value.getTime()))
|
|
1169
|
-
throw new TypeError('Date query parameters must be valid');
|
|
1170
|
-
return value.toISOString();
|
|
1171
|
-
}
|
|
1172
|
-
return String(value);
|
|
1173
|
-
}
|
|
1174
|
-
function dateText(value, typeOid) {
|
|
1175
|
-
const iso = scalarText(value);
|
|
1176
|
-
if (typeOid === exports.postgresOids.date)
|
|
1177
|
-
return iso.slice(0, 10);
|
|
1178
|
-
if (typeOid === exports.postgresOids.timestamp)
|
|
1179
|
-
return iso.slice(0, -1).replace('T', ' ');
|
|
1180
|
-
if (typeOid === exports.postgresOids.timestamptz || isTextOid(typeOid))
|
|
1181
|
-
return iso;
|
|
1182
|
-
throw unsupportedParameter(value, typeOid);
|
|
1183
|
-
}
|
|
1184
|
-
function isNumericOrTextOid(typeOid) {
|
|
1185
|
-
return ([
|
|
1186
|
-
exports.postgresOids.int2,
|
|
1187
|
-
exports.postgresOids.int4,
|
|
1188
|
-
exports.postgresOids.int8,
|
|
1189
|
-
exports.postgresOids.oid,
|
|
1190
|
-
exports.postgresOids.float4,
|
|
1191
|
-
exports.postgresOids.float8,
|
|
1192
|
-
exports.postgresOids.numeric,
|
|
1193
|
-
].includes(typeOid) || isTextOid(typeOid));
|
|
1194
|
-
}
|
|
1195
|
-
function isTextOid(typeOid) {
|
|
1196
|
-
return typeOid === exports.postgresOids.text || typeOid === exports.postgresOids.varchar;
|
|
1197
|
-
}
|
|
1198
|
-
function unsupportedParameter(value, typeOid) {
|
|
1199
|
-
const kind = value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value;
|
|
1200
|
-
return new TypeError('cannot safely encode ' +
|
|
1201
|
-
kind +
|
|
1202
|
-
' for PostgreSQL OID ' +
|
|
1203
|
-
typeOid +
|
|
1204
|
-
'; use a typed helper or encoder');
|
|
1205
|
-
}
|
|
1206
|
-
function isPlainRecord(value) {
|
|
1207
|
-
if (value === null || typeof value !== 'object')
|
|
1208
|
-
return false;
|
|
1209
|
-
const prototype = Object.getPrototypeOf(value);
|
|
1210
|
-
return prototype === Object.prototype || prototype === null;
|
|
1211
|
-
}
|
|
1212
|
-
function throwWithStatus(error, status) {
|
|
1213
|
-
if (status !== undefined)
|
|
1214
|
-
transactionStatuses.set(error, status);
|
|
1215
|
-
throw error;
|
|
1216
|
-
}
|
|
1217
|
-
function asError(error) {
|
|
1218
|
-
return error instanceof Error
|
|
1219
|
-
? error
|
|
1220
|
-
: withCause(new Error('PostgreSQL response parsing failed'), error);
|
|
1221
|
-
}
|
|
1222
|
-
function withCause(error, cause) {
|
|
1223
|
-
Object.defineProperty(error, 'cause', {
|
|
1224
|
-
value: cause,
|
|
1225
|
-
configurable: true,
|
|
1226
|
-
writable: true,
|
|
1227
|
-
});
|
|
1228
|
-
return error;
|
|
1229
|
-
}
|
|
1230
|
-
function decodeBuiltInText(value, typeOid) {
|
|
1231
|
-
switch (typeOid) {
|
|
1232
|
-
case exports.postgresOids.bool:
|
|
1233
|
-
if (value === 't')
|
|
1234
|
-
return true;
|
|
1235
|
-
if (value === 'f')
|
|
1236
|
-
return false;
|
|
1237
|
-
throw new Error('invalid PostgreSQL bool text ' + JSON.stringify(value));
|
|
1238
|
-
case exports.postgresOids.int2:
|
|
1239
|
-
case exports.postgresOids.int4:
|
|
1240
|
-
case exports.postgresOids.oid:
|
|
1241
|
-
return decodeInteger(value, typeOid);
|
|
1242
|
-
case exports.postgresOids.float4:
|
|
1243
|
-
case exports.postgresOids.float8:
|
|
1244
|
-
return decodeFloat(value, typeOid);
|
|
1245
|
-
case exports.postgresOids.json:
|
|
1246
|
-
case exports.postgresOids.jsonb:
|
|
1247
|
-
return JSON.parse(value);
|
|
1248
|
-
case exports.postgresOids.bytea:
|
|
1249
|
-
return decodeBytea(value);
|
|
1250
|
-
case exports.postgresOids.int8:
|
|
1251
|
-
case exports.postgresOids.numeric:
|
|
1252
|
-
case exports.postgresOids.date:
|
|
1253
|
-
case exports.postgresOids.time:
|
|
1254
|
-
case exports.postgresOids.timestamp:
|
|
1255
|
-
case exports.postgresOids.timestamptz:
|
|
1256
|
-
case exports.postgresOids.interval:
|
|
1257
|
-
case exports.postgresOids.timetz:
|
|
1258
|
-
return value;
|
|
1259
|
-
default: {
|
|
1260
|
-
const elementTypeOid = arrayElementTypeOid(typeOid);
|
|
1261
|
-
if (elementTypeOid === undefined)
|
|
1262
|
-
return value;
|
|
1263
|
-
return decodeArrayLiteral(value, elementTypeOid);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
function decodeInteger(value, typeOid) {
|
|
1268
|
-
if (!/^-?[0-9]+$/.test(value)) {
|
|
1269
|
-
throw new Error('invalid PostgreSQL integer text for OID ' + typeOid + ': ' + JSON.stringify(value));
|
|
1270
|
-
}
|
|
1271
|
-
const decoded = Number(value);
|
|
1272
|
-
if (!Number.isSafeInteger(decoded)) {
|
|
1273
|
-
throw new Error('PostgreSQL integer for OID ' + typeOid + ' exceeds JavaScript safe range');
|
|
1274
|
-
}
|
|
1275
|
-
return decoded;
|
|
1276
|
-
}
|
|
1277
|
-
function decodeFloat(value, typeOid) {
|
|
1278
|
-
if (value === 'NaN')
|
|
1279
|
-
return Number.NaN;
|
|
1280
|
-
if (value === 'Infinity')
|
|
1281
|
-
return Number.POSITIVE_INFINITY;
|
|
1282
|
-
if (value === '-Infinity')
|
|
1283
|
-
return Number.NEGATIVE_INFINITY;
|
|
1284
|
-
if (!/^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/.test(value)) {
|
|
1285
|
-
throw new Error('invalid PostgreSQL float text for OID ' + typeOid + ': ' + JSON.stringify(value));
|
|
1286
|
-
}
|
|
1287
|
-
const decoded = Number(value);
|
|
1288
|
-
if (!Number.isFinite(decoded))
|
|
1289
|
-
throw new Error('PostgreSQL float text is outside JavaScript range');
|
|
1290
|
-
return decoded;
|
|
1291
|
-
}
|
|
1292
|
-
function decodeBytea(value) {
|
|
1293
|
-
if (value.startsWith('\\x')) {
|
|
1294
|
-
const hex = value.slice(2);
|
|
1295
|
-
if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {
|
|
1296
|
-
throw new Error('invalid PostgreSQL hex bytea text');
|
|
1297
|
-
}
|
|
1298
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
1299
|
-
for (let index = 0; index < bytes.length; index += 1) {
|
|
1300
|
-
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
1301
|
-
}
|
|
1302
|
-
return bytes;
|
|
1303
|
-
}
|
|
1304
|
-
const bytes = [];
|
|
1305
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
1306
|
-
if (value[index] !== '\\') {
|
|
1307
|
-
bytes.push(value.charCodeAt(index));
|
|
1308
|
-
continue;
|
|
1309
|
-
}
|
|
1310
|
-
if (value[index + 1] === '\\') {
|
|
1311
|
-
bytes.push(0x5c);
|
|
1312
|
-
index += 1;
|
|
1313
|
-
continue;
|
|
1314
|
-
}
|
|
1315
|
-
const octal = value.slice(index + 1, index + 4);
|
|
1316
|
-
if (!/^[0-3][0-7]{2}$/.test(octal))
|
|
1317
|
-
throw new Error('invalid PostgreSQL escape bytea text');
|
|
1318
|
-
bytes.push(Number.parseInt(octal, 8));
|
|
1319
|
-
index += 3;
|
|
1320
|
-
}
|
|
1321
|
-
return Uint8Array.from(bytes);
|
|
1322
|
-
}
|
|
1323
|
-
const arrayElementOids = new Map([
|
|
1324
|
-
[exports.postgresOids.boolArray, exports.postgresOids.bool],
|
|
1325
|
-
[exports.postgresOids.byteaArray, exports.postgresOids.bytea],
|
|
1326
|
-
[exports.postgresOids.charArray, exports.postgresOids.char],
|
|
1327
|
-
[exports.postgresOids.nameArray, exports.postgresOids.name],
|
|
1328
|
-
[exports.postgresOids.int2Array, exports.postgresOids.int2],
|
|
1329
|
-
[exports.postgresOids.int4Array, exports.postgresOids.int4],
|
|
1330
|
-
[exports.postgresOids.textArray, exports.postgresOids.text],
|
|
1331
|
-
[exports.postgresOids.bpcharArray, exports.postgresOids.bpchar],
|
|
1332
|
-
[exports.postgresOids.varcharArray, exports.postgresOids.varchar],
|
|
1333
|
-
[exports.postgresOids.int8Array, exports.postgresOids.int8],
|
|
1334
|
-
[exports.postgresOids.float4Array, exports.postgresOids.float4],
|
|
1335
|
-
[exports.postgresOids.float8Array, exports.postgresOids.float8],
|
|
1336
|
-
[exports.postgresOids.oidArray, exports.postgresOids.oid],
|
|
1337
|
-
[exports.postgresOids.dateArray, exports.postgresOids.date],
|
|
1338
|
-
[exports.postgresOids.timeArray, exports.postgresOids.time],
|
|
1339
|
-
[exports.postgresOids.timestampArray, exports.postgresOids.timestamp],
|
|
1340
|
-
[exports.postgresOids.timestamptzArray, exports.postgresOids.timestamptz],
|
|
1341
|
-
[exports.postgresOids.intervalArray, exports.postgresOids.interval],
|
|
1342
|
-
[exports.postgresOids.numericArray, exports.postgresOids.numeric],
|
|
1343
|
-
[exports.postgresOids.timetzArray, exports.postgresOids.timetz],
|
|
1344
|
-
[exports.postgresOids.jsonArray, exports.postgresOids.json],
|
|
1345
|
-
[exports.postgresOids.xmlArray, exports.postgresOids.xml],
|
|
1346
|
-
[exports.postgresOids.uuidArray, exports.postgresOids.uuid],
|
|
1347
|
-
[exports.postgresOids.jsonbArray, exports.postgresOids.jsonb],
|
|
1348
|
-
]);
|
|
1349
|
-
function arrayElementTypeOid(arrayTypeOid) {
|
|
1350
|
-
return arrayElementOids.get(arrayTypeOid);
|
|
1351
|
-
}
|
|
1352
|
-
function encodeArrayLiteral(values, elementTypeOid) {
|
|
1353
|
-
return ('{' + Array.from(values, (value) => encodeArrayElement(value, elementTypeOid)).join(',') + '}');
|
|
1354
|
-
}
|
|
1355
|
-
function encodeArrayElement(value, elementTypeOid) {
|
|
1356
|
-
if (value === null)
|
|
1357
|
-
return 'NULL';
|
|
1358
|
-
if (value === undefined)
|
|
1359
|
-
throw new TypeError('PostgreSQL arrays cannot contain undefined');
|
|
1360
|
-
if (Array.isArray(value))
|
|
1361
|
-
return encodeArrayLiteral(value, elementTypeOid);
|
|
1362
|
-
const wrapper = typeof value === 'object' && value !== null ? parameterMetadata.get(value) : undefined;
|
|
1363
|
-
if (wrapper !== undefined) {
|
|
1364
|
-
if (elementTypeOid !== undefined &&
|
|
1365
|
-
wrapper.typeOid !== undefined &&
|
|
1366
|
-
wrapper.typeOid !== elementTypeOid) {
|
|
1367
|
-
throw new TypeError('array element declares PostgreSQL OID ' +
|
|
1368
|
-
wrapper.typeOid +
|
|
1369
|
-
', but the array resolves element OID ' +
|
|
1370
|
-
elementTypeOid);
|
|
1371
|
-
}
|
|
1372
|
-
if (wrapper.format === 'null')
|
|
1373
|
-
return 'NULL';
|
|
1374
|
-
if (wrapper.format === 'binary') {
|
|
1375
|
-
if (elementTypeOid !== undefined && elementTypeOid !== exports.postgresOids.bytea) {
|
|
1376
|
-
throw unsupportedParameter(value, elementTypeOid);
|
|
1377
|
-
}
|
|
1378
|
-
return quoteArrayElement('\\x' + bytesToHex(toUint8Array(wrapper.value)));
|
|
1379
|
-
}
|
|
1380
|
-
return quoteArrayElement(wrapper.value);
|
|
1381
|
-
}
|
|
1382
|
-
if (typeof value === 'string')
|
|
1383
|
-
return quoteArrayElement(value);
|
|
1384
|
-
if (typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') {
|
|
1385
|
-
if (elementTypeOid !== undefined) {
|
|
1386
|
-
normalizeQueryParam(value, elementTypeOid, undefined);
|
|
1387
|
-
}
|
|
1388
|
-
return quoteArrayElement(scalarText(value));
|
|
1389
|
-
}
|
|
1390
|
-
if (value instanceof Date) {
|
|
1391
|
-
return quoteArrayElement(elementTypeOid === undefined ? scalarText(value) : dateText(value, elementTypeOid));
|
|
1392
|
-
}
|
|
1393
|
-
if (isQueryBinaryInput(value)) {
|
|
1394
|
-
if (elementTypeOid !== undefined && elementTypeOid !== exports.postgresOids.bytea) {
|
|
1395
|
-
throw unsupportedParameter(value, elementTypeOid);
|
|
1396
|
-
}
|
|
1397
|
-
return quoteArrayElement('\\x' + bytesToHex(toUint8Array(value)));
|
|
1398
|
-
}
|
|
1399
|
-
if (isPlainRecord(value) &&
|
|
1400
|
-
(elementTypeOid === exports.postgresOids.json || elementTypeOid === exports.postgresOids.jsonb)) {
|
|
1401
|
-
return quoteArrayElement(json(value, elementTypeOid).value);
|
|
1402
|
-
}
|
|
1403
|
-
throw new TypeError('PostgreSQL array element is unsupported; use an explicit helper');
|
|
1404
|
-
}
|
|
1405
|
-
function quoteArrayElement(value) {
|
|
1406
|
-
return '"' + value.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
|
1407
|
-
}
|
|
1408
|
-
function bytesToHex(bytes) {
|
|
1409
|
-
let hex = '';
|
|
1410
|
-
for (const byte of bytes)
|
|
1411
|
-
hex += byte.toString(16).padStart(2, '0');
|
|
1412
|
-
return hex;
|
|
1413
|
-
}
|
|
1414
|
-
function decodeArrayLiteral(value, elementTypeOid) {
|
|
1415
|
-
const parsed = new ArrayTextParser(value).parse();
|
|
1416
|
-
return decodeArrayValues(parsed, elementTypeOid);
|
|
1417
|
-
}
|
|
1418
|
-
function decodeArrayValues(values, elementTypeOid) {
|
|
1419
|
-
return values.map((value) => Array.isArray(value)
|
|
1420
|
-
? decodeArrayValues(value, elementTypeOid)
|
|
1421
|
-
: value === null
|
|
1422
|
-
? null
|
|
1423
|
-
: decodeBuiltInText(value, elementTypeOid));
|
|
1424
|
-
}
|
|
1425
|
-
class ArrayTextParser {
|
|
1426
|
-
#input;
|
|
1427
|
-
#offset = 0;
|
|
1428
|
-
constructor(input) {
|
|
1429
|
-
this.#input = input;
|
|
1430
|
-
}
|
|
1431
|
-
parse() {
|
|
1432
|
-
if (this.#input.startsWith('[')) {
|
|
1433
|
-
const separator = this.#input.indexOf('=');
|
|
1434
|
-
if (separator < 0)
|
|
1435
|
-
throw new Error('invalid PostgreSQL array dimensions');
|
|
1436
|
-
this.#offset = separator + 1;
|
|
1437
|
-
}
|
|
1438
|
-
const result = this.#level();
|
|
1439
|
-
if (this.#offset !== this.#input.length)
|
|
1440
|
-
throw new Error('PostgreSQL array text has trailing data');
|
|
1441
|
-
return result;
|
|
1442
|
-
}
|
|
1443
|
-
#level() {
|
|
1444
|
-
if (this.#input[this.#offset] !== '{')
|
|
1445
|
-
throw new Error('PostgreSQL array text must start with {');
|
|
1446
|
-
this.#offset += 1;
|
|
1447
|
-
const result = [];
|
|
1448
|
-
if (this.#input[this.#offset] === '}') {
|
|
1449
|
-
this.#offset += 1;
|
|
1450
|
-
return result;
|
|
1451
|
-
}
|
|
1452
|
-
for (;;) {
|
|
1453
|
-
result.push(this.#input[this.#offset] === '{' ? this.#level() : this.#element());
|
|
1454
|
-
const delimiter = this.#input[this.#offset];
|
|
1455
|
-
if (delimiter === '}') {
|
|
1456
|
-
this.#offset += 1;
|
|
1457
|
-
return result;
|
|
1458
|
-
}
|
|
1459
|
-
if (delimiter !== ',')
|
|
1460
|
-
throw new Error('invalid PostgreSQL array delimiter');
|
|
1461
|
-
this.#offset += 1;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
#element() {
|
|
1465
|
-
if (this.#input[this.#offset] === '"') {
|
|
1466
|
-
this.#offset += 1;
|
|
1467
|
-
let value = '';
|
|
1468
|
-
for (;;) {
|
|
1469
|
-
const character = this.#input[this.#offset];
|
|
1470
|
-
if (character === undefined)
|
|
1471
|
-
throw new Error('unterminated quoted PostgreSQL array value');
|
|
1472
|
-
this.#offset += 1;
|
|
1473
|
-
if (character === '"')
|
|
1474
|
-
return value;
|
|
1475
|
-
if (character === '\\') {
|
|
1476
|
-
const escaped = this.#input[this.#offset];
|
|
1477
|
-
if (escaped === undefined)
|
|
1478
|
-
throw new Error('unterminated PostgreSQL array escape');
|
|
1479
|
-
value += escaped;
|
|
1480
|
-
this.#offset += 1;
|
|
1481
|
-
}
|
|
1482
|
-
else {
|
|
1483
|
-
value += character;
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
let value = '';
|
|
1488
|
-
while (this.#offset < this.#input.length) {
|
|
1489
|
-
const character = this.#input[this.#offset];
|
|
1490
|
-
if (character === ',' || character === '}')
|
|
1491
|
-
break;
|
|
1492
|
-
this.#offset += 1;
|
|
1493
|
-
if (character === '\\') {
|
|
1494
|
-
const escaped = this.#input[this.#offset];
|
|
1495
|
-
if (escaped === undefined)
|
|
1496
|
-
throw new Error('unterminated PostgreSQL array escape');
|
|
1497
|
-
value += escaped;
|
|
1498
|
-
this.#offset += 1;
|
|
1499
|
-
}
|
|
1500
|
-
else {
|
|
1501
|
-
value += character;
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
return value === 'NULL' ? null : value;
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
function assertNoTopLevelCopy(sql) {
|
|
1508
|
-
if (containsTopLevelCopy(sql)) {
|
|
1509
|
-
throw new Error('structured SQL does not support COPY; use a raw protocol API for COPY traffic');
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
/**
|
|
1513
|
-
* Reject transaction commands whose response boundary cannot prove that the
|
|
1514
|
-
* callback still owns the transaction it started. PostgreSQL reports both
|
|
1515
|
-
* `ROLLBACK TO SAVEPOINT` and `ROLLBACK AND CHAIN` as command tag `ROLLBACK`
|
|
1516
|
-
* with ReadyForQuery status `transaction`, so the latter must be rejected
|
|
1517
|
-
* before execution rather than inferred from the backend response.
|
|
1518
|
-
*/
|
|
1519
|
-
function assertNoTransactionChain(sql) {
|
|
1520
|
-
if (containsTransactionChain(sql)) {
|
|
1521
|
-
throw new Error('callback transactions do not support ROLLBACK/ABORT ... AND CHAIN; return or throw from the callback instead');
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
function structuredSimpleQuery(sql) {
|
|
1525
|
-
assertNoTopLevelCopy(sql);
|
|
1526
|
-
return (0, protocol_js_1.simpleQuery)(sql);
|
|
1527
|
-
}
|
|
1528
|
-
function containsTopLevelCopy(sql) {
|
|
1529
|
-
return (scanTopLevelTokens(sql, false, (word, first) => first && word === 'copy') ||
|
|
1530
|
-
scanTopLevelTokens(sql, true, (word, first) => first && word === 'copy'));
|
|
1531
|
-
}
|
|
1532
|
-
function containsTransactionChain(sql) {
|
|
1533
|
-
return scanTransactionChain(sql, false) || scanTransactionChain(sql, true);
|
|
1534
|
-
}
|
|
1535
|
-
function scanTransactionChain(sql, plainStringsEscapeBackslashes) {
|
|
1536
|
-
let currentStatement = -1;
|
|
1537
|
-
let state = 'ineligible';
|
|
1538
|
-
return scanTopLevelTokens(sql, plainStringsEscapeBackslashes, (word, first, statement) => {
|
|
1539
|
-
if (statement !== currentStatement) {
|
|
1540
|
-
currentStatement = statement;
|
|
1541
|
-
state = first && (word === 'rollback' || word === 'abort') ? 'afterControl' : 'ineligible';
|
|
1542
|
-
return false;
|
|
1543
|
-
}
|
|
1544
|
-
if (word === undefined) {
|
|
1545
|
-
state = 'ineligible';
|
|
1546
|
-
return false;
|
|
1547
|
-
}
|
|
1548
|
-
if (state === 'afterControl' && (word === 'work' || word === 'transaction')) {
|
|
1549
|
-
state = 'afterQualifier';
|
|
1550
|
-
}
|
|
1551
|
-
else if ((state === 'afterControl' || state === 'afterQualifier') && word === 'and') {
|
|
1552
|
-
state = 'afterAnd';
|
|
1553
|
-
}
|
|
1554
|
-
else if (state === 'afterAnd' && word === 'chain') {
|
|
1555
|
-
return true;
|
|
1556
|
-
}
|
|
1557
|
-
else {
|
|
1558
|
-
// This also keeps ROLLBACK TO [SAVEPOINT] inside the managed transaction.
|
|
1559
|
-
state = 'ineligible';
|
|
1560
|
-
}
|
|
1561
|
-
return false;
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
function scanTopLevelTokens(sql, plainStringsEscapeBackslashes, visit) {
|
|
1565
|
-
let offset = 0;
|
|
1566
|
-
let depth = 0;
|
|
1567
|
-
let statementStart = true;
|
|
1568
|
-
let statement = 0;
|
|
1569
|
-
while (offset < sql.length) {
|
|
1570
|
-
const character = sql[offset];
|
|
1571
|
-
if (/\s/.test(character)) {
|
|
1572
|
-
offset += 1;
|
|
1573
|
-
continue;
|
|
1574
|
-
}
|
|
1575
|
-
if (character === '-' && sql[offset + 1] === '-') {
|
|
1576
|
-
let end = offset + 2;
|
|
1577
|
-
while (end < sql.length && sql[end] !== '\n' && sql[end] !== '\r')
|
|
1578
|
-
end += 1;
|
|
1579
|
-
offset = end < sql.length ? end + 1 : sql.length;
|
|
1580
|
-
continue;
|
|
1581
|
-
}
|
|
1582
|
-
if (character === '/' && sql[offset + 1] === '*') {
|
|
1583
|
-
offset = skipBlockComment(sql, offset + 2);
|
|
1584
|
-
continue;
|
|
1585
|
-
}
|
|
1586
|
-
if ((character === 'e' || character === 'E') && sql[offset + 1] === "'") {
|
|
1587
|
-
if (depth === 0 && visit(undefined, statementStart, statement))
|
|
1588
|
-
return true;
|
|
1589
|
-
statementStart = false;
|
|
1590
|
-
offset = skipSingleQuote(sql, offset + 1, true);
|
|
1591
|
-
continue;
|
|
1592
|
-
}
|
|
1593
|
-
if (character === "'") {
|
|
1594
|
-
if (depth === 0 && visit(undefined, statementStart, statement))
|
|
1595
|
-
return true;
|
|
1596
|
-
statementStart = false;
|
|
1597
|
-
offset = skipSingleQuote(sql, offset, plainStringsEscapeBackslashes);
|
|
1598
|
-
continue;
|
|
1599
|
-
}
|
|
1600
|
-
if (character === '"') {
|
|
1601
|
-
if (depth === 0 && visit(undefined, statementStart, statement))
|
|
1602
|
-
return true;
|
|
1603
|
-
statementStart = false;
|
|
1604
|
-
offset = skipDoubleQuote(sql, offset);
|
|
1605
|
-
continue;
|
|
1606
|
-
}
|
|
1607
|
-
if (character === '$') {
|
|
1608
|
-
const delimiter = dollarQuoteDelimiter(sql, offset);
|
|
1609
|
-
if (delimiter !== undefined) {
|
|
1610
|
-
if (depth === 0 && visit(undefined, statementStart, statement))
|
|
1611
|
-
return true;
|
|
1612
|
-
statementStart = false;
|
|
1613
|
-
const end = sql.indexOf(delimiter, offset + delimiter.length);
|
|
1614
|
-
offset = end < 0 ? sql.length : end + delimiter.length;
|
|
1615
|
-
continue;
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
if (character === '(') {
|
|
1619
|
-
if (depth === 0 && visit(undefined, statementStart, statement))
|
|
1620
|
-
return true;
|
|
1621
|
-
depth += 1;
|
|
1622
|
-
statementStart = false;
|
|
1623
|
-
offset += 1;
|
|
1624
|
-
continue;
|
|
1625
|
-
}
|
|
1626
|
-
if (character === ')') {
|
|
1627
|
-
if (depth > 0)
|
|
1628
|
-
depth -= 1;
|
|
1629
|
-
offset += 1;
|
|
1630
|
-
continue;
|
|
1631
|
-
}
|
|
1632
|
-
if (character === ';' && depth === 0) {
|
|
1633
|
-
statementStart = true;
|
|
1634
|
-
statement += 1;
|
|
1635
|
-
offset += 1;
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
|
-
if (isPostgresIdentifierStart(character)) {
|
|
1639
|
-
const start = offset;
|
|
1640
|
-
offset += 1;
|
|
1641
|
-
while (offset < sql.length && isPostgresIdentifierContinuation(sql[offset]))
|
|
1642
|
-
offset += 1;
|
|
1643
|
-
if (depth === 0 && visit(sql.slice(start, offset).toLowerCase(), statementStart, statement)) {
|
|
1644
|
-
return true;
|
|
1645
|
-
}
|
|
1646
|
-
statementStart = false;
|
|
1647
|
-
continue;
|
|
1648
|
-
}
|
|
1649
|
-
if (depth === 0 && visit(undefined, statementStart, statement))
|
|
1650
|
-
return true;
|
|
1651
|
-
statementStart = false;
|
|
1652
|
-
offset += 1;
|
|
1653
|
-
}
|
|
1654
|
-
return false;
|
|
1655
|
-
}
|
|
1656
|
-
function skipBlockComment(sql, start) {
|
|
1657
|
-
let depth = 1;
|
|
1658
|
-
let offset = start;
|
|
1659
|
-
while (offset < sql.length && depth > 0) {
|
|
1660
|
-
if (sql[offset] === '/' && sql[offset + 1] === '*') {
|
|
1661
|
-
depth += 1;
|
|
1662
|
-
offset += 2;
|
|
1663
|
-
}
|
|
1664
|
-
else if (sql[offset] === '*' && sql[offset + 1] === '/') {
|
|
1665
|
-
depth -= 1;
|
|
1666
|
-
offset += 2;
|
|
1667
|
-
}
|
|
1668
|
-
else {
|
|
1669
|
-
offset += 1;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
return offset;
|
|
1673
|
-
}
|
|
1674
|
-
function skipSingleQuote(sql, quoteOffset, escapeBackslash) {
|
|
1675
|
-
let offset = quoteOffset + 1;
|
|
1676
|
-
while (offset < sql.length) {
|
|
1677
|
-
if (escapeBackslash && sql[offset] === '\\') {
|
|
1678
|
-
offset += Math.min(2, sql.length - offset);
|
|
1679
|
-
}
|
|
1680
|
-
else if (sql[offset] === "'" && sql[offset + 1] === "'") {
|
|
1681
|
-
offset += 2;
|
|
1682
|
-
}
|
|
1683
|
-
else if (sql[offset] === "'") {
|
|
1684
|
-
return offset + 1;
|
|
1685
|
-
}
|
|
1686
|
-
else {
|
|
1687
|
-
offset += 1;
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
return offset;
|
|
1691
|
-
}
|
|
1692
|
-
function skipDoubleQuote(sql, quoteOffset) {
|
|
1693
|
-
let offset = quoteOffset + 1;
|
|
1694
|
-
while (offset < sql.length) {
|
|
1695
|
-
if (sql[offset] === '"' && sql[offset + 1] === '"')
|
|
1696
|
-
offset += 2;
|
|
1697
|
-
else if (sql[offset] === '"')
|
|
1698
|
-
return offset + 1;
|
|
1699
|
-
else
|
|
1700
|
-
offset += 1;
|
|
1701
|
-
}
|
|
1702
|
-
return offset;
|
|
1703
|
-
}
|
|
1704
|
-
function dollarQuoteDelimiter(sql, offset) {
|
|
1705
|
-
let end = offset + 1;
|
|
1706
|
-
if (sql[end] === '$')
|
|
1707
|
-
return '$$';
|
|
1708
|
-
if (end >= sql.length || !isPostgresIdentifierStart(sql[end]))
|
|
1709
|
-
return undefined;
|
|
1710
|
-
end += 1;
|
|
1711
|
-
while (end < sql.length && sql[end] !== '$' && isPostgresIdentifierContinuation(sql[end])) {
|
|
1712
|
-
end += 1;
|
|
1713
|
-
}
|
|
1714
|
-
return sql[end] === '$' ? sql.slice(offset, end + 1) : undefined;
|
|
1715
|
-
}
|
|
1716
|
-
function isPostgresIdentifierStart(character) {
|
|
1717
|
-
const code = character.charCodeAt(0);
|
|
1718
|
-
return (character === '_' ||
|
|
1719
|
-
(code >= 0x41 && code <= 0x5a) ||
|
|
1720
|
-
(code >= 0x61 && code <= 0x7a) ||
|
|
1721
|
-
code >= 0x80);
|
|
1722
|
-
}
|
|
1723
|
-
function isPostgresIdentifierContinuation(character) {
|
|
1724
|
-
const code = character.charCodeAt(0);
|
|
1725
|
-
return (isPostgresIdentifierStart(character) || character === '$' || (code >= 0x30 && code <= 0x39));
|
|
1726
|
-
}
|
|
1727
|
-
class ByteWriter {
|
|
1728
|
-
#bytes;
|
|
1729
|
-
#offset = 0;
|
|
1730
|
-
constructor(length) {
|
|
1731
|
-
this.#bytes = new Uint8Array(length);
|
|
1732
|
-
}
|
|
1733
|
-
message(tag, bodyLength) {
|
|
1734
|
-
this.u8(tag);
|
|
1735
|
-
this.i32(bodyLength + 4);
|
|
1736
|
-
}
|
|
1737
|
-
u8(value) {
|
|
1738
|
-
this.#bytes[this.#offset] = value;
|
|
1739
|
-
this.#offset += 1;
|
|
1740
|
-
}
|
|
1741
|
-
i16(value) {
|
|
1742
|
-
this.#bytes[this.#offset] = (value >>> 8) & 0xff;
|
|
1743
|
-
this.#bytes[this.#offset + 1] = value & 0xff;
|
|
1744
|
-
this.#offset += 2;
|
|
1745
|
-
}
|
|
1746
|
-
i32(value) {
|
|
1747
|
-
this.#bytes[this.#offset] = (value >>> 24) & 0xff;
|
|
1748
|
-
this.#bytes[this.#offset + 1] = (value >>> 16) & 0xff;
|
|
1749
|
-
this.#bytes[this.#offset + 2] = (value >>> 8) & 0xff;
|
|
1750
|
-
this.#bytes[this.#offset + 3] = value & 0xff;
|
|
1751
|
-
this.#offset += 4;
|
|
1752
|
-
}
|
|
1753
|
-
bytes(value) {
|
|
1754
|
-
this.#bytes.set(value, this.#offset);
|
|
1755
|
-
this.#offset += value.length;
|
|
1756
|
-
}
|
|
1757
|
-
finish() {
|
|
1758
|
-
if (this.#offset !== this.#bytes.length) {
|
|
1759
|
-
throw new Error('extended query packet length invariant failed');
|
|
1760
|
-
}
|
|
1761
|
-
return this.#bytes;
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
function toUint8Array(input) {
|
|
1765
|
-
if (input instanceof Uint8Array) {
|
|
1766
|
-
return input;
|
|
1767
|
-
}
|
|
1768
|
-
if (ArrayBuffer.isView(input)) {
|
|
1769
|
-
return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
|
|
1770
|
-
}
|
|
1771
|
-
if (input instanceof ArrayBuffer) {
|
|
1772
|
-
return new Uint8Array(input);
|
|
1773
|
-
}
|
|
1774
|
-
return Uint8Array.from(input);
|
|
1775
|
-
}
|
|
1776
|
-
function parseRowDescription(cursor) {
|
|
1777
|
-
const count = cursor.readI16('RowDescription field count');
|
|
1778
|
-
if (count < 0) {
|
|
1779
|
-
throw new Error(`invalid RowDescription field count ${count}`);
|
|
1780
|
-
}
|
|
1781
|
-
const fields = [];
|
|
1782
|
-
for (let index = 0; index < count; index += 1) {
|
|
1783
|
-
fields.push({
|
|
1784
|
-
name: cursor.readCString('field name'),
|
|
1785
|
-
tableOid: cursor.readU32('field table oid'),
|
|
1786
|
-
tableAttribute: cursor.readI16('field table attribute'),
|
|
1787
|
-
typeOid: cursor.readU32('field type oid'),
|
|
1788
|
-
typeSize: cursor.readI16('field type size'),
|
|
1789
|
-
typeModifier: cursor.readI32('field type modifier'),
|
|
1790
|
-
format: queryFormat(cursor.readI16('field format')),
|
|
1791
|
-
});
|
|
1792
|
-
}
|
|
1793
|
-
return fields;
|
|
1794
|
-
}
|
|
1795
|
-
function parseDataRow(cursor, expectedColumns) {
|
|
1796
|
-
const count = cursor.readI16('DataRow column count');
|
|
1797
|
-
if (count < 0) {
|
|
1798
|
-
throw new Error(`invalid DataRow column count ${count}`);
|
|
1799
|
-
}
|
|
1800
|
-
if (count !== expectedColumns) {
|
|
1801
|
-
throw new Error(`DataRow column count ${count} does not match RowDescription count ${expectedColumns}`);
|
|
1802
|
-
}
|
|
1803
|
-
const values = new Array(count);
|
|
1804
|
-
for (let index = 0; index < count; index += 1) {
|
|
1805
|
-
const length = cursor.readI32('DataRow value length');
|
|
1806
|
-
if (length === -1) {
|
|
1807
|
-
values[index] = null;
|
|
1808
|
-
}
|
|
1809
|
-
else if (length < 0) {
|
|
1810
|
-
throw new Error(`invalid DataRow value length ${length}`);
|
|
1811
|
-
}
|
|
1812
|
-
else {
|
|
1813
|
-
values[index] = cursor.readBytes(length, 'DataRow value');
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
return new ParsedRawQueryRow(values);
|
|
1817
|
-
}
|
|
1818
|
-
function parseErrorResponse(cursor, notices = []) {
|
|
1819
|
-
return new PostgresError(parseDiagnosticFields(cursor, 'ErrorResponse'), notices);
|
|
1820
|
-
}
|
|
1821
|
-
function fieldValue(fields, code) {
|
|
1822
|
-
return fields.find((field) => field.code === code)?.value;
|
|
1823
|
-
}
|
|
1824
|
-
function queryFormat(code) {
|
|
1825
|
-
if (code === 0) {
|
|
1826
|
-
return 'text';
|
|
1827
|
-
}
|
|
1828
|
-
if (code === 1) {
|
|
1829
|
-
return 'binary';
|
|
1830
|
-
}
|
|
1831
|
-
return { code, kind: 'other' };
|
|
1832
|
-
}
|
|
1833
|
-
function hexBackendTag(tag) {
|
|
1834
|
-
return `0x${tag.toString(16).padStart(2, '0')}`;
|
|
1835
|
-
}
|
|
1836
|
-
function parseReadyForQuery(body) {
|
|
1837
|
-
const remaining = body.remainingBytes();
|
|
1838
|
-
if (remaining !== 1) {
|
|
1839
|
-
throw new Error(`ReadyForQuery contained ${remaining} bytes, expected 1`);
|
|
1840
|
-
}
|
|
1841
|
-
const status = body.readU8('ReadyForQuery transaction status');
|
|
1842
|
-
if (status === 0x49)
|
|
1843
|
-
return 'idle';
|
|
1844
|
-
if (status === 0x54)
|
|
1845
|
-
return 'transaction';
|
|
1846
|
-
if (status === 0x45)
|
|
1847
|
-
return 'failed';
|
|
1848
|
-
throw new Error(`ReadyForQuery contained invalid transaction status ${hexBackendTag(status)}`);
|
|
1849
|
-
}
|
|
1850
|
-
function parseParameterDescription(body) {
|
|
1851
|
-
const count = body.readI16('ParameterDescription parameter count');
|
|
1852
|
-
if (count < 0)
|
|
1853
|
-
throw new Error('invalid ParameterDescription parameter count ' + count);
|
|
1854
|
-
const typeOids = [];
|
|
1855
|
-
for (let index = 0; index < count; index += 1) {
|
|
1856
|
-
typeOids.push(body.readU32('ParameterDescription type OID'));
|
|
1857
|
-
}
|
|
1858
|
-
return typeOids;
|
|
1859
|
-
}
|
|
1860
|
-
function parseNoticeResponse(body) {
|
|
1861
|
-
const fields = parseDiagnosticFields(body, 'NoticeResponse');
|
|
1862
|
-
return {
|
|
1863
|
-
severity: fieldValue(fields, 0x53) ?? fieldValue(fields, 0x56),
|
|
1864
|
-
localizedSeverity: fieldValue(fields, 0x53),
|
|
1865
|
-
nonlocalizedSeverity: fieldValue(fields, 0x56),
|
|
1866
|
-
sqlstate: fieldValue(fields, 0x43),
|
|
1867
|
-
message: fieldValue(fields, 0x4d) ?? 'PostgreSQL notice',
|
|
1868
|
-
detail: fieldValue(fields, 0x44),
|
|
1869
|
-
hint: fieldValue(fields, 0x48),
|
|
1870
|
-
position: fieldValue(fields, 0x50),
|
|
1871
|
-
internalPosition: fieldValue(fields, 0x70),
|
|
1872
|
-
internalQuery: fieldValue(fields, 0x71),
|
|
1873
|
-
whereText: fieldValue(fields, 0x57),
|
|
1874
|
-
schemaName: fieldValue(fields, 0x73),
|
|
1875
|
-
tableName: fieldValue(fields, 0x74),
|
|
1876
|
-
columnName: fieldValue(fields, 0x63),
|
|
1877
|
-
dataTypeName: fieldValue(fields, 0x64),
|
|
1878
|
-
constraintName: fieldValue(fields, 0x6e),
|
|
1879
|
-
file: fieldValue(fields, 0x46),
|
|
1880
|
-
line: fieldValue(fields, 0x4c),
|
|
1881
|
-
routine: fieldValue(fields, 0x52),
|
|
1882
|
-
fields,
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
function parseDiagnosticFields(body, label) {
|
|
1886
|
-
const fields = [];
|
|
1887
|
-
for (;;) {
|
|
1888
|
-
if (body.isAtEnd())
|
|
1889
|
-
throw new Error(label + ' is missing terminator');
|
|
1890
|
-
const code = body.readU8(label + ' field code');
|
|
1891
|
-
if (code === 0) {
|
|
1892
|
-
body.requireEnd(label);
|
|
1893
|
-
return fields;
|
|
1894
|
-
}
|
|
1895
|
-
fields.push({ code, value: body.readCString(label + ' field') });
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
function validateParameterStatus(body) {
|
|
1899
|
-
body.readCString('ParameterStatus name');
|
|
1900
|
-
body.readCString('ParameterStatus value');
|
|
1901
|
-
body.requireEnd('ParameterStatus');
|
|
1902
|
-
}
|
|
1903
|
-
function validateNotificationResponse(body) {
|
|
1904
|
-
body.readI32('NotificationResponse process id');
|
|
1905
|
-
body.readCString('NotificationResponse channel');
|
|
1906
|
-
body.readCString('NotificationResponse payload');
|
|
1907
|
-
body.requireEnd('NotificationResponse');
|
|
1908
|
-
}
|
|
1909
|
-
class ByteCursor {
|
|
1910
|
-
#bytes;
|
|
1911
|
-
#offset = 0;
|
|
1912
|
-
constructor(bytes) {
|
|
1913
|
-
this.#bytes = bytes;
|
|
1914
|
-
}
|
|
1915
|
-
isAtEnd() {
|
|
1916
|
-
return this.#offset === this.#bytes.length;
|
|
1917
|
-
}
|
|
1918
|
-
remainingBytes() {
|
|
1919
|
-
return this.#bytes.length - this.#offset;
|
|
1920
|
-
}
|
|
1921
|
-
discardRemaining() {
|
|
1922
|
-
this.#offset = this.#bytes.length;
|
|
1923
|
-
}
|
|
1924
|
-
requireEnd(label) {
|
|
1925
|
-
if (!this.isAtEnd()) {
|
|
1926
|
-
throw new Error(`${label} contained trailing bytes`);
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
readU8(label) {
|
|
1930
|
-
this.#require(1, label);
|
|
1931
|
-
const value = this.#bytes[this.#offset];
|
|
1932
|
-
this.#offset += 1;
|
|
1933
|
-
return value;
|
|
1934
|
-
}
|
|
1935
|
-
readU32(label) {
|
|
1936
|
-
this.#require(4, label);
|
|
1937
|
-
const offset = this.#offset;
|
|
1938
|
-
this.#offset += 4;
|
|
1939
|
-
return ((this.#bytes[offset] * 0x1000000 +
|
|
1940
|
-
(this.#bytes[offset + 1] << 16) +
|
|
1941
|
-
(this.#bytes[offset + 2] << 8) +
|
|
1942
|
-
this.#bytes[offset + 3]) >>>
|
|
1943
|
-
0);
|
|
1944
|
-
}
|
|
1945
|
-
readI32(label) {
|
|
1946
|
-
const value = this.readU32(label);
|
|
1947
|
-
return value > 0x7fffffff ? value - 0x100000000 : value;
|
|
1948
|
-
}
|
|
1949
|
-
readI16(label) {
|
|
1950
|
-
this.#require(2, label);
|
|
1951
|
-
const value = (this.#bytes[this.#offset] << 8) | this.#bytes[this.#offset + 1];
|
|
1952
|
-
this.#offset += 2;
|
|
1953
|
-
return value > 0x7fff ? value - 0x10000 : value;
|
|
1954
|
-
}
|
|
1955
|
-
readCString(label) {
|
|
1956
|
-
const end = this.#bytes.indexOf(0, this.#offset);
|
|
1957
|
-
if (end < 0) {
|
|
1958
|
-
throw new Error(`${label} is missing null terminator`);
|
|
1959
|
-
}
|
|
1960
|
-
const value = decodeUtf8Strict(this.#bytes.subarray(this.#offset, end), label);
|
|
1961
|
-
this.#offset = end + 1;
|
|
1962
|
-
return value;
|
|
1963
|
-
}
|
|
1964
|
-
readBytes(count, label) {
|
|
1965
|
-
this.#require(count, label);
|
|
1966
|
-
const value = this.#bytes.subarray(this.#offset, this.#offset + count);
|
|
1967
|
-
this.#offset += count;
|
|
1968
|
-
return value;
|
|
1969
|
-
}
|
|
1970
|
-
#require(count, label) {
|
|
1971
|
-
if (count < 0 || count > this.#bytes.length - this.#offset) {
|
|
1972
|
-
throw new Error(`truncated ${label}`);
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
function validateCStringBody(bytes, valueLabel, bodyLabel) {
|
|
1977
|
-
const end = bytes.indexOf(0);
|
|
1978
|
-
if (end < 0) {
|
|
1979
|
-
throw new Error(`${valueLabel} is missing null terminator`);
|
|
1980
|
-
}
|
|
1981
|
-
for (let index = 0; index < end; index += 1) {
|
|
1982
|
-
if (bytes[index] < 0x80)
|
|
1983
|
-
continue;
|
|
1984
|
-
validateUtf8(bytes.subarray(0, end), valueLabel);
|
|
1985
|
-
break;
|
|
1986
|
-
}
|
|
1987
|
-
if (end !== bytes.length - 1) {
|
|
1988
|
-
throw new Error(`${bodyLabel} contained trailing bytes`);
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
function decodeUtf8Strict(bytes, label) {
|
|
1992
|
-
try {
|
|
1993
|
-
return utf8Decoder.decode(bytes);
|
|
1994
|
-
}
|
|
1995
|
-
catch {
|
|
1996
|
-
// Keep the precise protocol diagnostic off the valid-data hot path. The
|
|
1997
|
-
// platform decoder performs the usual validation in native code; this
|
|
1998
|
-
// scanner only runs after it has already rejected malformed UTF-8.
|
|
1999
|
-
validateUtf8(bytes, label);
|
|
2000
|
-
throw new Error(`${label} is not valid UTF-8`);
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
function validateUtf8(bytes, label) {
|
|
2004
|
-
let index = 0;
|
|
2005
|
-
while (index < bytes.length) {
|
|
2006
|
-
const first = bytes[index];
|
|
2007
|
-
if (first <= 0x7f) {
|
|
2008
|
-
index += 1;
|
|
2009
|
-
}
|
|
2010
|
-
else if (first >= 0xc2 && first <= 0xdf) {
|
|
2011
|
-
requireContinuation(bytes, index + 1, label);
|
|
2012
|
-
index += 2;
|
|
2013
|
-
}
|
|
2014
|
-
else if (first === 0xe0) {
|
|
2015
|
-
requireRange(bytes, index + 1, 0xa0, 0xbf, label);
|
|
2016
|
-
requireContinuation(bytes, index + 2, label);
|
|
2017
|
-
index += 3;
|
|
2018
|
-
}
|
|
2019
|
-
else if (first >= 0xe1 && first <= 0xec) {
|
|
2020
|
-
requireContinuation(bytes, index + 1, label);
|
|
2021
|
-
requireContinuation(bytes, index + 2, label);
|
|
2022
|
-
index += 3;
|
|
2023
|
-
}
|
|
2024
|
-
else if (first === 0xed) {
|
|
2025
|
-
requireRange(bytes, index + 1, 0x80, 0x9f, label);
|
|
2026
|
-
requireContinuation(bytes, index + 2, label);
|
|
2027
|
-
index += 3;
|
|
2028
|
-
}
|
|
2029
|
-
else if (first >= 0xee && first <= 0xef) {
|
|
2030
|
-
requireContinuation(bytes, index + 1, label);
|
|
2031
|
-
requireContinuation(bytes, index + 2, label);
|
|
2032
|
-
index += 3;
|
|
2033
|
-
}
|
|
2034
|
-
else if (first === 0xf0) {
|
|
2035
|
-
requireRange(bytes, index + 1, 0x90, 0xbf, label);
|
|
2036
|
-
requireContinuation(bytes, index + 2, label);
|
|
2037
|
-
requireContinuation(bytes, index + 3, label);
|
|
2038
|
-
index += 4;
|
|
2039
|
-
}
|
|
2040
|
-
else if (first >= 0xf1 && first <= 0xf3) {
|
|
2041
|
-
requireContinuation(bytes, index + 1, label);
|
|
2042
|
-
requireContinuation(bytes, index + 2, label);
|
|
2043
|
-
requireContinuation(bytes, index + 3, label);
|
|
2044
|
-
index += 4;
|
|
2045
|
-
}
|
|
2046
|
-
else if (first === 0xf4) {
|
|
2047
|
-
requireRange(bytes, index + 1, 0x80, 0x8f, label);
|
|
2048
|
-
requireContinuation(bytes, index + 2, label);
|
|
2049
|
-
requireContinuation(bytes, index + 3, label);
|
|
2050
|
-
index += 4;
|
|
2051
|
-
}
|
|
2052
|
-
else {
|
|
2053
|
-
throw invalidUtf8(label, index);
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
function requireContinuation(bytes, index, label) {
|
|
2058
|
-
requireRange(bytes, index, 0x80, 0xbf, label);
|
|
2059
|
-
}
|
|
2060
|
-
function requireRange(bytes, index, min, max, label) {
|
|
2061
|
-
const byte = bytes[index];
|
|
2062
|
-
if (byte === undefined || byte < min || byte > max) {
|
|
2063
|
-
throw invalidUtf8(label, index);
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
function invalidUtf8(label, index) {
|
|
2067
|
-
return new Error(`${label} is not valid UTF-8 at byte ${index}`);
|
|
2068
|
-
}
|