UNPKG

rawsql-ts

Version:

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

220 lines (219 loc) 8.24 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; } /** * 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) * - 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 * ``` * * @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[]; /** * 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; /** * Extracts the name from a CommonTable */ private getCTEName; }