From 9f9714230a6e07e95256301facf84978e2864263 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Tue, 31 Mar 2026 14:09:02 +0400 Subject: [PATCH] feat: evaluate rewrite costs and promote optimization state After optimization, evaluate rewrite costs for nudges that have AST rewrites attached. Format rewritten queries with prettier. Promote optimization state from no_improvement_found to improvements_available when a rewrite has significant cost savings. Depends on @query-doctor/core >= 0.6.0 (evaluateNudgeRewriteCosts, bestRewriteImprovement). Co-Authored-By: Claude Opus 4.6 (1M context) --- src/remote/query-optimizer.ts | 47 ++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/remote/query-optimizer.ts b/src/remote/query-optimizer.ts index bc0fc44..94405c2 100644 --- a/src/remote/query-optimizer.ts +++ b/src/remote/query-optimizer.ts @@ -1,4 +1,6 @@ import EventEmitter from "node:events"; +import * as prettier from "prettier"; +import prettierPluginSql from "prettier-plugin-sql"; import { OptimizedQuery, QueryHash, RecentQuery } from "../sql/recent-query.ts"; import type { LiveQueryOptimization } from "./optimization.ts"; import { ConnectionManager } from "../sync/connection-manager.ts"; @@ -17,6 +19,8 @@ import { PostgresVersion, Statistics, StatisticsMode, + evaluateNudgeRewriteCosts, + bestRewriteImprovement, } from "@query-doctor/core"; import { Connectable } from "../sync/connectable.ts"; import { parse } from "@libpg-query/parser"; @@ -298,11 +302,52 @@ export class QueryOptimizer extends EventEmitter { break; } this._validQueriesProcessed++; - const optimization = await this.optimizeQuery( + let optimization = await this.optimizeQuery( optimized, this.target, { timeoutMs: this.calculateTimeoutRetryDelay(optimized) }, ); + + // Evaluate rewrite costs and promote optimization state + if (optimized.nudges?.some((n) => n.rewrite) && "cost" in optimization) { + const updatedNudges = await evaluateNudgeRewriteCosts( + optimized.nudges, + optimization.cost, + (q) => this.target!.optimizer.explainCost(q), + ); + if (updatedNudges) { + for (const nudge of updatedNudges) { + if (nudge.rewrite?.query) { + try { + nudge.rewrite.query = await prettier.format( + nudge.rewrite.query, + { + parser: "sql", + plugins: [prettierPluginSql], + language: "postgresql", + keywordCase: "upper", + }, + ); + } catch {} + } + } + Object.assign(optimized, { nudges: updatedNudges }); + + const best = bestRewriteImprovement(updatedNudges); + if (best && optimization.state === "no_improvement_found") { + optimization = { + state: "improvements_available", + cost: optimization.cost, + optimizedCost: best.rewrittenCost, + costReductionPercentage: best.percentImprovement, + indexRecommendations: [], + indexesUsed: optimization.indexesUsed, + explainPlan: optimization.explainPlan, + }; + } + } + } + this.queriedSinceVacuum++; if (this.queriedSinceVacuum > QueryOptimizer.vacuumThreshold) { await this.vacuum();