@descryy/runtime-database-observation 0.5.2 → 0.5.3
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/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/mysql-row-collector.d.ts +33 -19
- package/dist/mysql-row-collector.d.ts.map +1 -1
- package/dist/mysql-row-collector.js +480 -26
- package/dist/mysql-row-collector.js.map +1 -1
- package/dist/postgres-log-parser.d.ts +15 -29
- package/dist/postgres-log-parser.d.ts.map +1 -1
- package/dist/postgres-log-parser.js +103 -32
- package/dist/postgres-log-parser.js.map +1 -1
- package/dist/postgres-log-source.d.ts +2 -0
- package/dist/postgres-log-source.d.ts.map +1 -1
- package/dist/postgres-log-source.js +171 -35
- package/dist/postgres-log-source.js.map +1 -1
- package/dist/postgres-query-collector.d.ts.map +1 -1
- package/dist/postgres-query-collector.js +43 -13
- package/dist/postgres-query-collector.js.map +1 -1
- package/dist/postgres-row-collector.d.ts +7 -0
- package/dist/postgres-row-collector.d.ts.map +1 -1
- package/dist/postgres-row-collector.js +112 -17
- package/dist/postgres-row-collector.js.map +1 -1
- package/dist/prisma-instrumentation.d.ts +20 -0
- package/dist/prisma-instrumentation.d.ts.map +1 -0
- package/dist/prisma-instrumentation.js +99 -0
- package/dist/prisma-instrumentation.js.map +1 -0
- package/dist/query-collector.d.ts +2 -0
- package/dist/query-collector.d.ts.map +1 -1
- package/dist/query-collector.js +38 -13
- package/dist/query-collector.js.map +1 -1
- package/dist/query-line-parser.d.ts.map +1 -1
- package/dist/query-line-parser.js +2 -1
- package/dist/query-line-parser.js.map +1 -1
- package/dist/query-marker.d.ts +10 -1
- package/dist/query-marker.d.ts.map +1 -1
- package/dist/query-marker.js +2 -0
- package/dist/query-marker.js.map +1 -1
- package/dist/row-capture-policy.d.ts +31 -0
- package/dist/row-capture-policy.d.ts.map +1 -0
- package/dist/row-capture-policy.js +95 -0
- package/dist/row-capture-policy.js.map +1 -0
- package/dist/smoke.d.ts.map +1 -1
- package/dist/smoke.js +2 -0
- package/dist/smoke.js.map +1 -1
- package/dist/sqlite-instrumentation.js +9 -2
- package/dist/sqlite-instrumentation.js.map +1 -1
- package/dist/sqlite-row-snapshot.d.ts +4 -0
- package/dist/sqlite-row-snapshot.d.ts.map +1 -1
- package/dist/sqlite-row-snapshot.js +153 -25
- package/dist/sqlite-row-snapshot.js.map +1 -1
- package/dist/table-extraction.d.ts.map +1 -1
- package/dist/table-extraction.js +124 -5
- package/dist/table-extraction.js.map +1 -1
- package/package.json +9 -8
|
@@ -1,31 +1,277 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { COLLECTOR_VERSION } from "./collector-version.js";
|
|
10
|
+
import { approximateCellBytes, MAX_SNAPSHOT_BYTES, mergeTruncated, mergeWithheld, resolveRowCapturePolicy } from "./row-capture-policy.js";
|
|
11
|
+
import { diffTableSnapshot } from "./sqlite-row-snapshot.js";
|
|
12
|
+
export const MYSQL_ROW_CAPTURE_REFUSAL_REASON = 'MySQL row-change capture needs the optional dependency "mysql2", which is not installed next to ' +
|
|
13
|
+
"@descryy/runtime-database-observation; install it there to capture rows. The query-text channel is unaffected.";
|
|
14
|
+
const EVENT_TYPE = "DATABASE_QUERY";
|
|
15
|
+
const DEFAULT_ROW_CAP = 10_000;
|
|
16
|
+
const DEFAULT_STATEMENT_TIMEOUT_MS = 30_000;
|
|
17
|
+
const DEFAULT_SNAPSHOT_DEADLINE_MS = 120_000;
|
|
18
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
19
|
+
const KEY_LOOKUP_CHUNK = 500;
|
|
20
|
+
export class MysqlDriverMissingError extends Error {
|
|
21
|
+
}
|
|
22
|
+
export function mysqlPromiseModule(loaded) {
|
|
23
|
+
const module = loaded;
|
|
24
|
+
if (typeof module?.createConnection === "function")
|
|
25
|
+
return module;
|
|
26
|
+
if (typeof module?.default?.createConnection === "function")
|
|
27
|
+
return module.default;
|
|
28
|
+
throw new Error('"mysql2/promise" was loaded but exposes no createConnection, at its top level or on its default export');
|
|
29
|
+
}
|
|
30
|
+
export async function loadMysqlPromiseModule() {
|
|
31
|
+
const moduleName = "mysql2/promise";
|
|
32
|
+
let loaded;
|
|
33
|
+
try {
|
|
34
|
+
loaded = await import(__rewriteRelativeImportExtension(moduleName));
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const code = error?.code;
|
|
38
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND")
|
|
39
|
+
throw new MysqlDriverMissingError(MYSQL_ROW_CAPTURE_REFUSAL_REASON);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
return mysqlPromiseModule(loaded);
|
|
43
|
+
}
|
|
44
|
+
export async function defaultClientFactory(connectionString, statementTimeoutMs) {
|
|
45
|
+
const mysql = await loadMysqlPromiseModule();
|
|
46
|
+
const connection = await mysql.createConnection({
|
|
47
|
+
uri: connectionString,
|
|
48
|
+
dateStrings: true,
|
|
49
|
+
supportBigNumbers: true,
|
|
50
|
+
bigNumberStrings: true,
|
|
51
|
+
jsonStrings: true,
|
|
52
|
+
multipleStatements: false,
|
|
53
|
+
connectTimeout: CONNECT_TIMEOUT_MS,
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
async query(sql, params = []) {
|
|
57
|
+
const [rows] = await connection.query({ sql, values: params, timeout: statementTimeoutMs });
|
|
58
|
+
return Array.isArray(rows) ? rows : [];
|
|
59
|
+
},
|
|
60
|
+
end() {
|
|
61
|
+
return connection.end();
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function describeMysqlTarget(connectionString) {
|
|
66
|
+
try {
|
|
67
|
+
const url = new URL(connectionString);
|
|
68
|
+
const database = url.pathname.replace(/^\//, "");
|
|
69
|
+
return `${url.hostname}${url.port === "" ? "" : `:${url.port}`}${database === "" ? "" : `/${database}`}`;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return "the configured MySQL server";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function escapedForRegExp(text) {
|
|
76
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
77
|
+
}
|
|
78
|
+
function withoutSecret(message, secret) {
|
|
79
|
+
if (secret === "")
|
|
80
|
+
return message;
|
|
81
|
+
if (secret.length >= 3)
|
|
82
|
+
return message.split(secret).join("[REDACTED]");
|
|
83
|
+
return message.replace(new RegExp(`(?<![A-Za-z0-9])${escapedForRegExp(secret)}(?![A-Za-z0-9])`, "g"), "[REDACTED]");
|
|
84
|
+
}
|
|
85
|
+
export function scrubConnectionSecrets(message, connectionString) {
|
|
86
|
+
let scrubbed = message.split(connectionString).join(describeMysqlTarget(connectionString));
|
|
87
|
+
scrubbed = scrubbed.replace(/(\/\/)[^/\s@]*@/g, "$1[REDACTED]@");
|
|
88
|
+
scrubbed = scrubbed.replace(/(\buser\s+)'[^']*'(?:@'[^']*')?/gi, "$1[REDACTED]");
|
|
89
|
+
let password;
|
|
90
|
+
try {
|
|
91
|
+
password = new URL(connectionString).password;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return scrubbed;
|
|
95
|
+
}
|
|
96
|
+
const variants = new Set([password]);
|
|
97
|
+
try {
|
|
98
|
+
variants.add(decodeURIComponent(password));
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
variants.add(password);
|
|
102
|
+
}
|
|
103
|
+
for (const secret of variants)
|
|
104
|
+
scrubbed = withoutSecret(scrubbed, secret);
|
|
105
|
+
return scrubbed;
|
|
106
|
+
}
|
|
107
|
+
export function quoteMysqlIdentifier(name) {
|
|
108
|
+
return `\`${name.replace(/`/g, "``")}\``;
|
|
109
|
+
}
|
|
110
|
+
export function normaliseMysqlValue(value) {
|
|
111
|
+
if (value === null || value === undefined)
|
|
112
|
+
return null;
|
|
113
|
+
if (Buffer.isBuffer(value))
|
|
114
|
+
return `\\x${value.toString("hex")}`;
|
|
115
|
+
if (value instanceof Uint8Array)
|
|
116
|
+
return `\\x${Buffer.from(value).toString("hex")}`;
|
|
117
|
+
if (typeof value === "bigint")
|
|
118
|
+
return value.toString();
|
|
119
|
+
if (value instanceof Date)
|
|
120
|
+
return Number.isNaN(value.getTime()) ? String(value) : value.toISOString();
|
|
121
|
+
if (typeof value === "object")
|
|
122
|
+
return JSON.stringify(value);
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
function normaliseRow(row) {
|
|
126
|
+
const normalised = {};
|
|
127
|
+
for (const [column, value] of Object.entries(row))
|
|
128
|
+
normalised[column] = normaliseMysqlValue(value);
|
|
129
|
+
return normalised;
|
|
130
|
+
}
|
|
131
|
+
function keyOf(row, keyColumns) {
|
|
132
|
+
return JSON.stringify(keyColumns.map((column) => normaliseMysqlValue(row[column])));
|
|
133
|
+
}
|
|
134
|
+
function rowTuple(columns) {
|
|
135
|
+
return columns.length === 1 ? quoteMysqlIdentifier(columns[0] ?? "") : `(${columns.map(quoteMysqlIdentifier).join(", ")})`;
|
|
136
|
+
}
|
|
137
|
+
function placeholderTuple(count) {
|
|
138
|
+
return count === 1 ? "?" : `(${new Array(count).fill("?").join(", ")})`;
|
|
139
|
+
}
|
|
140
|
+
async function readWindow(client, table, rowCap, upTo) {
|
|
141
|
+
const order = table.keyColumns.map(quoteMysqlIdentifier).join(", ");
|
|
142
|
+
const where = upTo === null ? "" : ` WHERE ${rowTuple(table.keyColumns)} <= ${placeholderTuple(table.keyColumns.length)}`;
|
|
143
|
+
const rows = await client.query(`SELECT * FROM ${quoteMysqlIdentifier(table.name)}${where} ORDER BY ${order} LIMIT ?`, [...(upTo ?? []), rowCap + 1]);
|
|
144
|
+
const truncated = rows.length > rowCap;
|
|
145
|
+
const bounded = truncated ? rows.slice(0, rowCap) : rows;
|
|
146
|
+
const rowsByRowid = new Map();
|
|
147
|
+
const rawKeys = new Map();
|
|
148
|
+
let lastKey = null;
|
|
149
|
+
for (const row of bounded) {
|
|
150
|
+
const key = keyOf(row, table.keyColumns);
|
|
151
|
+
lastKey = table.keyColumns.map((column) => row[column]);
|
|
152
|
+
rowsByRowid.set(key, normaliseRow(row));
|
|
153
|
+
rawKeys.set(key, lastKey);
|
|
154
|
+
}
|
|
155
|
+
let approximateBytes = 0;
|
|
156
|
+
for (const normalisedRow of rowsByRowid.values()) {
|
|
157
|
+
for (const cell of Object.values(normalisedRow))
|
|
158
|
+
approximateBytes += approximateCellBytes(cell);
|
|
159
|
+
}
|
|
160
|
+
return { snapshot: { rowsByRowid, truncated, approximateBytes }, rawKeys, lastKey };
|
|
161
|
+
}
|
|
162
|
+
async function readByKeys(client, table, keys) {
|
|
163
|
+
const found = new Map();
|
|
164
|
+
for (let start = 0; start < keys.length; start += KEY_LOOKUP_CHUNK) {
|
|
165
|
+
const chunk = keys.slice(start, start + KEY_LOOKUP_CHUNK);
|
|
166
|
+
const tuples = chunk.map(() => placeholderTuple(table.keyColumns.length)).join(", ");
|
|
167
|
+
const rows = await client.query(`SELECT * FROM ${quoteMysqlIdentifier(table.name)} WHERE ${rowTuple(table.keyColumns)} IN (${tuples})`, chunk.flat());
|
|
168
|
+
for (const row of rows)
|
|
169
|
+
found.set(keyOf(row, table.keyColumns), normaliseRow(row));
|
|
170
|
+
}
|
|
171
|
+
return found;
|
|
172
|
+
}
|
|
173
|
+
export function diffAgainstKnownRows(table, before, currentOfBefore, afterWindow) {
|
|
174
|
+
const restrictedAfter = new Map();
|
|
175
|
+
for (const [key, row] of currentOfBefore)
|
|
176
|
+
restrictedAfter.set(key, row);
|
|
177
|
+
for (const [key, row] of afterWindow.rowsByRowid) {
|
|
178
|
+
if (!before.rowsByRowid.has(key))
|
|
179
|
+
restrictedAfter.set(key, row);
|
|
180
|
+
}
|
|
181
|
+
return diffTableSnapshot(table, before, { rowsByRowid: restrictedAfter, truncated: true });
|
|
182
|
+
}
|
|
183
|
+
async function listKeyedTables(client, policy, schema) {
|
|
184
|
+
const tableRows = await client.query("SELECT TABLE_NAME AS name FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME");
|
|
185
|
+
const listed = tableRows.map((row) => String(row["name"]));
|
|
186
|
+
const present = new Set(listed.map((name) => name.toLowerCase()));
|
|
187
|
+
const missingAllowlisted = (policy.tables ?? []).filter((name) => !present.has(name) && !present.has(name.replace(`${schema.toLowerCase()}.`, "")));
|
|
188
|
+
const wanted = listed.filter((name) => policy.allows(name, schema));
|
|
189
|
+
const indexRows = await client.query("SELECT s.TABLE_NAME AS table_name, s.INDEX_NAME AS index_name, s.COLUMN_NAME AS column_name, s.SUB_PART AS sub_part, " +
|
|
190
|
+
"c.IS_NULLABLE AS nullable FROM information_schema.STATISTICS s " +
|
|
191
|
+
"LEFT JOIN information_schema.COLUMNS c ON c.TABLE_SCHEMA = s.TABLE_SCHEMA AND c.TABLE_NAME = s.TABLE_NAME AND c.COLUMN_NAME = s.COLUMN_NAME " +
|
|
192
|
+
"WHERE s.TABLE_SCHEMA = DATABASE() AND s.NON_UNIQUE = 0 ORDER BY s.TABLE_NAME, s.INDEX_NAME, s.SEQ_IN_INDEX");
|
|
193
|
+
const indexes = new Map();
|
|
194
|
+
for (const row of indexRows) {
|
|
195
|
+
const tableName = String(row["table_name"]);
|
|
196
|
+
const indexName = String(row["index_name"]);
|
|
197
|
+
const byIndex = indexes.get(tableName) ?? new Map();
|
|
198
|
+
const entry = byIndex.get(indexName) ?? { columns: [], usable: true };
|
|
199
|
+
const column = row["column_name"];
|
|
200
|
+
const prefixOnly = row["sub_part"] !== null && row["sub_part"] !== undefined;
|
|
201
|
+
const nullable = String(row["nullable"] ?? "YES").toUpperCase() === "YES";
|
|
202
|
+
if (column === null || column === undefined || prefixOnly || nullable)
|
|
203
|
+
entry.usable = false;
|
|
204
|
+
if (column !== null && column !== undefined)
|
|
205
|
+
entry.columns.push(String(column));
|
|
206
|
+
byIndex.set(indexName, entry);
|
|
207
|
+
indexes.set(tableName, byIndex);
|
|
208
|
+
}
|
|
209
|
+
const keyed = [];
|
|
210
|
+
const unkeyed = [];
|
|
211
|
+
for (const name of wanted) {
|
|
212
|
+
const byIndex = indexes.get(name);
|
|
213
|
+
const primary = byIndex?.get("PRIMARY");
|
|
214
|
+
const fallback = [...(byIndex?.entries() ?? [])]
|
|
215
|
+
.filter(([indexName, entry]) => indexName !== "PRIMARY" && entry.usable && entry.columns.length > 0)
|
|
216
|
+
.sort(([a], [b]) => a.localeCompare(b))[0]?.[1];
|
|
217
|
+
const key = primary !== undefined && primary.usable && primary.columns.length > 0 ? primary : fallback;
|
|
218
|
+
if (key === undefined) {
|
|
219
|
+
unkeyed.push(name);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
keyed.push({ name, keyColumns: key.columns });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { keyed, unkeyed, missingAllowlisted };
|
|
226
|
+
}
|
|
22
227
|
export function createMysqlRowChangeCollector(options) {
|
|
23
228
|
const processId = options.processId ?? "mysql-row-changes";
|
|
229
|
+
const rowCap = options.rowCap ?? DEFAULT_ROW_CAP;
|
|
230
|
+
const statementTimeoutMs = options.statementTimeoutMs ?? DEFAULT_STATEMENT_TIMEOUT_MS;
|
|
231
|
+
const snapshotDeadlineMs = options.snapshotDeadlineMs ?? DEFAULT_SNAPSHOT_DEADLINE_MS;
|
|
232
|
+
const clientFactory = options.clientFactory ?? defaultClientFactory;
|
|
233
|
+
const target = describeMysqlTarget(options.connectionString);
|
|
234
|
+
const policyResolution = resolveRowCapturePolicy(options.rowCapture);
|
|
235
|
+
const rowPolicy = policyResolution.ok ? policyResolution.policy : null;
|
|
236
|
+
let client = null;
|
|
237
|
+
let savedContext = null;
|
|
238
|
+
let refusalReason = null;
|
|
239
|
+
let schema = "";
|
|
240
|
+
let tables = [];
|
|
241
|
+
let unkeyedTables = [];
|
|
242
|
+
let unreadableTables = [];
|
|
243
|
+
let outOfTimeTables = [];
|
|
244
|
+
let missingAllowlisted = [];
|
|
245
|
+
let partialInsertTables = new Set();
|
|
246
|
+
let beforeWindows = new Map();
|
|
247
|
+
function describeError(error) {
|
|
248
|
+
const detail = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
249
|
+
return scrubConnectionSecrets(detail, options.connectionString);
|
|
250
|
+
}
|
|
24
251
|
function capabilities() {
|
|
25
252
|
const unrelated = {
|
|
26
253
|
availability: "unavailable",
|
|
27
254
|
reason: "MysqlRowChangeCollector observes row-level DATABASE_QUERY (row-change) events only.",
|
|
28
255
|
};
|
|
256
|
+
const databaseObservation = refusalReason !== null
|
|
257
|
+
? { availability: "unavailable", reason: refusalReason }
|
|
258
|
+
: {
|
|
259
|
+
availability: "available",
|
|
260
|
+
reason: `row-level INSERT/UPDATE/DELETE on ${target} via a read-only before/after snapshot diff keyed by each table's ` +
|
|
261
|
+
`primary key (or a NOT NULL unique key), capped at ${String(rowCap)} rows per table. A row changed and changed back ` +
|
|
262
|
+
"inside the window is not seen, several updates to one row arrive as one, and a key change arrives as a DELETE " +
|
|
263
|
+
"plus an INSERT. Tables over the cap are diffed for the rows the first snapshot held and disclosed per row as " +
|
|
264
|
+
"truncated: true; rows inserted past that window are not observed." +
|
|
265
|
+
(unkeyedTables.length > 0 ? ` Skipped, no primary or NOT NULL unique key to diff by: ${unkeyedTables.join(", ")}.` : "") +
|
|
266
|
+
(unreadableTables.length > 0 ? ` Skipped, could not be read: ${unreadableTables.join(", ")}.` : "") +
|
|
267
|
+
(outOfTimeTables.length > 0
|
|
268
|
+
? ` Skipped, the ${String(snapshotDeadlineMs)} ms snapshot deadline or the ${String(MAX_SNAPSHOT_BYTES)} byte snapshot budget ran out before them: ${outOfTimeTables.join(", ")}.`
|
|
269
|
+
: "") +
|
|
270
|
+
(partialInsertTables.size > 0
|
|
271
|
+
? ` More rows than the cap changed in: ${[...partialInsertTables].join(", ")}; their inserts are partial.`
|
|
272
|
+
: "") +
|
|
273
|
+
` ${rowPolicy?.describe(missingAllowlisted) ?? ""}`,
|
|
274
|
+
};
|
|
29
275
|
return {
|
|
30
276
|
domObservation: unrelated,
|
|
31
277
|
consoleObservation: unrelated,
|
|
@@ -35,17 +281,225 @@ export function createMysqlRowChangeCollector(options) {
|
|
|
35
281
|
sourceMapping: unrelated,
|
|
36
282
|
stackCapture: unrelated,
|
|
37
283
|
processLifecycle: unrelated,
|
|
38
|
-
databaseObservation
|
|
284
|
+
databaseObservation,
|
|
39
285
|
externalServiceObservation: unrelated,
|
|
40
286
|
};
|
|
41
287
|
}
|
|
288
|
+
function emitDiff(context, diff) {
|
|
289
|
+
const before = rowPolicy?.filterRow(diff.before) ?? { row: diff.before, withheld: [], truncated: [] };
|
|
290
|
+
const after = rowPolicy?.filterRow(diff.after) ?? { row: diff.after, withheld: [], truncated: [] };
|
|
291
|
+
const withheldColumns = mergeWithheld(before.withheld, after.withheld);
|
|
292
|
+
const truncatedColumns = mergeTruncated(before.truncated, after.truncated);
|
|
293
|
+
context.emit({
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
source: "backend-process",
|
|
296
|
+
service: options.service ?? null,
|
|
297
|
+
process: processId,
|
|
298
|
+
eventType: EVENT_TYPE,
|
|
299
|
+
payload: {
|
|
300
|
+
kind: "row-change",
|
|
301
|
+
schema,
|
|
302
|
+
table: diff.table,
|
|
303
|
+
op: diff.op,
|
|
304
|
+
before: before.row,
|
|
305
|
+
after: after.row,
|
|
306
|
+
...(withheldColumns.length > 0 ? { withheldColumns } : {}),
|
|
307
|
+
...(truncatedColumns.length > 0 ? { truncatedColumns } : {}),
|
|
308
|
+
beforeCompleteness: "full",
|
|
309
|
+
truncated: diff.truncated,
|
|
310
|
+
},
|
|
311
|
+
traceId: null,
|
|
312
|
+
requestId: null,
|
|
313
|
+
correlationId: null,
|
|
314
|
+
graphNodeId: null,
|
|
315
|
+
sourceLocation: null,
|
|
316
|
+
stackTrace: null,
|
|
317
|
+
confidence: 1,
|
|
318
|
+
redactionStatus: "pending-redaction",
|
|
319
|
+
collectorVersion: COLLECTOR_VERSION,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function emitCollectorError(context, detail) {
|
|
323
|
+
try {
|
|
324
|
+
context.emit({
|
|
325
|
+
timestamp: new Date().toISOString(),
|
|
326
|
+
source: "backend-process",
|
|
327
|
+
service: options.service ?? null,
|
|
328
|
+
process: processId,
|
|
329
|
+
eventType: "COLLECTOR_ERROR",
|
|
330
|
+
payload: {
|
|
331
|
+
raw: `mysql row-change collector for process "${processId}" failed: ${detail}`,
|
|
332
|
+
collectorId: `mysql-row-collector:${processId}`,
|
|
333
|
+
error: detail,
|
|
334
|
+
},
|
|
335
|
+
traceId: null,
|
|
336
|
+
requestId: null,
|
|
337
|
+
correlationId: null,
|
|
338
|
+
graphNodeId: null,
|
|
339
|
+
sourceLocation: null,
|
|
340
|
+
stackTrace: null,
|
|
341
|
+
confidence: 1,
|
|
342
|
+
redactionStatus: "pending-redaction",
|
|
343
|
+
collectorVersion: COLLECTOR_VERSION,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
catch (emitError) {
|
|
347
|
+
console.error(`[mysql-row-collector:${processId}] failed (${detail}) AND the failure could not be emitted: ${String(emitError)}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async function closeClient() {
|
|
351
|
+
const active = client;
|
|
352
|
+
client = null;
|
|
353
|
+
if (active === null)
|
|
354
|
+
return;
|
|
355
|
+
try {
|
|
356
|
+
await active.end();
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
if (savedContext !== null)
|
|
360
|
+
emitCollectorError(savedContext, `closing the database connection failed (${describeError(error)})`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async function inReadOnlySnapshot(active, body) {
|
|
364
|
+
await active.query("START TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY");
|
|
365
|
+
try {
|
|
366
|
+
return await body();
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
await active.query("COMMIT").catch((error) => {
|
|
370
|
+
if (savedContext !== null)
|
|
371
|
+
emitCollectorError(savedContext, `ending the read-only snapshot transaction failed (${describeError(error)})`);
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
async function diffTable(active, table, before) {
|
|
376
|
+
const upTo = before.snapshot.truncated ? before.lastKey : null;
|
|
377
|
+
const after = await readWindow(active, table, rowCap, upTo);
|
|
378
|
+
if (!after.snapshot.truncated) {
|
|
379
|
+
const diffs = diffTableSnapshot(table.name, before.snapshot, after.snapshot);
|
|
380
|
+
return before.snapshot.truncated ? diffs.map((diff) => ({ ...diff, truncated: true })) : diffs;
|
|
381
|
+
}
|
|
382
|
+
partialInsertTables.add(table.name);
|
|
383
|
+
const current = await readByKeys(active, table, [...before.rawKeys.values()]);
|
|
384
|
+
return diffAgainstKnownRows(table.name, before.snapshot, current, after.snapshot);
|
|
385
|
+
}
|
|
42
386
|
return {
|
|
43
387
|
collectorId: `mysql-row-collector:${processId}`,
|
|
44
|
-
start(
|
|
45
|
-
|
|
388
|
+
async start(context) {
|
|
389
|
+
savedContext = context;
|
|
390
|
+
refusalReason = null;
|
|
391
|
+
if (!policyResolution.ok || rowPolicy === null) {
|
|
392
|
+
refusalReason = policyResolution.ok ? "row capture policy did not resolve" : policyResolution.reason;
|
|
393
|
+
return { available: false, reason: refusalReason };
|
|
394
|
+
}
|
|
395
|
+
if (!Number.isInteger(rowCap) || rowCap < 1) {
|
|
396
|
+
refusalReason = `rowCap must be a positive integer, got ${String(rowCap)}.`;
|
|
397
|
+
return { available: false, reason: refusalReason };
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
client = await clientFactory(options.connectionString, statementTimeoutMs);
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
refusalReason =
|
|
404
|
+
error instanceof MysqlDriverMissingError
|
|
405
|
+
? MYSQL_ROW_CAPTURE_REFUSAL_REASON
|
|
406
|
+
: `could not connect to ${target} for row-level capture (${describeError(error)}); the query-text channel is unaffected.`;
|
|
407
|
+
client = null;
|
|
408
|
+
return { available: false, reason: refusalReason };
|
|
409
|
+
}
|
|
410
|
+
const active = client;
|
|
411
|
+
try {
|
|
412
|
+
const databaseRows = await active.query("SELECT DATABASE() AS name");
|
|
413
|
+
const current = databaseRows[0]?.["name"];
|
|
414
|
+
if (current === null || current === undefined || String(current) === "") {
|
|
415
|
+
refusalReason =
|
|
416
|
+
`the connection string for ${target} names no database, so there is no table set to capture; ` +
|
|
417
|
+
"add the database to the connection string's path.";
|
|
418
|
+
await closeClient();
|
|
419
|
+
return { available: false, reason: refusalReason };
|
|
420
|
+
}
|
|
421
|
+
schema = String(current);
|
|
422
|
+
await active.query("SET SESSION TRANSACTION READ ONLY");
|
|
423
|
+
const listed = await listKeyedTables(active, rowPolicy, schema);
|
|
424
|
+
tables = listed.keyed;
|
|
425
|
+
unkeyedTables = listed.unkeyed;
|
|
426
|
+
missingAllowlisted = listed.missingAllowlisted;
|
|
427
|
+
unreadableTables = [];
|
|
428
|
+
outOfTimeTables = [];
|
|
429
|
+
partialInsertTables = new Set();
|
|
430
|
+
beforeWindows = new Map();
|
|
431
|
+
const deadlineAt = Date.now() + snapshotDeadlineMs;
|
|
432
|
+
let snapshotBytes = 0;
|
|
433
|
+
await inReadOnlySnapshot(active, async () => {
|
|
434
|
+
for (const table of tables) {
|
|
435
|
+
if (Date.now() > deadlineAt || snapshotBytes > MAX_SNAPSHOT_BYTES) {
|
|
436
|
+
outOfTimeTables.push(table.name);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
const window = await readWindow(active, table, rowCap, null);
|
|
441
|
+
snapshotBytes += window.snapshot.approximateBytes ?? 0;
|
|
442
|
+
beforeWindows.set(table.name, window);
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
unreadableTables.push(`${table.name} (${describeError(error)})`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
return { available: true };
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
refusalReason = `could not start row-level capture on ${target}: ${describeError(error)}`;
|
|
453
|
+
await closeClient();
|
|
454
|
+
return { available: false, reason: refusalReason };
|
|
455
|
+
}
|
|
46
456
|
},
|
|
47
|
-
stop() {
|
|
48
|
-
|
|
457
|
+
async stop() {
|
|
458
|
+
const active = client;
|
|
459
|
+
if (active === null)
|
|
460
|
+
return;
|
|
461
|
+
const context = savedContext;
|
|
462
|
+
try {
|
|
463
|
+
const diffs = [];
|
|
464
|
+
const deadlineAt = Date.now() + snapshotDeadlineMs;
|
|
465
|
+
let snapshotBytes = 0;
|
|
466
|
+
await inReadOnlySnapshot(active, async () => {
|
|
467
|
+
for (const table of tables) {
|
|
468
|
+
const before = beforeWindows.get(table.name);
|
|
469
|
+
if (before === undefined)
|
|
470
|
+
continue;
|
|
471
|
+
if (Date.now() > deadlineAt || snapshotBytes > MAX_SNAPSHOT_BYTES) {
|
|
472
|
+
outOfTimeTables.push(table.name);
|
|
473
|
+
if (context !== null)
|
|
474
|
+
emitCollectorError(context, `table "${table.name}" was not compared: the closing snapshot ran out of its ${String(snapshotDeadlineMs)} ms deadline or byte budget`);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
diffs.push(...(await diffTable(active, table, before)));
|
|
479
|
+
snapshotBytes += before.snapshot.approximateBytes ?? 0;
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
unreadableTables.push(`${table.name} (${describeError(error)})`);
|
|
483
|
+
if (context !== null)
|
|
484
|
+
emitCollectorError(context, `table "${table.name}" could not be read when the run ended, so its row changes were not captured (${describeError(error)})`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
if (context !== null) {
|
|
489
|
+
for (const diff of diffs)
|
|
490
|
+
emitDiff(context, diff);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
const detail = describeError(error);
|
|
495
|
+
refusalReason = `could not read the closing snapshot from ${target}: ${detail}`;
|
|
496
|
+
if (context !== null)
|
|
497
|
+
emitCollectorError(context, detail);
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
beforeWindows = new Map();
|
|
501
|
+
await closeClient();
|
|
502
|
+
}
|
|
49
503
|
},
|
|
50
504
|
capabilities,
|
|
51
505
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql-row-collector.js","sourceRoot":"","sources":["../src/mysql-row-collector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,MAAM,CAAC,MAAM,gCAAgC,GAC3C,kHAAkH;IAClH,6EAA6E,CAAC;AAEhF,MAAM,UAAU,6BAA6B,CAAC,OAAuC;IACnF,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,mBAAmB,CAAC;IAE3D,SAAS,YAAY;QACnB,MAAM,SAAS,GAAG;YAChB,YAAY,EAAE,aAAsB;YACpC,MAAM,EAAE,qFAAqF;SAC9F,CAAC;QACF,OAAO;YACL,cAAc,EAAE,SAAS;YACzB,kBAAkB,EAAE,SAAS;YAC7B,kBAAkB,EAAE,SAAS;YAC7B,gBAAgB,EAAE,SAAS;YAC3B,gBAAgB,EAAE,SAAS;YAC3B,aAAa,EAAE,SAAS;YACxB,YAAY,EAAE,SAAS;YACvB,gBAAgB,EAAE,SAAS;YAC3B,mBAAmB,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,gCAAgC,EAAE;YAC9F,0BAA0B,EAAE,SAAS;SACtC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,WAAW,EAAE,uBAAuB,SAAS,EAAE;QAE/C,KAAK,CAAC,QAA0B;YAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gCAAgC,EAAE,CAAC,CAAC;QACzF,CAAC;QAED,IAAI;YACF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,YAAY;KACb,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"mysql-row-collector.js","sourceRoot":"","sources":["../src/mysql-row-collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,44 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RG-5(b): Postgres needs no preload — its own server-side statement log is
|
|
3
3
|
* the capture mechanism, the way the plan calls for ("its logging path
|
|
4
|
-
* needs no preload"). This reads
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* concern), and recognises exactly two shapes `log_statement = 'all'`
|
|
8
|
-
* produces:
|
|
9
|
-
*
|
|
10
|
-
* - `LOG: statement: <sql>` — the simple-query protocol.
|
|
11
|
-
* - `LOG: execute <name>: <sql>` — the extended/prepared-statement
|
|
12
|
-
* protocol (`<name>` is `<unnamed>` for an unnamed prepared statement, or
|
|
13
|
-
* a real name).
|
|
4
|
+
* needs no preload"). This reads that log line by line, whatever
|
|
5
|
+
* `log_line_prefix` a deployment configured (timestamp, pid, database name
|
|
6
|
+
* — all deployment-specific and none of them this parser's concern).
|
|
14
7
|
*
|
|
15
8
|
* Verified against PostgreSQL's own documented log format (`log_statement`,
|
|
16
9
|
* `log_line_prefix`), not guessed, and now also **live-tested against a
|
|
17
10
|
* real `postgres:16` Docker container** producing real `LOG: statement:`
|
|
18
11
|
* lines (`postgres-query-collector.ts`'s `test/postgres-live.test.ts`) —
|
|
19
12
|
* not just doc-verified syntax fed in by hand.
|
|
20
|
-
*
|
|
21
|
-
* **Disclosed, deliberate scope, same "refuse rather than guess" rule
|
|
22
|
-
* `query-line-parser.ts` and `table-extraction.ts` already apply:**
|
|
23
|
-
* - A statement split across multiple log lines (a long query Postgres
|
|
24
|
-
* wraps) is read as written on its own line only — refused, not
|
|
25
|
-
* reassembled, same as this package's other parsers stay bounded readers.
|
|
26
|
-
* - `duration` is always `null` here: Postgres logs it on a **separate**
|
|
27
|
-
* `LOG: duration: N ms` line, joined to the statement line only via
|
|
28
|
-
* `log_duration`/`log_min_duration_statement` and the same backend pid —
|
|
29
|
-
* a real correlation this parser does not attempt, so it reports what it
|
|
30
|
-
* does not know rather than a fabricated duration.
|
|
31
|
-
* - `success` is always `true` here: a failed statement logs as `ERROR:`,
|
|
32
|
-
* a different prefix this parser does not recognise — refused (`null`),
|
|
33
|
-
* not misread as a successful query.
|
|
34
13
|
*/
|
|
14
|
+
export declare const MAX_STATEMENT_CHARS: number;
|
|
35
15
|
export interface PostgresLogQuery {
|
|
36
16
|
readonly sql: string;
|
|
37
|
-
readonly method: "statement" | "execute";
|
|
38
|
-
readonly durationMs: null;
|
|
39
|
-
readonly success:
|
|
40
|
-
readonly
|
|
17
|
+
readonly method: "statement" | "execute" | "failed-statement";
|
|
18
|
+
readonly durationMs: number | null;
|
|
19
|
+
readonly success: boolean | null;
|
|
20
|
+
readonly outcomeKnown: boolean;
|
|
21
|
+
readonly error: string | null;
|
|
22
|
+
readonly sqlTruncated: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface PostgresLogReader {
|
|
25
|
+
push(line: string): readonly PostgresLogQuery[];
|
|
26
|
+
flush(): readonly PostgresLogQuery[];
|
|
41
27
|
}
|
|
42
|
-
|
|
28
|
+
export declare function createPostgresLogReader(): PostgresLogReader;
|
|
43
29
|
export declare function parsePostgresLogLine(line: string): PostgresLogQuery | null;
|
|
44
30
|
//# sourceMappingURL=postgres-log-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-log-parser.d.ts","sourceRoot":"","sources":["../src/postgres-log-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"postgres-log-parser.d.ts","sourceRoot":"","sources":["../src/postgres-log-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,mBAAmB,QAAa,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAC9D,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AA0ED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,gBAAgB,EAAE,CAAC;IAChD,KAAK,IAAI,SAAS,gBAAgB,EAAE,CAAC;CACtC;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,CAyC3D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAG1E"}
|