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
TypeScript
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;
}