UNPKG

rawsql-ts

Version:

[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.

289 lines (288 loc) 11.1 kB
import { SimpleSelectQuery } from "../models/SimpleSelectQuery"; import { SqlFormatterOptions } from "./SqlFormatter"; /** * Interface representing a decomposed CTE with executable query * @public */ export interface DecomposedCTE { /** Name of the CTE */ name: string; /** Executable SQL query for this CTE (includes dependencies) */ query: string; /** Array of CTE names that this CTE depends on */ dependencies: string[]; /** Array of CTE names that depend on this CTE */ dependents: string[]; /** Whether this CTE is recursive */ isRecursive: boolean; } /** * Options for CTEQueryDecomposer extending SqlFormatterOptions * @public */ export interface CTEDecomposerOptions extends SqlFormatterOptions { /** Whether to add comments to decomposed queries showing metadata and dependencies */ addComments?: boolean; } /** * Result of CTE SQL restoration containing executable query and metadata * @public */ export interface CTERestorationResult { /** Name of the CTE */ name: string; /** Executable SQL query including all dependencies */ executableSql: string; /** Array of CTE names that this CTE depends on (in execution order) */ dependencies: string[]; /** Any warnings encountered during restoration */ warnings: string[]; } /** * Decomposes complex CTEs into executable standalone queries * * This class analyzes Common Table Expressions and generates executable standalone queries * for each CTE, making complex CTE debugging easier. It supports: * - Recursive CTE detection and handling * - Dependency analysis (dependencies and dependents for each CTE) * - CTE SQL Restoration: Generate executable SQL for a specific CTE with its dependencies * - Configurable SQL formatter options (MySQL, PostgreSQL, custom styles) * - Optional comment generation showing CTE metadata and relationships * - Comprehensive error handling for circular dependencies * * @example * ```typescript * const decomposer = new CTEQueryDecomposer({ * preset: 'postgres', * addComments: true, * keywordCase: 'upper' * }); * * const query = ` * with users_data as (select * from users), * active_users as (select * from users_data where active = true) * select * from active_users * `; * * const decomposed = decomposer.decompose(SelectQueryParser.parse(query)); * // Returns array of DecomposedCTE objects with executable queries * * // Or restore a specific CTE for debugging: * const restored = decomposer.extractCTE(SelectQueryParser.parse(query), 'active_users'); * console.log(restored.executableSql); // Standalone executable SQL with dependencies * ``` * * @public */ export declare class CTEQueryDecomposer { private static readonly ERROR_MESSAGES; private static readonly COMMENT_TEXTS; private readonly dependencyAnalyzer; private readonly cteCollector; private readonly formatter; private readonly options; /** * Creates a new CTEQueryDecomposer instance * @param options - Configuration options extending SqlFormatterOptions */ constructor(options?: CTEDecomposerOptions); /** * Decomposes CTEs in a query into executable standalone queries * * This method analyzes the query structure to: * 1. Collect all CTEs and analyze their dependencies * 2. Detect recursive CTEs and handle them separately * 3. Generate executable queries for each CTE including required dependencies * 4. Add optional comments with metadata (if addComments option is enabled) * 5. Format output according to specified formatter options * * @param query - The SimpleSelectQuery containing CTEs to decompose * @returns Array of decomposed CTEs with executable queries, dependencies, and metadata * @throws Error if circular dependencies are detected in non-recursive CTEs * * @example * ```typescript * const query = SelectQueryParser.parse(` * with base as (select * from users), * filtered as (select * from base where active = true) * select * from filtered * `); * * const result = decomposer.decompose(query); * // Returns: * // [ * // { name: 'base', query: 'select * from users', dependencies: [], ... }, * // { name: 'filtered', query: 'with base as (...) select * from base where active = true', dependencies: ['base'], ... } * // ] * ``` */ decompose(query: SimpleSelectQuery): DecomposedCTE[]; /** * Synchronizes edited CTEs back into a unified query and re-decomposes them * * This method resolves inconsistencies between edited CTEs by: * 1. Composing the edited CTEs into a unified query * 2. Parsing the unified query to ensure consistency * 3. Re-decomposing the synchronized query * * This is useful when CTEs have been edited independently and may have * inconsistencies that need to be resolved through a unified composition. * * @param editedCTEs - Array of edited CTEs that may have inconsistencies * @param rootQuery - The main query that uses the CTEs * @returns Array of re-decomposed CTEs with resolved inconsistencies * @throws Error if the composed query cannot be parsed or contains errors * * @example * ```typescript * // After editing CTEs independently, synchronize them * const editedCTEs = [ * { name: 'users_data', query: 'select * from users where active = true' }, * { name: 'active_users', query: 'select * from users_data where id >= 1000' } * ]; * * const synchronized = decomposer.synchronize(editedCTEs, 'select count(*) from active_users'); * // Returns re-decomposed CTEs with resolved dependencies * ``` */ synchronize(editedCTEs: Array<{ name: string; query: string; }>, rootQuery: string): DecomposedCTE[]; /** * Restores executable SQL for a specific CTE by including all its dependencies * * This method provides a focused API for generating standalone, executable SQL * for a specific Common Table Expression. It analyzes dependencies and includes * all required CTEs in the correct execution order. * * Key features: * - Automatic dependency resolution and ordering * - Recursive CTE detection and handling * - Error handling for circular dependencies * - Optional dependency comments for debugging * * @param query - The query containing CTEs * @param cteName - The name of the CTE to restore * @returns CTERestorationResult with executable SQL and metadata * @throws Error if CTE is not found or circular dependencies exist * * @example * ```typescript * const query = SelectQueryParser.parse(` * with users_data as (select * from users), * active_users as (select * from users_data where active = true), * premium_users as (select * from active_users where premium = true) * select * from premium_users * `); * * // Get executable SQL for 'premium_users' CTE * const result = decomposer.extractCTE(query, 'premium_users'); * // result.executableSql will contain: * // with users_data as (select * from users), * // active_users as (select * from users_data where active = true) * // select * from active_users where premium = true * ``` */ extractCTE(query: SimpleSelectQuery, cteName: string): CTERestorationResult; /** * Flattens nested WITH clauses by extracting sub-CTEs from edited CTEs * @param editedCTEs Array of edited CTEs that may contain nested WITH clauses * @returns Flattened array of CTEs with sub-CTEs extracted to top level */ private flattenNestedWithClauses; /** * Validates circular dependencies for non-recursive CTEs * @param hasRecursiveCTEs Whether the query contains recursive CTEs * @throws Error if circular dependencies exist in non-recursive CTEs */ private validateCircularDependencies; /** * Processes CTE nodes and generates decomposed CTEs * @param query Original query * @param nodes CTE dependency nodes * @param recursiveCTEs List of recursive CTE names * @returns Array of decomposed CTEs */ private processCTENodes; /** * Creates a decomposed CTE for recursive CTEs */ private createRecursiveCTE; /** * Creates a decomposed CTE for standard (non-recursive) CTEs */ private createStandardCTE; /** * Builds an executable query for a CTE by including its dependencies */ private buildExecutableQuery; /** * Collects all required CTEs for a target CTE in dependency order */ private collectRequiredCTEs; /** * Builds WITH clause from required CTEs */ private buildWithClause; /** * Finds recursive CTEs in the query * @param query The query to analyze * @param ctes All CTEs in the query * @returns Array of recursive CTE names */ private findRecursiveCTEs; /** * Checks if a WITH clause is recursive * @param query The query to check * @returns true if the query contains WITH RECURSIVE */ private isRecursiveWithClause; /** * Adds comments to the query if addComments option is enabled * @param query The query string to add comments to * @param cteName The name of the CTE * @param dependencies Array of dependency names * @param dependents Array of dependent names * @param isRecursive Whether this is a recursive CTE * @returns Query with comments added if enabled, otherwise original query */ private addCommentsToQuery; /** * Generates comment lines for a CTE * @param cteName The name of the CTE * @param dependencies Array of dependency names * @param dependents Array of dependent names * @param isRecursive Whether this is a recursive CTE * @returns Array of comment strings */ private generateComments; /** * Adds text comments as SQL comments when AST parsing fails * @param query Original query * @param cteName The name of the CTE * @param dependencies Array of dependency names * @param dependents Array of dependent names * @param isRecursive Whether this is a recursive CTE * @returns Query with text comments prepended */ private addTextCommentsToQuery; /** * Adds restoration comments to the executable SQL if enabled * @param sql The executable SQL * @param targetNode The target CTE node * @param warnings Any warnings to include * @returns SQL with comments added */ private addRestorationComments; /** * Gets all CTE names from an array of CTEs * @param ctes Array of CommonTable objects * @returns Array of CTE names */ private getAllCTENames; /** * Extracts the name from a CommonTable */ private getCTEName; }