import { SelectQuery } from "../models/SelectQuery"; import { SortConditions } from "./SqlSortInjector"; import { PaginationOptions } from "./SqlPaginationInjector"; import { SqlParameterValue } from "../models/ValueComponent"; import { SchemaInfo } from "./OptimizeUnusedLeftJoins"; import { OptionalConditionParameterStates, OptionalConditionPruningParameters } from "./PruneOptionalConditionBranches"; import type { ExistsSubqueryDefinition } from "./ExistsPredicateInjector"; export type { ExistsSubqueryDefinition }; /** * Object-form filter condition supporting scalar operators, logical grouping, * and column-anchored EXISTS/NOT EXISTS predicates. * * @example * const filter: FilterConditionObject = { * min: 10, * max: 100, * exists: { sql: 'SELECT 1 FROM orders WHERE user_id = $c0' } * }; * Related tests: packages/core/tests/transformers/DynamicQueryBuilder.test.ts */ export interface FilterConditionObject { min?: SqlParameterValue; max?: SqlParameterValue; like?: string; ilike?: string; in?: SqlParameterValue[]; any?: SqlParameterValue[]; '='?: SqlParameterValue; '>'?: SqlParameterValue; '<'?: SqlParameterValue; '>='?: SqlParameterValue; '<='?: SqlParameterValue; '!='?: SqlParameterValue; '<>'?: SqlParameterValue; or?: { column: string; [operator: string]: SqlParameterValue | string; }[]; and?: { column: string; [operator: string]: SqlParameterValue | string; }[]; column?: string; exists?: ExistsSubqueryDefinition; notExists?: ExistsSubqueryDefinition; } export interface MultiColumnExistsDefinition extends ExistsSubqueryDefinition { on: string[]; } export type FilterConditionValue = SqlParameterValue | SqlParameterValue[] | FilterConditionObject | MultiColumnExistsDefinition[]; /** * Filter conditions for dynamic query building. * * Supports both unqualified and qualified column names: * - Unqualified: `{ name: 'Alice' }` - applies to all columns named 'name' * - Qualified: `{ 'users.name': 'Bob' }` - applies only to the 'name' column in the 'users' table/alias * - Hybrid: `{ name: 'Default', 'users.name': 'Override' }` - qualified names take priority over unqualified * * @example * ```typescript * // Basic usage (backward compatible) * const filter: FilterConditions = { * name: 'Alice', * status: 'active' * }; * * // Qualified names for disambiguation in JOINs * const filter: FilterConditions = { * 'users.name': 'Alice', // Only applies to users.name * 'profiles.name': 'Bob' // Only applies to profiles.name * }; * * // Hybrid approach * const filter: FilterConditions = { * status: 'active', // Applies to all 'status' columns * 'users.name': 'Alice', // Overrides for users.name specifically * 'profiles.name': 'Bob' // Overrides for profiles.name specifically * }; * ``` * Related tests: packages/core/tests/transformers/DynamicQueryBuilder.test.ts */ export type FilterConditions = Record; /** * Options for dynamic query building */ export interface QueryBuildOptions { /** * Legacy filter input for named-parameter binding only. * Dynamic predicate injection is no longer supported and will fail fast. */ filter?: FilterConditions; /** Sort conditions to inject into ORDER BY clause */ sort?: SortConditions; /** Pagination options to inject LIMIT/OFFSET clauses */ paging?: PaginationOptions; /** * Columns that should remain in the SELECT clause. * When specified, every other column is removed so the output matches this whitelist. */ includeColumns?: string[]; /** * Columns that should be removed from the SELECT clause. * Filters apply subtractively and only drop columns that exist in the original output. */ excludeColumns?: string[]; /** * Throw when column-anchored EXISTS filters fail to resolve. * Defaults to false so invalid definitions are skipped silently. */ existsStrict?: boolean; /** * Schema metadata used when removing unused LEFT JOINs; overrides builder defaults. */ schemaInfo?: SchemaInfo; /** * Remove unused LEFT JOINs before further processing when schema info is available. */ removeUnusedLeftJoins?: boolean; /** * Remove unused Common Table Expressions (CTEs) when they can be safely pruned. * Defaults to false to preserve original WITH definitions. */ removeUnusedCtes?: boolean; /** * Explicit opt-in values for truthful optional condition branches in source SQL. * Only listed parameter names are eligible for pruning, and `null`/`undefined` are treated as absent-equivalent. */ optionalConditionParameters?: OptionalConditionPruningParameters; /** * Legacy state-map form for optional branch pruning. * Prefer `optionalConditionParameters` for new code so SQL-facing null semantics stay intuitive. */ optionalConditionParameterStates?: OptionalConditionParameterStates; } /** * Builder-level configuration that can be reused across multiple build calls. */ export interface DynamicQueryBuilderOptions { /** Optional resolver for table column names (retains backward compatibility). */ tableColumnResolver?: (tableName: string) => string[]; /** * Schema metadata that may be applied by default when the optimizer is enabled. * Schema info provided via QueryBuildOptions takes precedence. */ schemaInfo?: SchemaInfo; } /** * DynamicQueryBuilder combines SQL parsing with dynamic condition injection (filters, sorts, paging). * * Key behaviours verified in packages/core/tests/transformers/DynamicQueryBuilder.test.ts: * - Preserves the input SQL when no options are supplied. * - Applies filter, sort, and pagination in a deterministic order. * - Fails fast for removed SQL-result JSON shaping. */ export declare class DynamicQueryBuilder { private tableColumnResolver?; private defaultSchemaInfo?; /** * Creates a new DynamicQueryBuilder instance. * Accepts either the legacy table resolver or an options object that can provide schema metadata. * * @param resolverOrOptions Optional resolver or configuration object */ constructor(resolverOrOptions?: ((tableName: string) => string[]) | DynamicQueryBuilderOptions); /** * Builds a SelectQuery from SQL content with dynamic conditions. * This is a pure function that does not perform any I/O operations. * @param sqlContent Raw SQL string to parse and modify * @param options Dynamic conditions to apply (filter, sort, paging) * @returns Modified SelectQuery with all dynamic conditions applied * @example * ```typescript * const builder = new DynamicQueryBuilder(); * const query = builder.buildQuery( * 'SELECT id, name FROM users WHERE active = true', * { * filter: { status: 'premium' }, * sort: { created_at: { desc: true } }, * paging: { page: 2, pageSize: 10 } * } * ); * ``` */ buildQuery(sqlContent: string, options?: QueryBuildOptions): SelectQuery; private resolveOptionalConditionPruningParameters; private applyColumnFilters; private normalizeColumnList; private normalizeColumnIdentifier; private getSelectItemName; /** * Legacy helper for binding existing named parameters without adding new runtime predicates. * Dynamic WHERE-condition injection is no longer supported; use SSSQL scaffold/refresh instead. * * @param sqlContent Raw SQL string to parse and modify * @param filter Named parameters to bind when they already exist in the SQL * @returns Modified SelectQuery after binding existing named parameters */ buildFilteredQuery(sqlContent: string, filter: FilterConditions): SelectQuery; /** * Builds a SelectQuery with only sorting applied. * Convenience method for when you only need dynamic ORDER BY clauses. * * @param sqlContent Raw SQL string to parse and modify * @param sort Sort conditions to apply * @returns Modified SelectQuery with sort conditions applied */ buildSortedQuery(sqlContent: string, sort: SortConditions): SelectQuery; /** * Builds a SelectQuery with only pagination applied. * Convenience method for when you only need LIMIT/OFFSET clauses. * * @param sqlContent Raw SQL string to parse and modify * @param paging Pagination options to apply * @returns Modified SelectQuery with pagination applied */ buildPaginatedQuery(sqlContent: string, paging: PaginationOptions): SelectQuery; /** * Validates SQL content by attempting to parse it. * Useful for testing SQL validity without applying any modifications. * * @param sqlContent Raw SQL string to validate * @returns true if SQL is valid, throws error if invalid * @throws Error if SQL cannot be parsed */ validateSql(sqlContent: string): boolean; }