From f78c49509f8a253a77524404b2081d8a57a3361e Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Wed, 13 May 2026 19:06:32 +0200 Subject: [PATCH] fix(status): report query history readiness --- packages/cli/src/doctor.test.ts | 14 ++-- packages/cli/src/historic-sql-doctor.test.ts | 74 ++++++++++++++------ packages/cli/src/historic-sql-doctor.ts | 40 +++++++---- 3 files changed, 83 insertions(+), 45 deletions(-) diff --git a/packages/cli/src/doctor.test.ts b/packages/cli/src/doctor.test.ts index d0ebdb95..4373337f 100644 --- a/packages/cli/src/doctor.test.ts +++ b/packages/cli/src/doctor.test.ts @@ -266,7 +266,7 @@ describe('runKtxDoctor', () => { expect(testIo.stdout()).toContain('PASS Connections: 1 configured'); }); - it('includes Postgres historic-SQL readiness in project doctor output', async () => { + it('includes Postgres query-history readiness in project doctor output', async () => { await writeFile( join(tempDir, 'ktx.yaml'), [ @@ -276,9 +276,9 @@ describe('runKtxDoctor', () => { ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', ' readonly: true', - ' historicSql:', - ' enabled: true', - ' dialect: postgres', + ' context:', + ' queryHistory:', + ' enabled: true', 'ingest:', ' adapters:', ' - live-database', @@ -290,8 +290,8 @@ describe('runKtxDoctor', () => { const testIo = makeIo(); const runHistoricSqlDoctorChecks = vi.fn(async () => [ { - id: 'historic-sql-postgres-warehouse', - label: 'Postgres Historic SQL (warehouse)', + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', status: 'pass' as const, detail: 'pg_stat_statements ready (PostgreSQL 16.4); info: pg_stat_statements.max is 1000; set it to at least 5000 to reduce query-template eviction churn', @@ -312,7 +312,7 @@ describe('runKtxDoctor', () => { ).resolves.toBe(0); expect(runHistoricSqlDoctorChecks).toHaveBeenCalledTimes(1); - expect(testIo.stdout()).toContain('PASS Postgres Historic SQL (warehouse): pg_stat_statements ready'); + expect(testIo.stdout()).toContain('PASS Postgres query history (warehouse): pg_stat_statements ready'); expect(testIo.stdout()).toContain('info: pg_stat_statements.max is 1000'); expect(testIo.stdout()).not.toContain('Fix: Update the Postgres parameter group or config'); }); diff --git a/packages/cli/src/historic-sql-doctor.test.ts b/packages/cli/src/historic-sql-doctor.test.ts index b6f5f0fa..fae089e7 100644 --- a/packages/cli/src/historic-sql-doctor.test.ts +++ b/packages/cli/src/historic-sql-doctor.test.ts @@ -22,7 +22,7 @@ function projectWithConnections(connections: Record { - it('passes when no Postgres historic-SQL connections are enabled', async () => { + it('passes when no Postgres query-history connections are enabled', async () => { const checks = await runPostgresHistoricSqlDoctorChecks( projectWithConnections({ warehouse: { driver: 'sqlite', path: './warehouse.db', readonly: true }, @@ -34,10 +34,10 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { expect(checks).toEqual([ { - id: 'historic-sql-postgres', - label: 'Postgres Historic SQL', + id: 'query-history-postgres', + label: 'Postgres query history', status: 'pass', - detail: 'No enabled Postgres historic-SQL connections', + detail: 'No enabled Postgres query-history connections', }, ]); }); @@ -54,7 +54,7 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', readonly: true, - historicSql: { enabled: true, dialect: 'postgres' }, + context: { queryHistory: { enabled: true } }, }, }), { postgresHistoricSqlProbe: probe }, @@ -67,14 +67,14 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', readonly: true, - historicSql: { enabled: true, dialect: 'postgres' }, + context: { queryHistory: { enabled: true } }, }, env: process.env, }); expect(checks).toEqual([ { - id: 'historic-sql-postgres-warehouse', - label: 'Postgres Historic SQL (warehouse)', + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', status: 'pass', detail: 'pg_stat_statements ready (PostgreSQL 16.4)', }, @@ -88,7 +88,7 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', readonly: true, - historicSql: { enabled: true, dialect: 'postgres' }, + context: { queryHistory: { enabled: true } }, }, }), { @@ -104,8 +104,8 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { expect(checks).toEqual([ { - id: 'historic-sql-postgres-warehouse', - label: 'Postgres Historic SQL (warehouse)', + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', status: 'pass', detail: 'pg_stat_statements ready (PostgreSQL 16.4); info: pg_stat_statements.max is 1000; set it to at least 5000 to reduce query-template eviction churn', @@ -120,7 +120,7 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', readonly: true, - historicSql: { enabled: true, dialect: 'postgres' }, + context: { queryHistory: { enabled: true } }, }, }), { @@ -138,8 +138,8 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { expect(checks).toEqual([ { - id: 'historic-sql-postgres-warehouse', - label: 'Postgres Historic SQL (warehouse)', + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', status: 'warn', detail: 'pg_stat_statements ready (PostgreSQL 16.4) with warnings: pg_stat_statements.track is none; set it to top or all in the Postgres parameter group or config; info: pg_stat_statements.max is 1000; set it to at least 5000 to reduce query-template eviction churn', @@ -148,14 +148,42 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { ]); }); - it('fails when a connection has postgres historic SQL but is not a Postgres driver', async () => { + it('still checks legacy historicSql blocks before setup migration', async () => { + const probe = vi.fn(async () => ({ + pgServerVersion: 'PostgreSQL 16.4', + warnings: [], + })); + + const checks = await runPostgresHistoricSqlDoctorChecks( + projectWithConnections({ + warehouse: { + driver: 'postgres', + url: 'env:WAREHOUSE_DATABASE_URL', + readonly: true, + historicSql: { enabled: true, dialect: 'postgres' }, + }, + }), + { postgresHistoricSqlProbe: probe }, + ); + + expect(checks).toEqual([ + { + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', + status: 'pass', + detail: 'pg_stat_statements ready (PostgreSQL 16.4)', + }, + ]); + }); + + it('fails when a connection has postgres query history but is not a Postgres driver', async () => { const checks = await runPostgresHistoricSqlDoctorChecks( projectWithConnections({ warehouse: { driver: 'mysql', url: 'env:WAREHOUSE_DATABASE_URL', readonly: true, - historicSql: { enabled: true, dialect: 'postgres' }, + context: { queryHistory: { enabled: true } }, }, }), { @@ -165,11 +193,11 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { expect(checks).toEqual([ { - id: 'historic-sql-postgres-warehouse', - label: 'Postgres Historic SQL (warehouse)', + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', status: 'fail', - detail: 'connections.warehouse.historicSql.dialect is postgres but driver is mysql', - fix: 'Set connections.warehouse.driver to postgres or disable historicSql for this connection', + detail: 'connections.warehouse.context.queryHistory is enabled but driver is mysql', + fix: 'Set connections.warehouse.driver to postgres or disable query history for this connection', }, ]); }); @@ -181,7 +209,7 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', readonly: true, - historicSql: { enabled: true, dialect: 'postgres' }, + context: { queryHistory: { enabled: true } }, }, }), { @@ -197,8 +225,8 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { expect(checks).toEqual([ { - id: 'historic-sql-postgres-warehouse', - label: 'Postgres Historic SQL (warehouse)', + id: 'query-history-postgres-warehouse', + label: 'Postgres query history (warehouse)', status: 'fail', detail: 'pg_stat_statements extension is not installed in the connection database.', fix: 'Run CREATE EXTENSION pg_stat_statements; against the connection database.', diff --git a/packages/cli/src/historic-sql-doctor.ts b/packages/cli/src/historic-sql-doctor.ts index c83e7fb4..30b0e548 100644 --- a/packages/cli/src/historic-sql-doctor.ts +++ b/packages/cli/src/historic-sql-doctor.ts @@ -32,16 +32,26 @@ function check(status: DoctorCheck['status'], id: string, label: string, detail: return fix ? { id, label, status, detail, fix } : { id, label, status, detail }; } -function historicSqlRecord(connection: KtxProjectConnectionConfig): Record | null { - const historicSql = connection.historicSql; - return historicSql && typeof historicSql === 'object' && !Array.isArray(historicSql) - ? (historicSql as Record) - : null; +function recordValue(value: unknown): Record | null { + return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record) : null; } -function isEnabledPostgresHistoricSql(connection: KtxProjectConnectionConfig): boolean { - const historicSql = historicSqlRecord(connection); - return historicSql?.enabled === true && historicSql.dialect === 'postgres'; +function queryHistoryRecord(connection: KtxProjectConnectionConfig): Record | null { + const context = recordValue(connection.context); + return recordValue(context?.queryHistory); +} + +function legacyHistoricSqlRecord(connection: KtxProjectConnectionConfig): Record | null { + return recordValue(connection.historicSql); +} + +function isEnabledPostgresQueryHistory(connection: KtxProjectConnectionConfig): boolean { + const queryHistory = queryHistoryRecord(connection); + if (queryHistory) { + return queryHistory.enabled === true; + } + const legacy = legacyHistoricSqlRecord(connection); + return legacy?.enabled === true && legacy.dialect === 'postgres'; } function isPostgresDriver(connection: KtxProjectConnectionConfig): boolean { @@ -50,7 +60,7 @@ function isPostgresDriver(connection: KtxProjectConnectionConfig): boolean { } function checkId(connectionId: string): string { - return `historic-sql-postgres-${connectionId.replace(/[^a-z0-9_-]+/gi, '-')}`; + return `query-history-postgres-${connectionId.replace(/[^a-z0-9_-]+/gi, '-')}`; } function capabilityFailureFix(error: unknown, connectionId: string, projectDir: string): string { @@ -61,7 +71,7 @@ function capabilityFailureFix(error: unknown, connectionId: string, projectDir: return String(error.remediation); } if (error instanceof Error && error.name === 'HistoricSqlVersionUnsupportedError') { - return 'Use PostgreSQL 14 or newer, or disable historicSql for this connection'; + return 'Use PostgreSQL 14 or newer, or disable query history for this connection'; } return `Fix connections.${connectionId} Postgres settings, then rerun \`ktx status --project-dir ${projectDir}\``; } @@ -107,12 +117,12 @@ export async function runPostgresHistoricSqlDoctorChecks( deps: HistoricSqlDoctorDeps = {}, ): Promise { const targets = Object.entries(project.config.connections) - .filter(([, connection]) => isEnabledPostgresHistoricSql(connection)) + .filter(([, connection]) => isEnabledPostgresQueryHistory(connection)) .sort(([left], [right]) => left.localeCompare(right)); if (targets.length === 0) { return [ - check('pass', 'historic-sql-postgres', 'Postgres Historic SQL', 'No enabled Postgres historic-SQL connections'), + check('pass', 'query-history-postgres', 'Postgres query history', 'No enabled Postgres query-history connections'), ]; } @@ -120,15 +130,15 @@ export async function runPostgresHistoricSqlDoctorChecks( const env = deps.env ?? process.env; const checks: DoctorCheck[] = []; for (const [connectionId, connection] of targets) { - const label = `Postgres Historic SQL (${connectionId})`; + const label = `Postgres query history (${connectionId})`; if (!isPostgresDriver(connection)) { checks.push( check( 'fail', checkId(connectionId), label, - `connections.${connectionId}.historicSql.dialect is postgres but driver is ${String(connection.driver)}`, - `Set connections.${connectionId}.driver to postgres or disable historicSql for this connection`, + `connections.${connectionId}.context.queryHistory is enabled but driver is ${String(connection.driver)}`, + `Set connections.${connectionId}.driver to postgres or disable query history for this connection`, ), ); continue;