diff --git a/scripts/generate-site-examples.js b/scripts/generate-site-examples.js index e450443e3..53b1c453b 100644 --- a/scripts/generate-site-examples.js +++ b/scripts/generate-site-examples.js @@ -25,27 +25,27 @@ const CODE_LINE_REGEX = /\*(.*)/ const moreExamplesByCategory = { select: { 'select method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#select', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#select', 'selectAll method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#selectAll', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#selectAll', 'selectFrom method': 'https://kysely-org.github.io/kysely/classes/Kysely.html#selectFrom', }, where: { 'where method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#where', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#where', 'whereRef method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#whereRef', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#whereRef', }, join: { 'innerJoin method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#innerJoin', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#innerJoin', 'leftJoin method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#leftJoin', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#leftJoin', 'rightJoin method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#rightJoin', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#rightJoin', 'fullJoin method': - 'https://kysely-org.github.io/kysely/classes/SelectQueryBuilder.html#fullJoin', + 'https://kysely-org.github.io/kysely/interfaces/SelectQueryBuilder.html#fullJoin', }, insert: { 'values method': diff --git a/src/expression/expression-builder.ts b/src/expression/expression-builder.ts index def83fe44..b4711452d 100644 --- a/src/expression/expression-builder.ts +++ b/src/expression/expression-builder.ts @@ -1,4 +1,7 @@ -import { SelectQueryBuilder } from '../query-builder/select-query-builder.js' +import { + SelectQueryBuilder, + createSelectQueryBuilder, +} from '../query-builder/select-query-builder.js' import { SelectQueryNode } from '../operation-node/select-query-node.js' import { parseTableExpressionOrList, @@ -771,7 +774,7 @@ export function createExpressionBuilder( eb: undefined! as ExpressionBuilder, selectFrom(table: TableExpressionOrList): any { - return new SelectQueryBuilder({ + return createSelectQueryBuilder({ queryId: createQueryId(), executor: executor, queryNode: SelectQueryNode.create(parseTableExpressionOrList(table)), diff --git a/src/parser/parse-utils.ts b/src/parser/parse-utils.ts index da565ff84..f440b99f3 100644 --- a/src/parser/parse-utils.ts +++ b/src/parser/parse-utils.ts @@ -3,7 +3,10 @@ import { OverNode } from '../operation-node/over-node.js' import { SelectQueryNode } from '../operation-node/select-query-node.js' import { JoinBuilder } from '../query-builder/join-builder.js' import { OverBuilder } from '../query-builder/over-builder.js' -import { SelectQueryBuilder } from '../query-builder/select-query-builder.js' +import { + SelectQueryBuilder, + createSelectQueryBuilder as newSelectQueryBuilder, +} from '../query-builder/select-query-builder.js' import { QueryCreator } from '../query-creator.js' import { NOOP_QUERY_EXECUTOR } from '../query-executor/noop-query-executor.js' import { createQueryId } from '../util/query-id.js' @@ -14,7 +17,7 @@ import { } from './table-parser.js' export function createSelectQueryBuilder(): SelectQueryBuilder { - return new SelectQueryBuilder({ + return newSelectQueryBuilder({ queryId: createQueryId(), executor: NOOP_QUERY_EXECUTOR, queryNode: SelectQueryNode.create(parseTableExpressionOrList([])), diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index 7d98da63c..32c697dbb 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -83,6 +83,7 @@ export class DeleteQueryBuilder ): DeleteQueryBuilder where(factory: WhereExpressionFactory): DeleteQueryBuilder + where(expression: Expression): DeleteQueryBuilder where(...args: any[]): any { diff --git a/src/query-builder/having-interface.ts b/src/query-builder/having-interface.ts index 99246c277..288429717 100644 --- a/src/query-builder/having-interface.ts +++ b/src/query-builder/having-interface.ts @@ -19,6 +19,7 @@ export interface HavingInterface { ): HavingInterface having(factory: HavingExpressionFactory): HavingInterface + having(expression: Expression): HavingInterface /** diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index acd6d9b32..a92b7cecc 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -68,26 +68,13 @@ import { KyselyTypeError } from '../util/type-error.js' import { Selectable } from '../util/column-type.js' import { Streamable } from '../util/streamable.js' -export class SelectQueryBuilder - implements - WhereInterface, +export interface SelectQueryBuilder + extends WhereInterface, HavingInterface, Expression, Compilable, Explainable, - Streamable -{ - readonly #props: SelectQueryBuilderProps - - constructor(props: SelectQueryBuilderProps) { - this.#props = freeze(props) - } - - /** @private */ - get expressionType(): O | undefined { - return undefined - } - + Streamable { where>( lhs: RE, op: ComparisonOperatorExpression, @@ -95,31 +82,14 @@ export class SelectQueryBuilder ): SelectQueryBuilder where(factory: WhereExpressionFactory): SelectQueryBuilder - where(expression: Expression): SelectQueryBuilder - where(...args: any[]): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithWhere( - this.#props.queryNode, - parseValueBinaryOperationOrExpression(args) - ), - }) - } + where(expression: Expression): SelectQueryBuilder whereRef( lhs: ReferenceExpression, op: ComparisonOperatorExpression, rhs: ReferenceExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithWhere( - this.#props.queryNode, - parseReferentialBinaryOperation(lhs, op, rhs) - ), - }) - } + ): SelectQueryBuilder having>( lhs: RE, @@ -133,29 +103,11 @@ export class SelectQueryBuilder having(expression: Expression): SelectQueryBuilder - having(...args: any[]): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithHaving( - this.#props.queryNode, - parseValueBinaryOperationOrExpression(args) - ), - }) - } - havingRef( lhs: ReferenceExpression, op: ComparisonOperatorExpression, rhs: ReferenceExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithHaving( - this.#props.queryNode, - parseReferentialBinaryOperation(lhs, op, rhs) - ), - }) - } + ): SelectQueryBuilder /** * Adds a select statement to the query. @@ -345,15 +297,7 @@ export class SelectQueryBuilder */ select>( selection: SelectArg - ): SelectQueryBuilder> { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSelections( - this.#props.queryNode, - parseSelectArg(selection) - ), - }) - } + ): SelectQueryBuilder> /** * Adds `distinct on` expressions to the select clause. @@ -388,16 +332,6 @@ export class SelectQueryBuilder selection: RE ): SelectQueryBuilder - distinctOn(selection: ReferenceExpressionOrList): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithDistinctOn( - this.#props.queryNode, - parseReferenceExpressionOrList(selection) - ), - }) - } - /** * This can be used to add any additional SQL to the front of the query __after__ the `select` keyword. * @@ -417,15 +351,7 @@ export class SelectQueryBuilder * from `person` * ``` */ - modifyFront(modifier: Expression): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithFrontModifier( - this.#props.queryNode, - SelectModifierNode.createWithExpression(modifier.toOperationNode()) - ), - }) - } + modifyFront(modifier: Expression): SelectQueryBuilder /** * This can be used to add any additional SQL to the end of the query. @@ -450,15 +376,7 @@ export class SelectQueryBuilder * for update * ``` */ - modifyEnd(modifier: Expression): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.createWithExpression(modifier.toOperationNode()) - ), - }) - } + modifyEnd(modifier: Expression): SelectQueryBuilder /** * Makes the selection distinct. @@ -480,93 +398,37 @@ export class SelectQueryBuilder * select distinct "first_name" from "person" * ``` */ - distinct(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithFrontModifier( - this.#props.queryNode, - SelectModifierNode.create('Distinct') - ), - }) - } + distinct(): SelectQueryBuilder /** * Adds the `for update` modifier to a select query on supported databases. */ - forUpdate(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.create('ForUpdate') - ), - }) - } + forUpdate(): SelectQueryBuilder /** * Adds the `for share` modifier to a select query on supported databases. */ - forShare(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.create('ForShare') - ), - }) - } + forShare(): SelectQueryBuilder /** * Adds the `for key share` modifier to a select query on supported databases. */ - forKeyShare(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.create('ForKeyShare') - ), - }) - } + forKeyShare(): SelectQueryBuilder /** * Adds the `for no key update` modifier to a select query on supported databases. */ - forNoKeyUpdate(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.create('ForNoKeyUpdate') - ), - }) - } + forNoKeyUpdate(): SelectQueryBuilder /** * Adds the `skip locked` modifier to a select query on supported databases. */ - skipLocked(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.create('SkipLocked') - ), - }) - } + skipLocked(): SelectQueryBuilder /** * Adds the `nowait` modifier to a select query on supported databases. */ - noWait(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithEndModifier( - this.#props.queryNode, - SelectModifierNode.create('NoWait') - ), - }) - } + noWait(): SelectQueryBuilder /** * Adds a `select *` or `select table.*` clause to the query. @@ -632,16 +494,6 @@ export class SelectQueryBuilder selectAll(): SelectQueryBuilder> - selectAll(table?: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSelections( - this.#props.queryNode, - parseSelectAll(table) - ), - }) - } - /** * Joins another table to the query using an inner join. * @@ -760,22 +612,19 @@ export class SelectQueryBuilder TE extends TableExpression, K1 extends JoinReferenceExpression, K2 extends JoinReferenceExpression - >(table: TE, k1: K1, k2: K2): SelectQueryBuilderWithInnerJoin + >( + table: TE, + k1: K1, + k2: K2 + ): SelectQueryBuilderWithInnerJoin innerJoin< TE extends TableExpression, FN extends JoinCallbackExpression - >(table: TE, callback: FN): SelectQueryBuilderWithInnerJoin - - innerJoin(...args: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('InnerJoin', args) - ), - }) - } + >( + table: TE, + callback: FN + ): SelectQueryBuilderWithInnerJoin /** * Just like {@link innerJoin} but adds a left join instead of an inner join. @@ -784,22 +633,19 @@ export class SelectQueryBuilder TE extends TableExpression, K1 extends JoinReferenceExpression, K2 extends JoinReferenceExpression - >(table: TE, k1: K1, k2: K2): SelectQueryBuilderWithLeftJoin + >( + table: TE, + k1: K1, + k2: K2 + ): SelectQueryBuilderWithLeftJoin leftJoin< TE extends TableExpression, FN extends JoinCallbackExpression - >(table: TE, callback: FN): SelectQueryBuilderWithLeftJoin - - leftJoin(...args: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LeftJoin', args) - ), - }) - } + >( + table: TE, + callback: FN + ): SelectQueryBuilderWithLeftJoin /** * Just like {@link innerJoin} but adds a right join instead of an inner join. @@ -808,22 +654,19 @@ export class SelectQueryBuilder TE extends TableExpression, K1 extends JoinReferenceExpression, K2 extends JoinReferenceExpression - >(table: TE, k1: K1, k2: K2): SelectQueryBuilderWithRightJoin + >( + table: TE, + k1: K1, + k2: K2 + ): SelectQueryBuilderWithRightJoin rightJoin< TE extends TableExpression, FN extends JoinCallbackExpression - >(table: TE, callback: FN): SelectQueryBuilderWithRightJoin - - rightJoin(...args: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('RightJoin', args) - ), - }) - } + >( + table: TE, + callback: FN + ): SelectQueryBuilderWithRightJoin /** * Just like {@link innerJoin} but adds a full join instead of an inner join. @@ -832,22 +675,19 @@ export class SelectQueryBuilder TE extends TableExpression, K1 extends JoinReferenceExpression, K2 extends JoinReferenceExpression - >(table: TE, k1: K1, k2: K2): SelectQueryBuilderWithFullJoin + >( + table: TE, + k1: K1, + k2: K2 + ): SelectQueryBuilderWithFullJoin fullJoin< TE extends TableExpression, FN extends JoinCallbackExpression - >(table: TE, callback: FN): SelectQueryBuilderWithFullJoin - - fullJoin(...args: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('FullJoin', args) - ), - }) - } + >( + table: TE, + callback: FN + ): SelectQueryBuilderWithFullJoin /** * Just like {@link innerJoin} but adds a lateral join instead of an inner join. @@ -872,22 +712,19 @@ export class SelectQueryBuilder TE extends TableExpression, K1 extends JoinReferenceExpression, K2 extends JoinReferenceExpression - >(table: TE, k1: K1, k2: K2): SelectQueryBuilderWithInnerJoin + >( + table: TE, + k1: K1, + k2: K2 + ): SelectQueryBuilderWithInnerJoin innerJoinLateral< TE extends TableExpression, FN extends JoinCallbackExpression - >(table: TE, callback: FN): SelectQueryBuilderWithInnerJoin - - innerJoinLateral(...args: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LateralInnerJoin', args) - ), - }) - } + >( + table: TE, + callback: FN + ): SelectQueryBuilderWithInnerJoin /** * Just like {@link innerJoin} but adds a lateral left join instead of an inner join. @@ -911,22 +748,19 @@ export class SelectQueryBuilder TE extends TableExpression, K1 extends JoinReferenceExpression, K2 extends JoinReferenceExpression - >(table: TE, k1: K1, k2: K2): SelectQueryBuilderWithLeftJoin + >( + table: TE, + k1: K1, + k2: K2 + ): SelectQueryBuilderWithLeftJoin leftJoinLateral< TE extends TableExpression, FN extends JoinCallbackExpression - >(table: TE, callback: FN): SelectQueryBuilderWithLeftJoin - - leftJoinLateral(...args: any): any { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LateralLeftJoin', args) - ), - }) - } + >( + table: TE, + callback: FN + ): SelectQueryBuilderWithLeftJoin /** * Adds an `order by` clause to the query. @@ -1018,15 +852,7 @@ export class SelectQueryBuilder orderBy( orderBy: OrderByExpression, direction?: OrderByDirectionExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithOrderByItem( - this.#props.queryNode, - parseOrderBy(orderBy, direction) - ), - }) - } + ): SelectQueryBuilder /** * Adds a `group by` clause to the query. @@ -1126,15 +952,7 @@ export class SelectQueryBuilder * group by "first_name" * ``` */ - groupBy(groupBy: GroupByArg): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithGroupByItems( - this.#props.queryNode, - parseGroupBy(groupBy) - ), - }) - } + groupBy(groupBy: GroupByArg): SelectQueryBuilder /** * Adds a limit clause to the query. @@ -1160,15 +978,7 @@ export class SelectQueryBuilder * .limit(10) * ``` */ - limit(limit: number): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithLimit( - this.#props.queryNode, - LimitNode.create(limit) - ), - }) - } + limit(limit: number): SelectQueryBuilder /** * Adds an offset clause to the query. @@ -1185,15 +995,7 @@ export class SelectQueryBuilder * .limit(10) * ``` */ - offset(offset: number): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithOffset( - this.#props.queryNode, - OffsetNode.create(offset) - ), - }) - } + offset(offset: number): SelectQueryBuilder /** * Combines another select query or raw expression to this query using `union`. @@ -1221,17 +1023,7 @@ export class SelectQueryBuilder * .orderBy('name') * ``` */ - union( - expression: SetOperandExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSetOperation( - this.#props.queryNode, - parseSetOperation('union', expression, false) - ), - }) - } + union(expression: SetOperandExpression): SelectQueryBuilder /** * Combines another select query or raw expression to this query using `union all`. @@ -1261,15 +1053,7 @@ export class SelectQueryBuilder */ unionAll( expression: SetOperandExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSetOperation( - this.#props.queryNode, - parseSetOperation('union', expression, true) - ), - }) - } + ): SelectQueryBuilder /** * Combines another select query or raw expression to this query using `intersect`. @@ -1299,15 +1083,7 @@ export class SelectQueryBuilder */ intersect( expression: SetOperandExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSetOperation( - this.#props.queryNode, - parseSetOperation('intersect', expression, false) - ), - }) - } + ): SelectQueryBuilder /** * Combines another select query or raw expression to this query using `intersect all`. @@ -1337,15 +1113,7 @@ export class SelectQueryBuilder */ intersectAll( expression: SetOperandExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSetOperation( - this.#props.queryNode, - parseSetOperation('intersect', expression, true) - ), - }) - } + ): SelectQueryBuilder /** * Combines another select query or raw expression to this query using `except`. @@ -1373,17 +1141,7 @@ export class SelectQueryBuilder * .orderBy('name') * ``` */ - except( - expression: SetOperandExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSetOperation( - this.#props.queryNode, - parseSetOperation('except', expression, false) - ), - }) - } + except(expression: SetOperandExpression): SelectQueryBuilder /** * Combines another select query or raw expression to this query using `except all`. @@ -1413,15 +1171,7 @@ export class SelectQueryBuilder */ exceptAll( expression: SetOperandExpression - ): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithSetOperation( - this.#props.queryNode, - parseSetOperation('except', expression, true) - ), - }) - } + ): SelectQueryBuilder /** * Gives an alias for the query. This method is only useful for sub queries. @@ -1442,9 +1192,7 @@ export class SelectQueryBuilder * pets[0].owner_first_name * ``` */ - as(alias: A): AliasedSelectQueryBuilder { - return new AliasedSelectQueryBuilder(this, alias) - } + as(alias: A): AliasedSelectQueryBuilder /** * Clears all select clauses from the query. @@ -1464,19 +1212,9 @@ export class SelectQueryBuilder * select "id", "gender" from "person" * ``` */ - clearSelect(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithoutSelections(this.#props.queryNode), - }) - } + clearSelect(): SelectQueryBuilder - clearWhere(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithoutWhere(this.#props.queryNode), - }) - } + clearWhere(): SelectQueryBuilder /** * Clears limit clause from the query. @@ -1496,12 +1234,7 @@ export class SelectQueryBuilder * select * from "person" * ``` */ - clearLimit(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithoutLimit(this.#props.queryNode), - }) - } + clearLimit(): SelectQueryBuilder /** * Clears offset clause from the query. @@ -1522,12 +1255,7 @@ export class SelectQueryBuilder * select * from "person" limit 10 * ``` */ - clearOffset(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithoutOffset(this.#props.queryNode), - }) - } + clearOffset(): SelectQueryBuilder /** * Clears all `order by` clauses from the query. @@ -1547,12 +1275,7 @@ export class SelectQueryBuilder * select * from "person" * ``` */ - clearOrderBy(): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - queryNode: SelectQueryNode.cloneWithoutOrderBy(this.#props.queryNode), - }) - } + clearOrderBy(): SelectQueryBuilder /** * Simply calls the provided function passing `this` as the only argument. `$call` returns @@ -1577,9 +1300,7 @@ export class SelectQueryBuilder * .execute() * ``` */ - $call(func: (qb: this) => T): T { - return func(this) - } + $call(func: (qb: this) => T): T /** * Call `func(this)` if `condition` is true. @@ -1649,15 +1370,7 @@ export class SelectQueryBuilder $if( condition: boolean, func: (qb: this) => SelectQueryBuilder - ): SelectQueryBuilder> { - if (condition) { - return func(this) - } - - return new SelectQueryBuilder({ - ...this.#props, - }) - } + ): SelectQueryBuilder> /** * Change the output type of the query. @@ -1665,9 +1378,7 @@ export class SelectQueryBuilder * You should only use this method as the last resort if the types * don't support your use case. */ - $castTo(): SelectQueryBuilder { - return new SelectQueryBuilder(this.#props) - } + $castTo(): SelectQueryBuilder /** * Narrows (parts of) the output type of the query. @@ -1709,9 +1420,7 @@ export class SelectQueryBuilder * functionThatExpectsPersonWithNonNullValue(person) * ``` */ - $narrowType(): SelectQueryBuilder> { - return new SelectQueryBuilder(this.#props) - } + $narrowType(): SelectQueryBuilder> /** * Asserts that query's output row type equals the given type `T`. @@ -1754,58 +1463,29 @@ export class SelectQueryBuilder */ $assertType(): O extends T ? SelectQueryBuilder - : KyselyTypeError<`$assertType() call failed: The type passed in is not equal to the output type of the query.`> { - return new SelectQueryBuilder(this.#props) as unknown as any - } + : KyselyTypeError<`$assertType() call failed: The type passed in is not equal to the output type of the query.`> /** * Returns a copy of this SelectQueryBuilder instance with the given plugin installed. */ - withPlugin(plugin: KyselyPlugin): SelectQueryBuilder { - return new SelectQueryBuilder({ - ...this.#props, - executor: this.#props.executor.withPlugin(plugin), - }) - } + withPlugin(plugin: KyselyPlugin): SelectQueryBuilder - toOperationNode(): SelectQueryNode { - return this.#props.executor.transformQuery( - this.#props.queryNode, - this.#props.queryId - ) - } + toOperationNode(): SelectQueryNode - compile(): CompiledQuery> { - return this.#props.executor.compileQuery( - this.toOperationNode(), - this.#props.queryId - ) - } + compile(): CompiledQuery> /** * Executes the query and returns an array of rows. * * Also see the {@link executeTakeFirst} and {@link executeTakeFirstOrThrow} methods. */ - async execute(): Promise[]> { - const compiledQuery = this.compile() - - const result = await this.#props.executor.executeQuery( - compiledQuery, - this.#props.queryId - ) - - return result.rows - } + execute(): Promise[]> /** * Executes the query and returns the first result or undefined if * the query returned no result. */ - async executeTakeFirst(): Promise> { - const [result] = await this.execute() - return result as SimplifySingleResult - } + executeTakeFirst(): Promise> /** * Executes the query and returns the first result or throws if @@ -1815,101 +1495,600 @@ export class SelectQueryBuilder * provide a custom error class, or callback to throw a different * error. */ - async executeTakeFirstOrThrow( - errorConstructor: - | NoResultErrorConstructor - | ((node: QueryNode) => Error) = NoResultError - ): Promise> { - const result = await this.executeTakeFirst() - - if (result === undefined) { - const error = isNoResultErrorConstructor(errorConstructor) - ? new errorConstructor(this.toOperationNode()) - : errorConstructor(this.toOperationNode()) + executeTakeFirstOrThrow( + errorConstructor?: NoResultErrorConstructor | ((node: QueryNode) => Error) + ): Promise> - throw error - } + stream(chunkSize?: number): AsyncIterableIterator - return result as O - } + explain = Record>( + format?: ExplainFormat, + options?: Expression + ): Promise +} - async *stream(chunkSize: number = 100): AsyncIterableIterator { - const compiledQuery = this.compile() +class SelectQueryBuilderImpl + implements SelectQueryBuilder +{ + readonly #props: SelectQueryBuilderProps - const stream = this.#props.executor.stream( - compiledQuery, - chunkSize, - this.#props.queryId - ) + constructor(props: SelectQueryBuilderProps) { + this.#props = freeze(props) + } - for await (const item of stream) { - yield* item.rows - } + get expressionType(): O | undefined { + return undefined } - async explain = Record>( - format?: ExplainFormat, - options?: Expression - ): Promise { - const builder = new SelectQueryBuilder({ + where(...args: any[]): any { + return new SelectQueryBuilderImpl({ ...this.#props, - queryNode: QueryNode.cloneWithExplain( + queryNode: QueryNode.cloneWithWhere( this.#props.queryNode, - format, - options + parseValueBinaryOperationOrExpression(args) ), }) + } - return await builder.execute() + whereRef( + lhs: ReferenceExpression, + op: ComparisonOperatorExpression, + rhs: ReferenceExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithWhere( + this.#props.queryNode, + parseReferentialBinaryOperation(lhs, op, rhs) + ), + }) } -} -preventAwait( - SelectQueryBuilder, - "don't await SelectQueryBuilder instances directly. To execute the query you need to call `execute` or `executeTakeFirst`." -) + having(...args: any[]): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithHaving( + this.#props.queryNode, + parseValueBinaryOperationOrExpression(args) + ), + }) + } -export interface SelectQueryBuilderProps { - readonly queryId: QueryId - readonly queryNode: SelectQueryNode - readonly executor: QueryExecutor -} + havingRef( + lhs: ReferenceExpression, + op: ComparisonOperatorExpression, + rhs: ReferenceExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithHaving( + this.#props.queryNode, + parseReferentialBinaryOperation(lhs, op, rhs) + ), + }) + } -/** - * {@link SelectQueryBuilder} with an alias. The result of calling {@link SelectQueryBuilder.as}. - */ -export class AliasedSelectQueryBuilder< - DB, - TB extends keyof DB, - O = undefined, - A extends string = never -> implements AliasedExpression -{ - readonly #queryBuilder: SelectQueryBuilder - readonly #alias: A + select>( + selection: SelectArg + ): SelectQueryBuilder> { + return new SelectQueryBuilderImpl>({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSelections( + this.#props.queryNode, + parseSelectArg(selection) + ), + }) + } - constructor(queryBuilder: SelectQueryBuilder, alias: A) { - this.#queryBuilder = queryBuilder - this.#alias = alias + distinctOn(selection: ReferenceExpressionOrList): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithDistinctOn( + this.#props.queryNode, + parseReferenceExpressionOrList(selection) + ), + }) } - /** @private */ - get expression(): Expression { - return this.#queryBuilder + modifyFront(modifier: Expression): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithFrontModifier( + this.#props.queryNode, + SelectModifierNode.createWithExpression(modifier.toOperationNode()) + ), + }) } - /** @private */ - get alias(): A { - return this.#alias + modifyEnd(modifier: Expression): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.createWithExpression(modifier.toOperationNode()) + ), + }) } - toOperationNode(): AliasNode { - return AliasNode.create( - this.#queryBuilder.toOperationNode(), - IdentifierNode.create(this.#alias) - ) + distinct(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithFrontModifier( + this.#props.queryNode, + SelectModifierNode.create('Distinct') + ), + }) } -} + + forUpdate(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.create('ForUpdate') + ), + }) + } + + forShare(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.create('ForShare') + ), + }) + } + + forKeyShare(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.create('ForKeyShare') + ), + }) + } + + forNoKeyUpdate(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.create('ForNoKeyUpdate') + ), + }) + } + + skipLocked(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.create('SkipLocked') + ), + }) + } + + noWait(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithEndModifier( + this.#props.queryNode, + SelectModifierNode.create('NoWait') + ), + }) + } + + selectAll(table?: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSelections( + this.#props.queryNode, + parseSelectAll(table) + ), + }) + } + + innerJoin(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('InnerJoin', args) + ), + }) + } + + leftJoin(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('LeftJoin', args) + ), + }) + } + + rightJoin(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('RightJoin', args) + ), + }) + } + + fullJoin(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('FullJoin', args) + ), + }) + } + + innerJoinLateral(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('LateralInnerJoin', args) + ), + }) + } + + leftJoinLateral(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('LateralLeftJoin', args) + ), + }) + } + + orderBy( + orderBy: OrderByExpression, + direction?: OrderByDirectionExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithOrderByItem( + this.#props.queryNode, + parseOrderBy(orderBy, direction) + ), + }) + } + + groupBy(groupBy: GroupByArg): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithGroupByItems( + this.#props.queryNode, + parseGroupBy(groupBy) + ), + }) + } + + limit(limit: number): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithLimit( + this.#props.queryNode, + LimitNode.create(limit) + ), + }) + } + + offset(offset: number): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithOffset( + this.#props.queryNode, + OffsetNode.create(offset) + ), + }) + } + + union( + expression: SetOperandExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSetOperation( + this.#props.queryNode, + parseSetOperation('union', expression, false) + ), + }) + } + + unionAll( + expression: SetOperandExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSetOperation( + this.#props.queryNode, + parseSetOperation('union', expression, true) + ), + }) + } + + intersect( + expression: SetOperandExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSetOperation( + this.#props.queryNode, + parseSetOperation('intersect', expression, false) + ), + }) + } + + intersectAll( + expression: SetOperandExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSetOperation( + this.#props.queryNode, + parseSetOperation('intersect', expression, true) + ), + }) + } + + except( + expression: SetOperandExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSetOperation( + this.#props.queryNode, + parseSetOperation('except', expression, false) + ), + }) + } + + exceptAll( + expression: SetOperandExpression + ): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithSetOperation( + this.#props.queryNode, + parseSetOperation('except', expression, true) + ), + }) + } + + as(alias: A): AliasedSelectQueryBuilder { + return new AliasedSelectQueryBuilderImpl(this, alias) + } + + clearSelect(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithoutSelections(this.#props.queryNode), + }) + } + + clearWhere(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithoutWhere(this.#props.queryNode), + }) + } + + clearLimit(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithoutLimit(this.#props.queryNode), + }) + } + + clearOffset(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithoutOffset(this.#props.queryNode), + }) + } + + clearOrderBy(): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: SelectQueryNode.cloneWithoutOrderBy(this.#props.queryNode), + }) + } + + $call(func: (qb: this) => T): T { + return func(this) + } + + $if( + condition: boolean, + func: (qb: this) => SelectQueryBuilder + ): SelectQueryBuilder> { + if (condition) { + return func(this) + } + + return new SelectQueryBuilderImpl>({ + ...this.#props, + }) + } + + $castTo(): SelectQueryBuilder { + return new SelectQueryBuilderImpl(this.#props) + } + + $narrowType(): SelectQueryBuilder> { + return new SelectQueryBuilderImpl(this.#props) + } + + $assertType(): O extends T + ? SelectQueryBuilder + : KyselyTypeError<`$assertType() call failed: The type passed in is not equal to the output type of the query.`> { + return new SelectQueryBuilderImpl(this.#props) as unknown as any + } + + withPlugin(plugin: KyselyPlugin): SelectQueryBuilder { + return new SelectQueryBuilderImpl({ + ...this.#props, + executor: this.#props.executor.withPlugin(plugin), + }) + } + + toOperationNode(): SelectQueryNode { + return this.#props.executor.transformQuery( + this.#props.queryNode, + this.#props.queryId + ) + } + + compile(): CompiledQuery> { + return this.#props.executor.compileQuery( + this.toOperationNode(), + this.#props.queryId + ) + } + + async execute(): Promise[]> { + const compiledQuery = this.compile() + + const result = await this.#props.executor.executeQuery( + compiledQuery, + this.#props.queryId + ) + + return result.rows + } + + async executeTakeFirst(): Promise> { + const [result] = await this.execute() + return result as SimplifySingleResult + } + + async executeTakeFirstOrThrow( + errorConstructor: + | NoResultErrorConstructor + | ((node: QueryNode) => Error) = NoResultError + ): Promise> { + const result = await this.executeTakeFirst() + + if (result === undefined) { + const error = isNoResultErrorConstructor(errorConstructor) + ? new errorConstructor(this.toOperationNode()) + : errorConstructor(this.toOperationNode()) + + throw error + } + + return result as O + } + + async *stream(chunkSize: number = 100): AsyncIterableIterator { + const compiledQuery = this.compile() + + const stream = this.#props.executor.stream( + compiledQuery, + chunkSize, + this.#props.queryId + ) + + for await (const item of stream) { + yield* item.rows + } + } + + async explain = Record>( + format?: ExplainFormat, + options?: Expression + ): Promise { + const builder = new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithExplain( + this.#props.queryNode, + format, + options + ), + }) + + return await builder.execute() + } +} + +preventAwait( + SelectQueryBuilderImpl, + "don't await SelectQueryBuilder instances directly. To execute the query you need to call `execute` or `executeTakeFirst`." +) + +export function createSelectQueryBuilder( + props: SelectQueryBuilderProps +): SelectQueryBuilder { + return new SelectQueryBuilderImpl(props) +} + +export interface SelectQueryBuilderProps { + readonly queryId: QueryId + readonly queryNode: SelectQueryNode + readonly executor: QueryExecutor +} + +export interface AliasedSelectQueryBuilder< + DB, + TB extends keyof DB, + O = undefined, + A extends string = never +> extends AliasedExpression { + get selectQueryBuilder(): SelectQueryBuilder +} + +/** + * {@link SelectQueryBuilder} with an alias. The result of calling {@link SelectQueryBuilder.as}. + */ +class AliasedSelectQueryBuilderImpl< + DB, + TB extends keyof DB, + O = undefined, + A extends string = never +> implements AliasedSelectQueryBuilder +{ + readonly #queryBuilder: SelectQueryBuilder + readonly #alias: A + + constructor(queryBuilder: SelectQueryBuilder, alias: A) { + this.#queryBuilder = queryBuilder + this.#alias = alias + } + + get expression(): Expression { + return this.#queryBuilder + } + + get alias(): A { + return this.#alias + } + + get selectQueryBuilder(): SelectQueryBuilder { + return this.#queryBuilder + } + + toOperationNode(): AliasNode { + return AliasNode.create( + this.#queryBuilder.toOperationNode(), + IdentifierNode.create(this.#alias) + ) + } +} + +preventAwait( + AliasedSelectQueryBuilderImpl, + "don't await AliasedSelectQueryBuilder instances directly. AliasedSelectQueryBuilder should never be executed directly since it's always a part of another query." +) export type SelectQueryBuilderWithInnerJoin< DB, diff --git a/src/query-builder/where-interface.ts b/src/query-builder/where-interface.ts index 6953e1895..d25d810af 100644 --- a/src/query-builder/where-interface.ts +++ b/src/query-builder/where-interface.ts @@ -314,6 +314,7 @@ export interface WhereInterface { ): WhereInterface where(factory: WhereExpressionFactory): WhereInterface + where(expression: Expression): WhereInterface /** diff --git a/src/query-creator.ts b/src/query-creator.ts index 5b733f43e..c6b2346a3 100644 --- a/src/query-creator.ts +++ b/src/query-creator.ts @@ -1,4 +1,7 @@ -import { SelectQueryBuilder } from './query-builder/select-query-builder.js' +import { + SelectQueryBuilder, + createSelectQueryBuilder, +} from './query-builder/select-query-builder.js' import { InsertQueryBuilder } from './query-builder/insert-query-builder.js' import { DeleteQueryBuilder } from './query-builder/delete-query-builder.js' import { UpdateQueryBuilder } from './query-builder/update-query-builder.js' @@ -176,7 +179,7 @@ export class QueryCreator { ): SelectQueryBuilder, FromTables, {}> selectFrom(from: TableExpressionOrList): any { - return new SelectQueryBuilder({ + return createSelectQueryBuilder({ queryId: createQueryId(), executor: this.#props.executor, queryNode: SelectQueryNode.create( diff --git a/src/raw-builder/raw-builder.ts b/src/raw-builder/raw-builder.ts index 102fc7957..1afe4658a 100644 --- a/src/raw-builder/raw-builder.ts +++ b/src/raw-builder/raw-builder.ts @@ -4,7 +4,7 @@ import { RawNode } from '../operation-node/raw-node.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' -import { freeze, isFunction, isObject } from '../util/object-utils.js' +import { freeze } from '../util/object-utils.js' import { KyselyPlugin } from '../plugin/kysely-plugin.js' import { NOOP_QUERY_EXECUTOR } from '../query-executor/noop-query-executor.js' import { QueryExecutorProvider } from '../query-executor/query-executor-provider.js' @@ -19,17 +19,11 @@ import { isOperationNodeSource } from '../operation-node/operation-node-source.j * You shouldn't need to create `RawBuilder` instances directly. Instead you should * use the {@link sql} template tag. */ -export class RawBuilder implements Expression { - readonly #props: RawBuilderProps - - constructor(props: RawBuilderProps) { - this.#props = freeze(props) - } - - /** @private */ - get expressionType(): O | undefined { - return undefined - } +export interface RawBuilder extends Expression { + /** + * @private Without this SelectQueryBuilder extends RawBuilder and things break. + */ + get isRawBuilder(): true /** * Returns an aliased version of the SQL expression. @@ -86,9 +80,6 @@ export class RawBuilder implements Expression { */ as(alias: A): AliasedRawBuilder as(alias: Expression): AliasedRawBuilder - as(alias: string | Expression): AliasedRawBuilder { - return new AliasedRawBuilder(this, alias) - } /** * Change the output type of the raw expression. @@ -96,22 +87,64 @@ export class RawBuilder implements Expression { * This method call doesn't change the SQL in any way. This methods simply * returns a copy of this `RawBuilder` with a new output type. */ - $castTo(): RawBuilder { - return new RawBuilder({ ...this.#props }) - } + $castTo(): RawBuilder /** - * @deprecated Use `$castTo` instead. + * Adds a plugin for this SQL snippet. */ - castTo(): RawBuilder { - return this.$castTo() - } + withPlugin(plugin: KyselyPlugin): RawBuilder /** - * Adds a plugin for this SQL snippet. + * Compiles the builder to a `CompiledQuery`. + * + * ### Examples + * + * ```ts + * const { sql } = sql`select * from ${sql.table('person')}`.compile(db) + * console.log(sql) + * ``` */ + compile(executorProvider: QueryExecutorProvider): CompiledQuery + + /** + * Executes the raw query. + * + * ### Examples + * + * ```ts + * const result = await sql`select * from ${sql.table('person')}`.execute(db) + * ``` + */ + execute(executorProvider: QueryExecutorProvider): Promise> + + toOperationNode(): RawNode +} + +class RawBuilderImpl implements RawBuilder { + readonly #props: RawBuilderProps + + constructor(props: RawBuilderProps) { + this.#props = freeze(props) + } + + get expressionType(): O | undefined { + return undefined + } + + get isRawBuilder(): true { + return true + } + + as(alias: string | Expression): AliasedRawBuilder { + return new AliasedRawBuilderImpl(this, alias) + } + + $castTo(): RawBuilder { + return new RawBuilderImpl({ ...this.#props }) + } + withPlugin(plugin: KyselyPlugin): RawBuilder { - return new RawBuilder({ + return new RawBuilderImpl({ ...this.#props, plugins: this.#props.plugins !== undefined @@ -162,27 +195,31 @@ export class RawBuilder implements Expression { } } -export function isRawBuilder(obj: unknown): obj is RawBuilder { - return ( - isObject(obj) && - isFunction(obj.as) && - isFunction(obj.$castTo) && - isFunction(obj.withPlugin) && - isFunction(obj.toOperationNode) && - isFunction(obj.execute) - ) +export interface RawBuilderProps { + readonly queryId: QueryId + readonly rawNode: RawNode + readonly plugins?: ReadonlyArray +} + +export function createRawBuilder(props: RawBuilderProps): RawBuilder { + return new RawBuilderImpl(props) } preventAwait( - RawBuilder, + RawBuilderImpl, "don't await RawBuilder instances directly. To execute the query you need to call `execute`" ) /** * {@link RawBuilder} with an alias. The result of calling {@link RawBuilder.as}. */ -export class AliasedRawBuilder - implements AliasedExpression +export interface AliasedRawBuilder + extends AliasedExpression { + get rawBuilder(): RawBuilder +} + +class AliasedRawBuilderImpl + implements AliasedRawBuilder { readonly #rawBuilder: RawBuilder readonly #alias: A | Expression @@ -192,16 +229,18 @@ export class AliasedRawBuilder this.#alias = alias } - /** @private */ get expression(): Expression { return this.#rawBuilder } - /** @private */ get alias(): A | Expression { return this.#alias } + get rawBuilder(): RawBuilder { + return this.#rawBuilder + } + toOperationNode(): AliasNode { return AliasNode.create( this.#rawBuilder.toOperationNode(), @@ -212,8 +251,7 @@ export class AliasedRawBuilder } } -export interface RawBuilderProps { - readonly queryId: QueryId - readonly rawNode: RawNode - readonly plugins?: ReadonlyArray -} +preventAwait( + AliasedRawBuilderImpl, + "don't await AliasedRawBuilder instances directly. AliasedRawBuilder should never be executed directly since it's always a part of another query." +) diff --git a/src/raw-builder/sql.ts b/src/raw-builder/sql.ts index fd9c731e7..1973d85a6 100644 --- a/src/raw-builder/sql.ts +++ b/src/raw-builder/sql.ts @@ -6,7 +6,7 @@ import { parseStringReference } from '../parser/reference-parser.js' import { parseTable } from '../parser/table-parser.js' import { parseValueExpression } from '../parser/value-parser.js' import { createQueryId } from '../util/query-id.js' -import { RawBuilder } from './raw-builder.js' +import { RawBuilder, createRawBuilder } from './raw-builder.js' export interface Sql { /** @@ -374,7 +374,7 @@ export const sql: Sql = Object.assign( sqlFragments: TemplateStringsArray, ...parameters: unknown[] ): RawBuilder => { - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.create( sqlFragments, @@ -384,14 +384,14 @@ export const sql: Sql = Object.assign( }, { ref(columnReference: string): RawBuilder { - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithChild(parseStringReference(columnReference)), }) }, val(value: V): RawBuilder { - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithChild(parseValueExpression(value)), }) @@ -402,7 +402,7 @@ export const sql: Sql = Object.assign( }, table(tableReference: string): RawBuilder { - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithChild(parseTable(tableReference)), }) @@ -414,14 +414,14 @@ export const sql: Sql = Object.assign( fragments[0] = '' fragments[fragments.length - 1] = '' - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.create(fragments, ids.map(IdentifierNode.create)), }) }, lit(value: V): RawBuilder { - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithChild(ValueNode.createImmediate(value)), }) @@ -432,7 +432,7 @@ export const sql: Sql = Object.assign( }, raw(sql: string): RawBuilder { - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithSql(sql), }) @@ -453,7 +453,7 @@ export const sql: Sql = Object.assign( } } - return new RawBuilder({ + return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithChildren(nodes), })