()
| 385 | } |
| 386 | |
| 387 | private buildQuery(): string { |
| 388 | const parts: string[] = []; |
| 389 | |
| 390 | // Add WITH clause if CTEs exist |
| 391 | if (this._ctes.length > 0) { |
| 392 | const cteStatements = this._ctes.map((cte) => { |
| 393 | const queryStr = |
| 394 | typeof cte.query === 'string' ? cte.query : cte.query.toSQL(); |
| 395 | return `${cte.name} AS (${queryStr})`; |
| 396 | }); |
| 397 | parts.push(`WITH ${cteStatements.join(', ')}`); |
| 398 | } |
| 399 | |
| 400 | // SELECT |
| 401 | if (this._select.length > 0) { |
| 402 | parts.push( |
| 403 | 'SELECT', |
| 404 | this._select |
| 405 | // Important: Expressions are treated as raw SQL; do not run escapeDate() |
| 406 | // on them, otherwise any embedded date strings get double-escaped |
| 407 | // (e.g. ''2025-12-16 23:59:59'') which ClickHouse rejects. |
| 408 | .map((col) => |
| 409 | col instanceof Expression ? col.toString() : this.escapeDate(col) |
| 410 | ) |
| 411 | .join(', ') |
| 412 | ); |
| 413 | } else { |
| 414 | parts.push('SELECT *'); |
| 415 | } |
| 416 | |
| 417 | if (this._except.length > 0) { |
| 418 | parts.push('EXCEPT', `(${this._except.map(this.escapeDate).join(', ')})`); |
| 419 | } |
| 420 | |
| 421 | // FROM |
| 422 | if (this._from) { |
| 423 | if (this._from instanceof Expression) { |
| 424 | parts.push(`FROM (${this._from.toString()})`); |
| 425 | } else { |
| 426 | parts.push(`FROM ${this._from}${this._final ? ' FINAL' : ''}`); |
| 427 | } |
| 428 | |
| 429 | // Add joins |
| 430 | this._joins.forEach((join) => { |
| 431 | const aliasClause = join.alias ? ` ${join.alias} ` : ' '; |
| 432 | const conditionStr = join.condition ? `ON ${join.condition}` : ''; |
| 433 | parts.push( |
| 434 | `${join.type} JOIN ${join.table instanceof Query ? `(${join.table.toSQL()})` : join.table instanceof Expression ? `(${join.table.toString()})` : join.table}${aliasClause}${conditionStr}` |
| 435 | ); |
| 436 | }); |
| 437 | // Add raw joins (e.g. ARRAY JOIN) |
| 438 | this._rawJoins.forEach((join) => { |
| 439 | parts.push(join); |
| 440 | }); |
| 441 | } |
| 442 | |
| 443 | // WHERE |
| 444 | if (this._where.length > 0) { |
no test coverage detected