diff --git a/src/operation-node/insert-query-node.ts b/src/operation-node/insert-query-node.ts index eb6553b96..936c9b6a0 100644 --- a/src/operation-node/insert-query-node.ts +++ b/src/operation-node/insert-query-node.ts @@ -6,6 +6,7 @@ import { OnDuplicateKeyNode } from './on-duplicate-key-node.js' import { OperationNode } from './operation-node.js' import { ReturningNode } from './returning-node.js' import { TableNode } from './table-node.js' +import { TopNode } from './top-node.js' import { WithNode } from './with-node.js' export type InsertQueryNodeProps = Omit @@ -22,6 +23,7 @@ export interface InsertQueryNode extends OperationNode { readonly ignore?: boolean readonly replace?: boolean readonly explain?: ExplainNode + readonly top?: TopNode } /** diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index b6d6bf57f..bbfb421f5 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -373,6 +373,7 @@ export class OperationNodeTransformer { ignore: node.ignore, replace: node.replace, explain: this.transformNode(node.explain), + top: this.transformNode(node.top), }) } diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index 8cdeb77cf..79cc84b9d 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -132,9 +132,9 @@ export class DeleteQueryBuilder } /** - * Adds a `top` clause to the query. + * Changes a `delete from` query into a `delete top from` query. * - * This clause is only supported by some dialects like MS SQL Server. + * `top` clause is only supported by some dialects like MS SQL Server. * * ### Examples * diff --git a/src/query-builder/insert-query-builder.ts b/src/query-builder/insert-query-builder.ts index 46ec4c0ce..2be48c981 100644 --- a/src/query-builder/insert-query-builder.ts +++ b/src/query-builder/insert-query-builder.ts @@ -357,6 +357,63 @@ export class InsertQueryBuilder }) } + /** + * Changes an `insert into` query to an `insert top into` query. + * + * `top` clause is only supported by some dialects like MS SQL Server. + * + * ### Examples + * + * Insert the first 5 rows: + * + * ```ts + * await db.insertInto('person') + * .top(5) + * .columns(['first_name', 'gender']) + * .expression( + * (eb) => eb.selectFrom('pet').select(['name', sql.lit('other').as('gender')]) + * ) + * .execute() + * ``` + * + * The generated SQL (MS SQL Server): + * + * ```sql + * insert top(5) into "person" ("first_name", "gender") select "name", 'other' as "gender" from "pet" + * ``` + * + * Insert the first 50 percent of rows: + * + * ```ts + * await db.insertInto('person') + * .top(50, 'percent') + * .columns(['first_name', 'gender']) + * .expression( + * (eb) => eb.selectFrom('pet').select(['name', sql.lit('other').as('gender')]) + * ) + * .execute() + * ``` + * + * The generated SQL (MS SQL Server): + * + * ```sql + * insert top(50) percent into "person" ("first_name", "gender") select "name", 'other' as "gender" from "pet" + * ``` + */ + top( + expression: number | bigint, + modifiers?: 'percent' + ): InsertQueryBuilder { + return new InsertQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithTop( + this.#props.queryNode, + expression, + modifiers + ), + }) + } + /** * Adds an `on conflict` clause to the query. * diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index 63808889f..1d0ab5c2e 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -1080,7 +1080,7 @@ export interface SelectQueryBuilder offset(offset: number): SelectQueryBuilder /** - * Adds a top clause to the query. + * Adds a `top` clause to the query. * * This clause is only supported by some dialects like MS SQL Server. * diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index dcb3316f9..e12f64f3a 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -133,9 +133,9 @@ export class UpdateQueryBuilder } /** - * Adds a `top` clause to the query. + * Changes an `update` query into a `update top` query. * - * This clause is only supported by some dialects like MS SQL Server. + * `top` clause is only supported by some dialects like MS SQL Server. * * ### Examples * diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index c466cfe2c..fef96021e 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -302,6 +302,11 @@ export class DefaultQueryCompiler this.append(' ignore') } + if (node.top) { + this.append(' ') + this.visitNode(node.top) + } + this.append(' into ') this.visitNode(node.into) diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index ed32f4136..d96f11e55 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -880,6 +880,52 @@ for (const dialect of DIALECTS) { expect(people).to.eql(values) }) } + + if (dialect === 'mssql') { + it('should insert top', async () => { + const query = ctx.db + .insertInto('person') + .top(1) + .columns(['first_name', 'gender']) + .expression((eb) => + eb.selectFrom('pet').select(['name', eb.val('other').as('gender')]) + ) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'insert top(1) into "person" ("first_name", "gender") select "name", @1 as "gender" from "pet"', + parameters: ['other'], + }, + sqlite: NOT_SUPPORTED, + }) + + await query.executeTakeFirstOrThrow() + }) + + it('should insert top percent', async () => { + const query = ctx.db + .insertInto('person') + .top(50, 'percent') + .columns(['first_name', 'gender']) + .expression((eb) => + eb.selectFrom('pet').select(['name', eb.val('other').as('gender')]) + ) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'insert top(50) percent into "person" ("first_name", "gender") select "name", @1 as "gender" from "pet"', + parameters: ['other'], + }, + sqlite: NOT_SUPPORTED, + }) + + await query.executeTakeFirstOrThrow() + }) + } }) async function getNewestPerson(