UNPKG

@sqlsmith/core

Version:

Core SQL schema merging engine with dependency resolution

107 lines 4.28 kB
export class TopologicalSorter { #logger; constructor(logger) { this.#logger = logger; } /** * Sort SQL statements using Kahn's algorithm */ sortStatements(statements, graph) { // Create maps for quick lookups const statementMap = new Map(); const fileMap = new Map(); for (const statement of statements) { statementMap.set(statement.name, statement); fileMap.set(statement.name, statement.filePath); } // Kahn's algorithm implementation const inDegree = new Map(); const queue = []; // Initialize in-degree for all nodes for (const node of graph.nodes) { const dependencies = graph.edges.get(node) || new Set(); // Exclude self-references from in-degree calculation (hierarchical structures) const nonSelfDependencies = Array.from(dependencies).filter((dep) => dep !== node); inDegree.set(node, nonSelfDependencies.length); // Add nodes with no dependencies to the queue if (nonSelfDependencies.length === 0) { queue.push(node); } } const sortedNames = []; // Process the queue while (queue.length > 0) { const current = queue.shift(); if (current === undefined) { break; } sortedNames.push(current); // Update in-degree for all dependents const dependents = graph.reversedEdges.get(current) || new Set(); for (const dependent of dependents) { // Skip self-references in dependency processing if (dependent === current) { continue; } const currentDegree = inDegree.get(dependent) || 0; const newDegree = currentDegree - 1; inDegree.set(dependent, newDegree); // If this statement now has no more dependencies, add it to queue if (newDegree === 0) { queue.push(dependent); } } } // Verify we processed all nodes if (sortedNames.length !== graph.nodes.size) { throw new Error('Topological sort failed: not all nodes were processed (unexpected cycle detected)'); } // Convert sorted names back to SQL statements const sortedStatements = []; for (const name of sortedNames) { const statement = statementMap.get(name); if (statement) { sortedStatements.push(statement); } } this.#logSortResults(sortedStatements); return sortedStatements; } /** * Sort SQL files (legacy compatibility method) */ sortFiles(files, graph) { // Extract all statements from files const allStatements = []; const fileToStatements = new Map(); for (const file of files) { fileToStatements.set(file.path, file.statements); allStatements.push(...file.statements); } // Sort statements const sortedStatements = this.sortStatements(allStatements, graph); // Convert back to files in order, ensuring each file appears only once const sortedFiles = []; const processedFiles = new Set(); for (const statement of sortedStatements) { if (!processedFiles.has(statement.filePath)) { const originalFile = files.find((f) => f.path === statement.filePath); if (originalFile) { sortedFiles.push(originalFile); processedFiles.add(statement.filePath); } } } return sortedFiles; } #logSortResults(sortedStatements) { this.#logger.header('🔄 Topological Sort Result', '-'); sortedStatements.forEach((statement, index) => { const fileName = statement.filePath.split('/').pop(); const type = statement.type.toUpperCase(); this.#logger.info(`${index + 1}. ${fileName} (${type}: ${statement.name})`); }); this.#logger.raw(''); } } //# sourceMappingURL=topological-sorter.js.map