UNPKG

ddl-manager

Version:

store postgres procedures and triggers in files

166 lines (161 loc) 6.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CacheScanner = void 0; const ast_1 = require("../ast"); class CacheScanner { constructor(driver, database, graph) { this.driver = driver; this.database = database; this.graph = graph; } async scan(params = {}) { let allCacheColumns = this.graph.findCacheColumnsForTablesOrColumns(params.concreteTables); const brokenColumns = []; for (const column of allCacheColumns) { const hasWrongValues = await this.tryScanColumnOnWrongValues(params, column); if (hasWrongValues) { brokenColumns.push(column); } } if (params.onFinish) { await params.onFinish(); } return brokenColumns; } async tryScanColumnOnWrongValues(params, column) { const timeStart = new Date(); if (params.onStartScanColumn) { await params.onStartScanColumn(column.toString()); } try { const wrongExample = await this.tryFindWrongRowExampleForColumn(params, column); if (params.onScanColumn) { const timeEnd = new Date(); await params.onScanColumn({ column: column.getId(), hasWrongValues: !!wrongExample, wrongExample, time: { start: timeStart, end: timeEnd, duration: +timeEnd - +timeStart } }); } return wrongExample; } catch (error) { if (params.onScanError) { const timeEnd = new Date(); await params.onScanError({ column: column.toString(), error: error, time: { start: timeStart, end: timeEnd, duration: +timeEnd - +timeStart } }); } } } async tryFindWrongRowExampleForColumn(params, column) { var _a; const columnRef = `${column.for.getIdentifier()}.${column.name}`; let details = "null::json"; if (column.select.from.length === 1) { const selectSql = column.select.toString(); const fromAlias = column.select.getFromTable().getIdentifier(); const sourceRowJson = `row_to_json(${fromAlias}.*)`; if (this.database.aggregators.some(aggName => selectSql.includes(aggName + "("))) { details = `array_agg(${sourceRowJson})`; } else if (column.select.orderBy) { details = `(${column.select.clone({ columns: [new ast_1.SelectColumn({ expression: ast_1.Expression.unknown(`array_agg(${sourceRowJson})`), name: "source_row" })], orderBy: undefined, limit: undefined })})`; } else { details = `ARRAY[${sourceRowJson}]`; } } const selectExpected = column.select.clone({ columns: [ ...column.select.columns, new ast_1.SelectColumn({ expression: ast_1.Expression.unknown(details), name: "__cache_source_row_details__" }) ] }); let whereBroken = `${columnRef} is distinct from tmp.${column.name}`; const expression = column.getColumnExpression(); if (expression.isFuncCall()) { const [call] = expression.getFuncCalls(); if (call.name === "array_agg") { whereBroken = ` ${columnRef} is distinct from tmp.${column.name} and not(${columnRef} @> tmp.${column.name} and ${columnRef} <@ tmp.${column.name}) `; } if (call.name === "sum") { whereBroken = `coalesce(${columnRef}, 0) is distinct from coalesce(tmp.${column.name}, 0)`; } if (call.name === "string_agg" && !call.orderBy) { const actual = `string_to_array(coalesce(${columnRef}, ''), ${call.args[1]})`; const expected = `string_to_array(coalesce(tmp.${column.name}, ''), ${call.args[1]})`; whereBroken = ` ${actual} is distinct from ${expected} and not(${actual} @> ${expected} and ${actual} <@ ${expected}) `; } } if (column.name == ast_1.Cache.generateJsonHelperColumnName(column.cache.name)) { whereBroken = `coalesce(${columnRef}, '{}'::jsonb) is distinct from coalesce(tmp.${column.name}, '{}'::jsonb)`; } const selectHasBroken = ` select ${columnRef} as "actual", tmp.${column.name} as "expected", '${column.getTableId()}' as "table", row_to_json(${column.for.getIdentifier()}.*) as "cacheRow", tmp.__cache_source_row_details__ as "sourceRows" from ${column.for} left join lateral ( ${selectExpected.toSQL()} ) as tmp on true where ${whereBroken} order by ${column.for.getIdentifier()}.id limit 1 `; const { rows } = await this.driver.queryWithTimeout(selectHasBroken, (_a = params.timeout) !== null && _a !== void 0 ? _a : 0); const wrongExample = rows[0]; if (wrongExample) { wrongExample.table = column.getTableId(); wrongExample.selectExpectedForThatRow = ` select ${column.for.getIdentifier()}.id, ${columnRef} as "actual", tmp.${column.name} as "expected" from ${column.for} left join lateral ( ${selectExpected.toSQL()} ) as tmp on true where ${column.for.getIdentifier()}.id = ${wrongExample.cacheRow.id} and ${columnRef} is distinct from tmp.${column.name} `; } return wrongExample; } } exports.CacheScanner = CacheScanner; //# sourceMappingURL=CacheScanner.js.map