@memberjunction/db-auto-doc 6.2.0-edge.3 → 6.2.0-edge.5
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/README.md +200 -3
- package/dist/commands/analyze.d.ts +1 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +6 -0
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/export.d.ts +32 -15
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +144 -14
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/core/AnalysisEngine.d.ts +15 -6
- package/dist/core/AnalysisEngine.d.ts.map +1 -1
- package/dist/core/AnalysisEngine.js +41 -40
- package/dist/core/AnalysisEngine.js.map +1 -1
- package/dist/core/AnalysisOrchestrator.d.ts +25 -1
- package/dist/core/AnalysisOrchestrator.d.ts.map +1 -1
- package/dist/core/AnalysisOrchestrator.js +163 -27
- package/dist/core/AnalysisOrchestrator.js.map +1 -1
- package/dist/core/PKProposalGate.d.ts +22 -0
- package/dist/core/PKProposalGate.d.ts.map +1 -0
- package/dist/core/PKProposalGate.js +52 -0
- package/dist/core/PKProposalGate.js.map +1 -0
- package/dist/database/Database.d.ts +8 -0
- package/dist/database/Database.d.ts.map +1 -1
- package/dist/database/Database.js +23 -1
- package/dist/database/Database.js.map +1 -1
- package/dist/discovery/EnumCandidateGate.d.ts +19 -0
- package/dist/discovery/EnumCandidateGate.d.ts.map +1 -1
- package/dist/discovery/EnumCandidateGate.js +47 -5
- package/dist/discovery/EnumCandidateGate.js.map +1 -1
- package/dist/discovery/FKGraphWalker.d.ts +35 -0
- package/dist/discovery/FKGraphWalker.d.ts.map +1 -1
- package/dist/discovery/FKGraphWalker.js +113 -12
- package/dist/discovery/FKGraphWalker.js.map +1 -1
- package/dist/discovery/LLMDiscoveryValidator.d.ts +2 -0
- package/dist/discovery/LLMDiscoveryValidator.d.ts.map +1 -1
- package/dist/discovery/LLMDiscoveryValidator.js +20 -9
- package/dist/discovery/LLMDiscoveryValidator.js.map +1 -1
- package/dist/discovery/OrganicKeyDetector.d.ts.map +1 -1
- package/dist/discovery/OrganicKeyDetector.js +8 -2
- package/dist/discovery/OrganicKeyDetector.js.map +1 -1
- package/dist/discovery/StructuralPhase.d.ts +27 -5
- package/dist/discovery/StructuralPhase.d.ts.map +1 -1
- package/dist/discovery/StructuralPhase.js +42 -9
- package/dist/discovery/StructuralPhase.js.map +1 -1
- package/dist/discovery/TransitiveBridgeDetector.d.ts +5 -1
- package/dist/discovery/TransitiveBridgeDetector.d.ts.map +1 -1
- package/dist/discovery/TransitiveBridgeDetector.js +25 -3
- package/dist/discovery/TransitiveBridgeDetector.js.map +1 -1
- package/dist/drivers/BaseAutoDocDriver.d.ts +6 -1
- package/dist/drivers/BaseAutoDocDriver.d.ts.map +1 -1
- package/dist/drivers/BaseAutoDocDriver.js +3 -5
- package/dist/drivers/BaseAutoDocDriver.js.map +1 -1
- package/dist/drivers/IndexCatalog.d.ts +54 -0
- package/dist/drivers/IndexCatalog.d.ts.map +1 -0
- package/dist/drivers/IndexCatalog.js +145 -0
- package/dist/drivers/IndexCatalog.js.map +1 -0
- package/dist/drivers/MySQLDriver.d.ts +5 -1
- package/dist/drivers/MySQLDriver.d.ts.map +1 -1
- package/dist/drivers/MySQLDriver.js +15 -6
- package/dist/drivers/MySQLDriver.js.map +1 -1
- package/dist/drivers/PostgreSQLDriver.d.ts +14 -2
- package/dist/drivers/PostgreSQLDriver.d.ts.map +1 -1
- package/dist/drivers/PostgreSQLDriver.js +52 -19
- package/dist/drivers/PostgreSQLDriver.js.map +1 -1
- package/dist/drivers/SQLServerDriver.d.ts +5 -1
- package/dist/drivers/SQLServerDriver.d.ts.map +1 -1
- package/dist/drivers/SQLServerDriver.js +13 -1
- package/dist/drivers/SQLServerDriver.js.map +1 -1
- package/dist/generators/AdditionalSchemaInfoGenerator.d.ts +34 -0
- package/dist/generators/AdditionalSchemaInfoGenerator.d.ts.map +1 -1
- package/dist/generators/AdditionalSchemaInfoGenerator.js +85 -4
- package/dist/generators/AdditionalSchemaInfoGenerator.js.map +1 -1
- package/dist/generators/ErdClusterGenerator.d.ts +44 -0
- package/dist/generators/ErdClusterGenerator.d.ts.map +1 -0
- package/dist/generators/ErdClusterGenerator.js +216 -0
- package/dist/generators/ErdClusterGenerator.js.map +1 -0
- package/dist/generators/ErdClusterer.d.ts +130 -0
- package/dist/generators/ErdClusterer.d.ts.map +1 -0
- package/dist/generators/ErdClusterer.js +308 -0
- package/dist/generators/ErdClusterer.js.map +1 -0
- package/dist/generators/HTMLGenerator.d.ts +6 -0
- package/dist/generators/HTMLGenerator.d.ts.map +1 -1
- package/dist/generators/HTMLGenerator.js +41 -6
- package/dist/generators/HTMLGenerator.js.map +1 -1
- package/dist/generators/MermaidGenerator.d.ts +3 -0
- package/dist/generators/MermaidGenerator.d.ts.map +1 -1
- package/dist/generators/MermaidGenerator.js +4 -0
- package/dist/generators/MermaidGenerator.js.map +1 -1
- package/dist/generators/index.d.ts +2 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins/BaseAutoDocPlugin.d.ts +33 -0
- package/dist/plugins/BaseAutoDocPlugin.d.ts.map +1 -0
- package/dist/plugins/BaseAutoDocPlugin.js +70 -0
- package/dist/plugins/BaseAutoDocPlugin.js.map +1 -0
- package/dist/plugins/PluginManager.d.ts +54 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -0
- package/dist/plugins/PluginManager.js +115 -0
- package/dist/plugins/PluginManager.js.map +1 -0
- package/dist/plugins/builtins.d.ts +13 -0
- package/dist/plugins/builtins.d.ts.map +1 -0
- package/dist/plugins/builtins.js +12 -0
- package/dist/plugins/builtins.js.map +1 -0
- package/dist/plugins/index-advisor/IndexAdvisorPlugin.d.ts +25 -0
- package/dist/plugins/index-advisor/IndexAdvisorPlugin.d.ts.map +1 -0
- package/dist/plugins/index-advisor/IndexAdvisorPlugin.js +128 -0
- package/dist/plugins/index-advisor/IndexAdvisorPlugin.js.map +1 -0
- package/dist/plugins/index-advisor/IndexAdvisorTypes.d.ts +111 -0
- package/dist/plugins/index-advisor/IndexAdvisorTypes.d.ts.map +1 -0
- package/dist/plugins/index-advisor/IndexAdvisorTypes.js +5 -0
- package/dist/plugins/index-advisor/IndexAdvisorTypes.js.map +1 -0
- package/dist/plugins/index-advisor/IndexLLMReview.d.ts +48 -0
- package/dist/plugins/index-advisor/IndexLLMReview.d.ts.map +1 -0
- package/dist/plugins/index-advisor/IndexLLMReview.js +181 -0
- package/dist/plugins/index-advisor/IndexLLMReview.js.map +1 -0
- package/dist/plugins/index-advisor/IndexMigrationGenerator.d.ts +28 -0
- package/dist/plugins/index-advisor/IndexMigrationGenerator.d.ts.map +1 -0
- package/dist/plugins/index-advisor/IndexMigrationGenerator.js +98 -0
- package/dist/plugins/index-advisor/IndexMigrationGenerator.js.map +1 -0
- package/dist/plugins/index-advisor/IndexRules.d.ts +19 -0
- package/dist/plugins/index-advisor/IndexRules.d.ts.map +1 -0
- package/dist/plugins/index-advisor/IndexRules.js +154 -0
- package/dist/plugins/index-advisor/IndexRules.js.map +1 -0
- package/dist/plugins/index.d.ts +13 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +13 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +82 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +9 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/types/analysis.d.ts +2 -0
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/config.d.ts +52 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +7 -4
- package/dist/types/config.js.map +1 -1
- package/dist/types/discovery.d.ts +2 -0
- package/dist/types/discovery.d.ts.map +1 -1
- package/dist/types/driver.d.ts +30 -0
- package/dist/types/driver.d.ts.map +1 -1
- package/dist/types/driver.js +5 -1
- package/dist/types/driver.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/state.d.ts +36 -0
- package/dist/types/state.d.ts.map +1 -1
- package/dist/utils/config-loader.d.ts +5 -0
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-loader.js +38 -1
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/index-hints.d.ts +36 -0
- package/dist/utils/index-hints.d.ts.map +1 -0
- package/dist/utils/index-hints.js +65 -0
- package/dist/utils/index-hints.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/llm-factory.d.ts +1 -0
- package/dist/utils/llm-factory.d.ts.map +1 -1
- package/dist/utils/llm-factory.js +6 -1
- package/dist/utils/llm-factory.js.map +1 -1
- package/package.json +5 -5
- package/prompts/fk-pruning-table.md +13 -1
- package/prompts/index-review.md +60 -0
- package/prompts/pk-pruning-table.md +10 -0
- package/prompts/table-analysis.md +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexAdvisorTypes.d.ts","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexAdvisorTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexAdvisorTypes.js","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexAdvisorTypes.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM review layer for the Index Advisor.
|
|
3
|
+
*
|
|
4
|
+
* Per table, the model sees the rule-based proposals plus the context DBAutoDoc built (column
|
|
5
|
+
* statistics, descriptions, existing indexes, sample queries) and may keep, drop or modify each
|
|
6
|
+
* proposal and suggest new ones. The model returns structured decisions, never SQL; every
|
|
7
|
+
* suggestion is validated deterministically before it is applied.
|
|
8
|
+
*/
|
|
9
|
+
import { PromptEngine } from '../../prompts/PromptEngine.js';
|
|
10
|
+
import type { AIConfig } from '../../types/config.js';
|
|
11
|
+
import type { DatabaseDocumentation, TableDefinition } from '../../types/state.js';
|
|
12
|
+
import type { AutoDocIndex } from '../../types/driver.js';
|
|
13
|
+
import type { IndexAdvisorData, IndexLLMReviewSummary, IndexProposal, IndexReviewResponse } from './IndexAdvisorTypes.js';
|
|
14
|
+
/** A table chosen for review. */
|
|
15
|
+
export interface IndexReviewTarget {
|
|
16
|
+
SchemaName: string;
|
|
17
|
+
Table: TableDefinition;
|
|
18
|
+
}
|
|
19
|
+
/** Result of applying one table's review response. */
|
|
20
|
+
export interface IndexReviewApplyResult {
|
|
21
|
+
Proposals: IndexProposal[];
|
|
22
|
+
Dropped: IndexLLMReviewSummary['Dropped'];
|
|
23
|
+
Rejected: IndexLLMReviewSummary['Rejected'];
|
|
24
|
+
}
|
|
25
|
+
export declare class IndexLLMReviewer {
|
|
26
|
+
private promptEngine;
|
|
27
|
+
private ai;
|
|
28
|
+
private log;
|
|
29
|
+
constructor(promptEngine: PromptEngine, ai: AIConfig, log: (message: string) => void);
|
|
30
|
+
/** Creates a reviewer with its own prompt engine over the package's prompt templates. */
|
|
31
|
+
static Create(ai: AIConfig, log: (message: string) => void): Promise<IndexLLMReviewer>;
|
|
32
|
+
/** Reviews the selected tables and returns the revised proposal list plus a summary. */
|
|
33
|
+
Review(state: DatabaseDocumentation, data: IndexAdvisorData, platform: string): Promise<{
|
|
34
|
+
Proposals: IndexProposal[];
|
|
35
|
+
Summary: IndexLLMReviewSummary;
|
|
36
|
+
}>;
|
|
37
|
+
private reviewTable;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Tables worth reviewing: large enough, and either carrying a rule-based proposal or used by a
|
|
41
|
+
* sample query. Largest first, capped at MaxLLMTables.
|
|
42
|
+
*/
|
|
43
|
+
export declare function SelectReviewTables(state: DatabaseDocumentation, data: IndexAdvisorData): IndexReviewTarget[];
|
|
44
|
+
/** Template context for `prompts/index-review.md`. */
|
|
45
|
+
export declare function BuildReviewContext(state: DatabaseDocumentation, target: IndexReviewTarget, proposals: IndexProposal[], existing: AutoDocIndex[], platform: string): object;
|
|
46
|
+
/** Applies one table's review response, validating every change. Pure; no LLM involved. */
|
|
47
|
+
export declare function ApplyReviewResponse(target: IndexReviewTarget, proposals: IndexProposal[], existing: AutoDocIndex[], response: IndexReviewResponse): IndexReviewApplyResult;
|
|
48
|
+
//# sourceMappingURL=IndexLLMReview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexLLMReview.d.ts","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexLLMReview.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAUhC,iCAAiC;AACjC,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,sDAAsD;AACtD,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC1C,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,GAAG;gBAFH,YAAY,EAAE,YAAY,EAC1B,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAGxC,yFAAyF;WACrE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAOnG,wFAAwF;IAC3E,MAAM,CACjB,KAAK,EAAE,qBAAqB,EAC5B,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,SAAS,EAAE,aAAa,EAAE,CAAC;QAAC,OAAO,EAAE,qBAAqB,CAAA;KAAE,CAAC;YAY5D,WAAW;CA6B1B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,EAAE,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,EAAE,CAY5G;AAED,sDAAsD;AACtD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,aAAa,EAAE,EAC1B,QAAQ,EAAE,YAAY,EAAE,EACxB,QAAQ,EAAE,MAAM,GACf,MAAM,CAqBR;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,aAAa,EAAE,EAC1B,QAAQ,EAAE,YAAY,EAAE,EACxB,QAAQ,EAAE,mBAAmB,GAC5B,sBAAsB,CAaxB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM review layer for the Index Advisor.
|
|
3
|
+
*
|
|
4
|
+
* Per table, the model sees the rule-based proposals plus the context DBAutoDoc built (column
|
|
5
|
+
* statistics, descriptions, existing indexes, sample queries) and may keep, drop or modify each
|
|
6
|
+
* proposal and suggest new ones. The model returns structured decisions, never SQL; every
|
|
7
|
+
* suggestion is validated deterministically before it is applied.
|
|
8
|
+
*/
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { PromptEngine } from '../../prompts/PromptEngine.js';
|
|
12
|
+
import { IsCovered, IsPoorKeyColumn, IndexPriority } from './IndexRules.js';
|
|
13
|
+
const MAX_KEY_COLUMNS = 5;
|
|
14
|
+
const MAX_INCLUDE_COLUMNS = 10;
|
|
15
|
+
const MAX_SAMPLE_QUERIES = 3;
|
|
16
|
+
const MAX_SQL_CHARS = 1500;
|
|
17
|
+
const MODEL_OVERRIDE_KEY = 'indexAdvisor';
|
|
18
|
+
const lower = (s) => s.toLowerCase();
|
|
19
|
+
export class IndexLLMReviewer {
|
|
20
|
+
constructor(promptEngine, ai, log) {
|
|
21
|
+
this.promptEngine = promptEngine;
|
|
22
|
+
this.ai = ai;
|
|
23
|
+
this.log = log;
|
|
24
|
+
}
|
|
25
|
+
/** Creates a reviewer with its own prompt engine over the package's prompt templates. */
|
|
26
|
+
static async Create(ai, log) {
|
|
27
|
+
const promptsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../prompts');
|
|
28
|
+
const engine = new PromptEngine(ai, promptsDir);
|
|
29
|
+
await engine.Initialize();
|
|
30
|
+
return new IndexLLMReviewer(engine, ai, log);
|
|
31
|
+
}
|
|
32
|
+
/** Reviews the selected tables and returns the revised proposal list plus a summary. */
|
|
33
|
+
async Review(state, data, platform) {
|
|
34
|
+
const override = this.ai.modelOverrides?.[MODEL_OVERRIDE_KEY];
|
|
35
|
+
const summary = {
|
|
36
|
+
Model: override?.model ?? this.ai.model, TablesReviewed: 0, TokensUsed: 0, Dropped: [], Rejected: [], Errors: []
|
|
37
|
+
};
|
|
38
|
+
let proposals = [...data.Proposed];
|
|
39
|
+
for (const target of SelectReviewTables(state, data)) {
|
|
40
|
+
proposals = await this.reviewTable(state, data, target, platform, proposals, summary);
|
|
41
|
+
}
|
|
42
|
+
return { Proposals: proposals.sort((a, b) => b.Priority - a.Priority), Summary: summary };
|
|
43
|
+
}
|
|
44
|
+
async reviewTable(state, data, target, platform, proposals, summary) {
|
|
45
|
+
const isThisTable = (p) => sameTable(p, target);
|
|
46
|
+
const tableProposals = proposals.filter(isThisTable);
|
|
47
|
+
const existing = data.Existing.filter((ix) => sameTable(ix, target));
|
|
48
|
+
const override = this.ai.modelOverrides?.[MODEL_OVERRIDE_KEY];
|
|
49
|
+
const result = await this.promptEngine.ExecutePrompt('index-review', BuildReviewContext(state, target, tableProposals, existing, platform), { responseFormat: 'JSON', temperature: override?.temperature ?? 0.1, maxTokens: override?.maxTokens ?? this.ai.maxTokens, modelOverride: override?.model, effortLevelOverride: override?.effortLevel });
|
|
50
|
+
summary.TablesReviewed++;
|
|
51
|
+
summary.TokensUsed += result.tokensUsed;
|
|
52
|
+
if (!result.success || !result.result) {
|
|
53
|
+
summary.Errors.push({ TableName: target.Table.name, Error: result.errorMessage ?? 'empty response' });
|
|
54
|
+
this.log(`Index Advisor review failed for ${target.SchemaName}.${target.Table.name} (keeping rule-based proposals)`);
|
|
55
|
+
return proposals;
|
|
56
|
+
}
|
|
57
|
+
const applied = ApplyReviewResponse(target, tableProposals, existing, result.result);
|
|
58
|
+
summary.Dropped.push(...applied.Dropped);
|
|
59
|
+
summary.Rejected.push(...applied.Rejected);
|
|
60
|
+
return [...proposals.filter((p) => !isThisTable(p)), ...applied.Proposals];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Tables worth reviewing: large enough, and either carrying a rule-based proposal or used by a
|
|
65
|
+
* sample query. Largest first, capped at MaxLLMTables.
|
|
66
|
+
*/
|
|
67
|
+
export function SelectReviewTables(state, data) {
|
|
68
|
+
const wanted = new Set([
|
|
69
|
+
...data.Proposed.map((p) => tableId(p.SchemaName, p.TableName)),
|
|
70
|
+
...(state.sampleQueries?.queries ?? []).flatMap((q) => [...q.primaryEntities, ...q.relatedEntities].map((e) => tableId(e.schema, e.table)))
|
|
71
|
+
]);
|
|
72
|
+
const skipped = new Set(data.SkippedSchemas.map((s) => lower(s.SchemaName)));
|
|
73
|
+
return state.schemas
|
|
74
|
+
.filter((s) => !skipped.has(lower(s.name)))
|
|
75
|
+
.flatMap((s) => s.tables.map((t) => ({ SchemaName: s.name, Table: t })))
|
|
76
|
+
.filter((t) => t.Table.rowCount >= data.Options.MinRowCount && wanted.has(tableId(t.SchemaName, t.Table.name)))
|
|
77
|
+
.sort((a, b) => b.Table.rowCount - a.Table.rowCount)
|
|
78
|
+
.slice(0, data.Options.MaxLLMTables);
|
|
79
|
+
}
|
|
80
|
+
/** Template context for `prompts/index-review.md`. */
|
|
81
|
+
export function BuildReviewContext(state, target, proposals, existing, platform) {
|
|
82
|
+
const t = target.Table;
|
|
83
|
+
return {
|
|
84
|
+
platform,
|
|
85
|
+
table: { schema: target.SchemaName, name: t.name, rowCount: t.rowCount, description: t.description ?? '' },
|
|
86
|
+
columns: t.columns.map((c) => ({
|
|
87
|
+
name: c.name,
|
|
88
|
+
dataType: c.dataType,
|
|
89
|
+
distinctCount: c.statistics?.distinctCount ?? null,
|
|
90
|
+
nullPercentage: c.statistics ? Math.round(c.statistics.nullPercentage) : null,
|
|
91
|
+
description: (c.description ?? '').slice(0, 160)
|
|
92
|
+
})),
|
|
93
|
+
existing: existing.map((ix) => ({
|
|
94
|
+
name: ix.IndexName, columns: ix.Columns, include: ix.IncludeColumns, isUnique: ix.IsUnique, isPrimaryKey: ix.IsPrimaryKey, filter: ix.FilterDefinition ?? ''
|
|
95
|
+
})),
|
|
96
|
+
proposals: proposals.map((p) => ({ columns: p.Columns, reason: p.Reason })),
|
|
97
|
+
sampleQueries: (state.sampleQueries?.queries ?? [])
|
|
98
|
+
.filter((q) => [...q.primaryEntities, ...q.relatedEntities].some((e) => tableId(e.schema, e.table) === tableId(target.SchemaName, t.name)))
|
|
99
|
+
.slice(0, MAX_SAMPLE_QUERIES)
|
|
100
|
+
.map((q) => ({ name: q.name, sql: q.sqlQuery.slice(0, MAX_SQL_CHARS) }))
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Applies one table's review response, validating every change. Pure; no LLM involved. */
|
|
104
|
+
export function ApplyReviewResponse(target, proposals, existing, response) {
|
|
105
|
+
const out = { Proposals: [], Dropped: [], Rejected: [] };
|
|
106
|
+
const decisions = new Map((Array.isArray(response.Decisions) ? response.Decisions : []).map((d) => [d.Proposal, d]));
|
|
107
|
+
proposals.forEach((proposal, i) => applyDecision(target, proposal, decisions.get(i + 1), existing, out));
|
|
108
|
+
for (const addition of Array.isArray(response.Additions) ? response.Additions : []) {
|
|
109
|
+
const candidate = {
|
|
110
|
+
SchemaName: target.SchemaName, TableName: target.Table.name, Columns: addition.Columns ?? [], IncludeColumns: addition.IncludeColumns ?? [],
|
|
111
|
+
Source: 'LLM', Reason: addition.Reason ?? '', Priority: Math.max(1, IndexPriority(target.Table.rowCount) - 5),
|
|
112
|
+
Evidence: { RowCount: target.Table.rowCount }
|
|
113
|
+
};
|
|
114
|
+
acceptIfValid(target, candidate, existing, out);
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
function applyDecision(target, proposal, decision, existing, out) {
|
|
119
|
+
if (decision?.Action === 'drop' && decision.Reason) {
|
|
120
|
+
out.Dropped.push({ Proposal: proposal, Reason: decision.Reason });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (decision?.Action === 'modify') {
|
|
124
|
+
const modified = {
|
|
125
|
+
...proposal,
|
|
126
|
+
Columns: decision.Columns ?? proposal.Columns,
|
|
127
|
+
IncludeColumns: decision.IncludeColumns ?? proposal.IncludeColumns,
|
|
128
|
+
Reason: `${proposal.Reason} Adjusted by review: ${decision.Reason}`
|
|
129
|
+
};
|
|
130
|
+
if (acceptIfValid(target, modified, existing, out)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// keep, no decision, or an invalid modification: the rule-based proposal stands
|
|
135
|
+
acceptIfValid(target, proposal, existing, out);
|
|
136
|
+
}
|
|
137
|
+
/** Validates a proposal against the table and what is already planned; records rejections. */
|
|
138
|
+
function acceptIfValid(target, p, existing, out) {
|
|
139
|
+
const why = validate(target, p, existing, out.Proposals);
|
|
140
|
+
if (why) {
|
|
141
|
+
out.Rejected.push({ TableName: target.Table.name, Columns: p.Columns, Why: why });
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
out.Proposals.push({ ...p, Columns: canonical(target.Table, p.Columns), IncludeColumns: canonical(target.Table, p.IncludeColumns) });
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
function validate(target, p, existing, planned) {
|
|
148
|
+
const columns = p.Columns ?? [];
|
|
149
|
+
const include = p.IncludeColumns ?? [];
|
|
150
|
+
const unknown = [...columns, ...include].filter((c) => !findColumn(target.Table, c));
|
|
151
|
+
if (columns.length === 0 || columns.length > MAX_KEY_COLUMNS)
|
|
152
|
+
return `needs 1–${MAX_KEY_COLUMNS} key columns`;
|
|
153
|
+
if (include.length > MAX_INCLUDE_COLUMNS)
|
|
154
|
+
return `too many INCLUDE columns`;
|
|
155
|
+
if (unknown.length)
|
|
156
|
+
return `unknown column(s): ${unknown.join(', ')}`;
|
|
157
|
+
if (new Set(columns.map(lower)).size !== columns.length)
|
|
158
|
+
return 'repeats a key column';
|
|
159
|
+
if (include.some((c) => columns.some((k) => lower(k) === lower(c))))
|
|
160
|
+
return 'INCLUDE repeats a key column';
|
|
161
|
+
if (IsPoorKeyColumn(findColumn(target.Table, columns[0])))
|
|
162
|
+
return `leading column ${columns[0]} is a poor index key`;
|
|
163
|
+
if (IsCovered(columns, existing))
|
|
164
|
+
return 'an existing index already starts with these columns';
|
|
165
|
+
if (planned.some((q) => q.Columns.length === columns.length && q.Columns.every((c, i) => lower(c) === lower(columns[i]))))
|
|
166
|
+
return 'duplicates another proposal';
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function findColumn(table, name) {
|
|
170
|
+
return table.columns.find((c) => lower(c.name) === lower(name));
|
|
171
|
+
}
|
|
172
|
+
function canonical(table, names) {
|
|
173
|
+
return names.map((n) => findColumn(table, n)?.name ?? n);
|
|
174
|
+
}
|
|
175
|
+
function sameTable(item, target) {
|
|
176
|
+
return lower(item.SchemaName) === lower(target.SchemaName) && lower(item.TableName) === lower(target.Table.name);
|
|
177
|
+
}
|
|
178
|
+
function tableId(schema, table) {
|
|
179
|
+
return `${lower(schema)}.${lower(table)}`;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=IndexLLMReview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexLLMReview.js","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexLLMReview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns Index Advisor proposals into a dialect-specific, Flyway-style SQL migration.
|
|
3
|
+
*/
|
|
4
|
+
import type { AutoDocExportFile, AutoDocExporterOptions } from '../types.js';
|
|
5
|
+
import type { IndexAdvisorData, IndexProposal } from './IndexAdvisorTypes.js';
|
|
6
|
+
type Provider = AutoDocExporterOptions['Provider'];
|
|
7
|
+
export declare class IndexMigrationGenerator {
|
|
8
|
+
/** Returns the migration file (plus a Flyway `.conf` when PostgreSQL CONCURRENTLY is on), or nothing when there are no proposals. */
|
|
9
|
+
Generate(data: IndexAdvisorData, options: AutoDocExporterOptions): AutoDocExportFile[];
|
|
10
|
+
/**
|
|
11
|
+
* Index name for a proposal: IX_<table>_<columns>, shortened with a hash suffix when too long.
|
|
12
|
+
* PostgreSQL index names must be unique per schema, and the readable part is lossy (Order.Line_ID and
|
|
13
|
+
* Order_Line.ID both give IX_Order_Line_ID), so PostgreSQL names always carry a hash of the exact table and columns.
|
|
14
|
+
*/
|
|
15
|
+
static IndexName(proposal: Pick<IndexProposal, 'TableName' | 'Columns'>, provider: Provider): string;
|
|
16
|
+
/** Flattens text onto one line so it cannot end a `--` comment and become executable SQL. */
|
|
17
|
+
static CommentText(text: string): string;
|
|
18
|
+
/** yyyyMMddHHmm (UTC) from an ISO timestamp. */
|
|
19
|
+
static Timestamp(iso: string): string;
|
|
20
|
+
private buildScript;
|
|
21
|
+
private statement;
|
|
22
|
+
/** Quotes an identifier for the platform, doubling any embedded quote character. */
|
|
23
|
+
static Quote(identifier: string, provider: Provider): string;
|
|
24
|
+
/** 8-hex-digit FNV-1a hash, used to keep shortened index names unique. */
|
|
25
|
+
private static hash;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=IndexMigrationGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexMigrationGenerator.d.ts","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexMigrationGenerator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE9E,KAAK,QAAQ,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;AAOnD,qBAAa,uBAAuB;IAClC,qIAAqI;IAC9H,QAAQ,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,sBAAsB,GAAG,iBAAiB,EAAE;IAc7F;;;;OAIG;WACW,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAa3G,6FAA6F;WAC/E,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI/C,gDAAgD;WAClC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAM5C,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,SAAS;IAmBjB,oFAAoF;WACtE,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAWnE,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,IAAI;CAQpB"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns Index Advisor proposals into a dialect-specific, Flyway-style SQL migration.
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum identifier length per platform. */
|
|
5
|
+
const MAX_IDENTIFIER = { sqlserver: 128, postgresql: 63, mysql: 64 };
|
|
6
|
+
const BASE_NAME = 'dbautodoc_indexes';
|
|
7
|
+
export class IndexMigrationGenerator {
|
|
8
|
+
/** Returns the migration file (plus a Flyway `.conf` when PostgreSQL CONCURRENTLY is on), or nothing when there are no proposals. */
|
|
9
|
+
Generate(data, options) {
|
|
10
|
+
if (data.Proposed.length === 0) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
const concurrently = options.Provider === 'postgresql' && data.Options.Concurrently;
|
|
14
|
+
const fileName = `${options.FileNamePrefix ?? `V${IndexMigrationGenerator.Timestamp(data.AnalyzedAt)}__`}${BASE_NAME}.sql`;
|
|
15
|
+
const files = [{ FileName: fileName, Content: this.buildScript(data, options.Provider, concurrently) }];
|
|
16
|
+
if (concurrently) {
|
|
17
|
+
// CREATE INDEX CONCURRENTLY cannot run inside a transaction; tell Flyway to run this script without one.
|
|
18
|
+
files.push({ FileName: `${fileName}.conf`, Content: 'executeInTransaction=false\n' });
|
|
19
|
+
}
|
|
20
|
+
return files;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Index name for a proposal: IX_<table>_<columns>, shortened with a hash suffix when too long.
|
|
24
|
+
* PostgreSQL index names must be unique per schema, and the readable part is lossy (Order.Line_ID and
|
|
25
|
+
* Order_Line.ID both give IX_Order_Line_ID), so PostgreSQL names always carry a hash of the exact table and columns.
|
|
26
|
+
*/
|
|
27
|
+
static IndexName(proposal, provider) {
|
|
28
|
+
const raw = `IX_${proposal.TableName}_${proposal.Columns.join('_')}`.replace(/[^A-Za-z0-9_]/g, '_');
|
|
29
|
+
const max = MAX_IDENTIFIER[provider];
|
|
30
|
+
if (provider === 'postgresql') {
|
|
31
|
+
const suffix = IndexMigrationGenerator.hash(JSON.stringify([proposal.TableName, proposal.Columns]));
|
|
32
|
+
return `${raw.slice(0, max - 9)}_${suffix}`;
|
|
33
|
+
}
|
|
34
|
+
if (raw.length <= max) {
|
|
35
|
+
return raw;
|
|
36
|
+
}
|
|
37
|
+
return `${raw.slice(0, max - 9)}_${IndexMigrationGenerator.hash(raw)}`;
|
|
38
|
+
}
|
|
39
|
+
/** Flattens text onto one line so it cannot end a `--` comment and become executable SQL. */
|
|
40
|
+
static CommentText(text) {
|
|
41
|
+
return text.replace(/[\r\n\u0085\u2028\u2029]+/g, ' ');
|
|
42
|
+
}
|
|
43
|
+
/** yyyyMMddHHmm (UTC) from an ISO timestamp. */
|
|
44
|
+
static Timestamp(iso) {
|
|
45
|
+
const d = new Date(iso);
|
|
46
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
47
|
+
return `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}`;
|
|
48
|
+
}
|
|
49
|
+
buildScript(data, provider, concurrently) {
|
|
50
|
+
const header = [
|
|
51
|
+
`-- Index migration generated by DBAutoDoc Index Advisor (${data.AnalyzedAt}).`,
|
|
52
|
+
`-- Platform: ${provider}. ${data.Proposed.length} proposed index(es), highest priority first.`,
|
|
53
|
+
'-- Review before applying. Each index is created only if it does not already exist.',
|
|
54
|
+
provider === 'mysql' ? '-- MySQL has no CREATE INDEX IF NOT EXISTS; re-running this script fails on indexes that already exist.' : ''
|
|
55
|
+
].filter((l) => l.length > 0);
|
|
56
|
+
const statements = data.Proposed.map((p) => `-- ${p.Source}: ${IndexMigrationGenerator.CommentText(p.Reason)}\n${this.statement(p, provider, concurrently)}`);
|
|
57
|
+
return `${header.join('\n')}\n\n${statements.join('\n\n')}\n`;
|
|
58
|
+
}
|
|
59
|
+
statement(p, provider, concurrently) {
|
|
60
|
+
const name = IndexMigrationGenerator.IndexName(p, provider);
|
|
61
|
+
const q = (id) => IndexMigrationGenerator.Quote(id, provider);
|
|
62
|
+
const table = `${q(p.SchemaName)}.${q(p.TableName)}`;
|
|
63
|
+
const columns = p.Columns.map(q).join(', ');
|
|
64
|
+
const include = p.IncludeColumns.length && provider !== 'mysql' ? ` INCLUDE (${p.IncludeColumns.map(q).join(', ')})` : '';
|
|
65
|
+
switch (provider) {
|
|
66
|
+
case 'sqlserver':
|
|
67
|
+
return [
|
|
68
|
+
`IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'${name}' AND object_id = OBJECT_ID(N'${table.replace(/'/g, "''")}'))`,
|
|
69
|
+
` CREATE NONCLUSTERED INDEX ${q(name)} ON ${table} (${columns})${include};`
|
|
70
|
+
].join('\n');
|
|
71
|
+
case 'postgresql':
|
|
72
|
+
return `CREATE INDEX ${concurrently ? 'CONCURRENTLY ' : ''}IF NOT EXISTS ${q(name)} ON ${table} (${columns})${include};`;
|
|
73
|
+
case 'mysql':
|
|
74
|
+
return `CREATE INDEX ${q(name)} ON ${table} (${columns});`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Quotes an identifier for the platform, doubling any embedded quote character. */
|
|
78
|
+
static Quote(identifier, provider) {
|
|
79
|
+
switch (provider) {
|
|
80
|
+
case 'sqlserver':
|
|
81
|
+
return `[${identifier.replace(/]/g, ']]')}]`;
|
|
82
|
+
case 'postgresql':
|
|
83
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
84
|
+
case 'mysql':
|
|
85
|
+
return `\`${identifier.replace(/`/g, '``')}\``;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** 8-hex-digit FNV-1a hash, used to keep shortened index names unique. */
|
|
89
|
+
static hash(value) {
|
|
90
|
+
let h = 0x811c9dc5;
|
|
91
|
+
for (let i = 0; i < value.length; i++) {
|
|
92
|
+
h ^= value.charCodeAt(i);
|
|
93
|
+
h = Math.imul(h, 0x01000193);
|
|
94
|
+
}
|
|
95
|
+
return (h >>> 0).toString(16).padStart(8, '0');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=IndexMigrationGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexMigrationGenerator.js","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexMigrationGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic index rules. Pure functions over the state file and the existing indexes,
|
|
3
|
+
* so they can be tested without a database.
|
|
4
|
+
*/
|
|
5
|
+
import type { DatabaseDocumentation, TableDefinition, ColumnDefinition } from '../../types/state.js';
|
|
6
|
+
import type { AutoDocIndex } from '../../types/driver.js';
|
|
7
|
+
import type { IndexAdvisorOptions, IndexProposal, RedundantIndex } from './IndexAdvisorTypes.js';
|
|
8
|
+
/** Proposes indexes for uncovered foreign keys and discovered primary keys. */
|
|
9
|
+
export declare function ProposeIndexes(state: DatabaseDocumentation, existing: AutoDocIndex[], options: IndexAdvisorOptions): IndexProposal[];
|
|
10
|
+
/** Flags existing indexes whose key columns are a prefix of (or equal to) another index's. */
|
|
11
|
+
export declare function FindRedundantIndexes(existing: AutoDocIndex[]): RedundantIndex[];
|
|
12
|
+
/** True when an index on `table` already starts with exactly these columns. */
|
|
13
|
+
export declare function IsCovered(columns: string[], tableIndexes: AutoDocIndex[]): boolean;
|
|
14
|
+
/** Larger tables benefit most: 1k rows → ~30, 100k → ~50, 10M → ~70, capped at 100. */
|
|
15
|
+
export declare function IndexPriority(rowCount: number): number;
|
|
16
|
+
/** True for columns that make poor leading index keys (large/unstructured types, ≤2 distinct values). */
|
|
17
|
+
export declare function IsPoorKeyColumn(column: ColumnDefinition): boolean;
|
|
18
|
+
export declare function FindTable(state: DatabaseDocumentation, schemaName: string, tableName: string): TableDefinition | undefined;
|
|
19
|
+
//# sourceMappingURL=IndexRules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexRules.d.ts","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexRules.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAyB,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAiBxH,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,mBAAmB,GAC3B,aAAa,EAAE,CAajB;AAED,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,cAAc,EAAE,CAiB/E;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO,CAElF;AAkFD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,yGAAyG;AACzG,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAGjE;AAiCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAI1H"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic index rules. Pure functions over the state file and the existing indexes,
|
|
3
|
+
* so they can be tested without a database.
|
|
4
|
+
*/
|
|
5
|
+
/** Column types that make poor btree index keys. */
|
|
6
|
+
const POOR_KEY_TYPES = /^(text|ntext|image|xml|json|jsonb|bytea|blob|longblob|mediumblob|longtext|mediumtext|tsvector|geometry|geography|hierarchyid|sql_variant)$|\(max\)$/i;
|
|
7
|
+
const lower = (s) => s.toLowerCase();
|
|
8
|
+
const tableKey = (schema, table) => `${lower(schema)}.${lower(table)}`;
|
|
9
|
+
/** Proposes indexes for uncovered foreign keys and discovered primary keys. */
|
|
10
|
+
export function ProposeIndexes(state, existing, options) {
|
|
11
|
+
const indexesByTable = groupByTable(existing);
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
const proposals = [];
|
|
14
|
+
for (const candidate of [...collectForeignKeys(state, options), ...collectSoftPrimaryKeys(state, options)]) {
|
|
15
|
+
const proposal = evaluateCandidate(state, candidate, indexesByTable, options);
|
|
16
|
+
const id = `${tableKey(candidate.SchemaName, candidate.TableName)}(${candidate.Columns.map(lower).join(',')})`;
|
|
17
|
+
if (proposal && !seen.has(id)) {
|
|
18
|
+
seen.add(id);
|
|
19
|
+
proposals.push(proposal);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return proposals.sort((a, b) => b.Priority - a.Priority);
|
|
23
|
+
}
|
|
24
|
+
/** Flags existing indexes whose key columns are a prefix of (or equal to) another index's. */
|
|
25
|
+
export function FindRedundantIndexes(existing) {
|
|
26
|
+
const redundant = [];
|
|
27
|
+
for (const indexes of groupByTable(existing).values()) {
|
|
28
|
+
for (const candidate of indexes.filter(isDroppableShape)) {
|
|
29
|
+
const coveredBy = indexes.find((other) => other !== candidate && coversAsPrefix(other, candidate));
|
|
30
|
+
if (coveredBy) {
|
|
31
|
+
redundant.push({
|
|
32
|
+
SchemaName: candidate.SchemaName,
|
|
33
|
+
TableName: candidate.TableName,
|
|
34
|
+
IndexName: candidate.IndexName,
|
|
35
|
+
CoveredBy: coveredBy.IndexName,
|
|
36
|
+
Reason: `Its key columns (${candidate.Columns.join(', ')}) are a leading prefix of ${coveredBy.IndexName} (${coveredBy.Columns.join(', ')}).`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return redundant;
|
|
42
|
+
}
|
|
43
|
+
/** True when an index on `table` already starts with exactly these columns. */
|
|
44
|
+
export function IsCovered(columns, tableIndexes) {
|
|
45
|
+
return tableIndexes.some((ix) => !ix.FilterDefinition && startsWith(ix.Columns, columns));
|
|
46
|
+
}
|
|
47
|
+
function evaluateCandidate(state, candidate, indexesByTable, options) {
|
|
48
|
+
const table = FindTable(state, candidate.SchemaName, candidate.TableName);
|
|
49
|
+
if (!table || table.rowCount < options.MinRowCount) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (IsCovered(candidate.Columns, indexesByTable.get(tableKey(candidate.SchemaName, candidate.TableName)) ?? [])) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const leading = table.columns.find((c) => lower(c.name) === lower(candidate.Columns[0]));
|
|
56
|
+
if (!leading || IsPoorKeyColumn(leading)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
SchemaName: candidate.SchemaName,
|
|
61
|
+
TableName: table.name,
|
|
62
|
+
Columns: candidate.Columns,
|
|
63
|
+
IncludeColumns: [],
|
|
64
|
+
Source: candidate.Source,
|
|
65
|
+
Reason: describe(candidate),
|
|
66
|
+
Priority: IndexPriority(table.rowCount),
|
|
67
|
+
Evidence: { ...candidate.Evidence, RowCount: table.rowCount, LeadingColumnDistinctCount: leading.statistics?.distinctCount }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function collectForeignKeys(state, options) {
|
|
71
|
+
const declared = state.schemas.flatMap((schema) => schema.tables.flatMap((table) => table.dependsOn.map((fk) => ({
|
|
72
|
+
SchemaName: schema.name,
|
|
73
|
+
TableName: table.name,
|
|
74
|
+
Columns: [fk.column],
|
|
75
|
+
Source: 'FKCoverage',
|
|
76
|
+
Evidence: { References: `${fk.schema}.${fk.table}.${fk.referencedColumn}`, KeyOrigin: 'declared' }
|
|
77
|
+
}))));
|
|
78
|
+
if (!options.IncludeDiscoveredFKs) {
|
|
79
|
+
return declared;
|
|
80
|
+
}
|
|
81
|
+
const discovered = (state.phases.keyDetection?.discovered.foreignKeys ?? [])
|
|
82
|
+
.filter((fk) => fk.status === 'confirmed' && fk.confidence >= options.KeyMinConfidence)
|
|
83
|
+
.map((fk) => ({
|
|
84
|
+
SchemaName: fk.schemaName,
|
|
85
|
+
TableName: fk.sourceTable,
|
|
86
|
+
Columns: [fk.sourceColumn],
|
|
87
|
+
Source: 'FKCoverage',
|
|
88
|
+
Evidence: { References: `${fk.targetSchema}.${fk.targetTable}.${fk.targetColumn}`, KeyConfidence: fk.confidence, KeyOrigin: 'discovered' }
|
|
89
|
+
}));
|
|
90
|
+
return [...declared, ...discovered];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Confirmed discovered primary keys. A table whose key is declared in the database already has
|
|
94
|
+
* the key's index, so {@link IsCovered} drops those; only truly undeclared keys get proposals.
|
|
95
|
+
*/
|
|
96
|
+
function collectSoftPrimaryKeys(state, options) {
|
|
97
|
+
return (state.phases.keyDetection?.discovered.primaryKeys ?? [])
|
|
98
|
+
.filter((pk) => pk.status === 'confirmed' && pk.confidence >= options.KeyMinConfidence)
|
|
99
|
+
.map((pk) => ({
|
|
100
|
+
SchemaName: pk.schemaName,
|
|
101
|
+
TableName: pk.tableName,
|
|
102
|
+
Columns: pk.columnNames,
|
|
103
|
+
Source: 'SoftPrimaryKey',
|
|
104
|
+
Evidence: { KeyConfidence: pk.confidence, KeyOrigin: 'discovered' }
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
function describe(candidate) {
|
|
108
|
+
if (candidate.Source === 'SoftPrimaryKey') {
|
|
109
|
+
return `Discovered primary key (${candidate.Columns.join(', ')}) has no index; lookups by key scan the table.`;
|
|
110
|
+
}
|
|
111
|
+
const origin = candidate.Evidence.KeyOrigin === 'declared' ? 'Foreign key' : 'Discovered foreign key';
|
|
112
|
+
return `${origin} ${candidate.Columns.join(', ')} → ${candidate.Evidence.References} has no index; joins and parent deletes scan the table.`;
|
|
113
|
+
}
|
|
114
|
+
/** Larger tables benefit most: 1k rows → ~30, 100k → ~50, 10M → ~70, capped at 100. */
|
|
115
|
+
export function IndexPriority(rowCount) {
|
|
116
|
+
return Math.max(1, Math.min(100, Math.round(10 * Math.log10(Math.max(rowCount, 1)))));
|
|
117
|
+
}
|
|
118
|
+
/** True for columns that make poor leading index keys (large/unstructured types, ≤2 distinct values). */
|
|
119
|
+
export function IsPoorKeyColumn(column) {
|
|
120
|
+
const distinct = column.statistics?.distinctCount;
|
|
121
|
+
return POOR_KEY_TYPES.test(column.dataType.trim()) || (distinct !== undefined && distinct <= 2);
|
|
122
|
+
}
|
|
123
|
+
function isDroppableShape(ix) {
|
|
124
|
+
return !ix.IsPrimaryKey && !ix.IsUnique && !ix.FilterDefinition && !ix.HasExpressions && ix.Columns.length > 0;
|
|
125
|
+
}
|
|
126
|
+
function coversAsPrefix(other, candidate) {
|
|
127
|
+
if (other.FilterDefinition || other.HasExpressions || !sameMethod(other, candidate)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const strictlyLonger = other.Columns.length > candidate.Columns.length;
|
|
131
|
+
const identical = other.Columns.length === candidate.Columns.length && other.IndexName < candidate.IndexName;
|
|
132
|
+
return (strictlyLonger || identical) && startsWith(other.Columns, candidate.Columns);
|
|
133
|
+
}
|
|
134
|
+
function sameMethod(a, b) {
|
|
135
|
+
const norm = (m) => (/^(nonclustered|clustered|btree)$/i.test(m) ? 'btree' : lower(m));
|
|
136
|
+
return norm(a.Method) === norm(b.Method);
|
|
137
|
+
}
|
|
138
|
+
function startsWith(indexColumns, columns) {
|
|
139
|
+
return columns.length > 0 && columns.length <= indexColumns.length && columns.every((c, i) => lower(c) === lower(indexColumns[i]));
|
|
140
|
+
}
|
|
141
|
+
function groupByTable(indexes) {
|
|
142
|
+
const map = new Map();
|
|
143
|
+
for (const ix of indexes) {
|
|
144
|
+
const key = tableKey(ix.SchemaName, ix.TableName);
|
|
145
|
+
map.set(key, [...(map.get(key) ?? []), ix]);
|
|
146
|
+
}
|
|
147
|
+
return map;
|
|
148
|
+
}
|
|
149
|
+
export function FindTable(state, schemaName, tableName) {
|
|
150
|
+
return state.schemas
|
|
151
|
+
.find((s) => lower(s.name) === lower(schemaName))
|
|
152
|
+
?.tables.find((t) => lower(t.name) === lower(tableName));
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=IndexRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexRules.js","sourceRoot":"","sources":["../../../src/plugins/index-advisor/IndexRules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin system exports. External plugins import BaseAutoDocPlugin and the types from here.
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { BaseAutoDocPlugin } from './BaseAutoDocPlugin.js';
|
|
6
|
+
export { PluginManager, LoadedAutoDocPlugin, AutoDocPluginRunContext } from './PluginManager.js';
|
|
7
|
+
export { BUILT_IN_PLUGINS, BuiltInAutoDocPlugin } from './builtins.js';
|
|
8
|
+
export * from './index-advisor/IndexAdvisorTypes.js';
|
|
9
|
+
export { IndexAdvisorPlugin, INDEX_ADVISOR_PLUGIN_NAME, DEFAULT_INDEX_ADVISOR_OPTIONS, GetIndexAdvisorData, ResolveIndexAdvisorOptions } from './index-advisor/IndexAdvisorPlugin.js';
|
|
10
|
+
export { ProposeIndexes, FindRedundantIndexes, IsCovered } from './index-advisor/IndexRules.js';
|
|
11
|
+
export { IndexMigrationGenerator } from './index-advisor/IndexMigrationGenerator.js';
|
|
12
|
+
export { IndexLLMReviewer, SelectReviewTables, BuildReviewContext, ApplyReviewResponse, IndexReviewTarget, IndexReviewApplyResult } from './index-advisor/IndexLLMReview.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|