From e80b79f3fc5a1dd0cd2b2141d811e924ab8bc90e Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sun, 7 Jul 2024 23:02:15 +0300 Subject: [PATCH 01/28] `InferResult` should output plural. (#1064) --- src/util/infer-result.ts | 2 +- test/typings/test-d/infer-result.test-d.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/infer-result.ts b/src/util/infer-result.ts index d972bbe9d..b616e3386 100644 --- a/src/util/infer-result.ts +++ b/src/util/infer-result.ts @@ -56,5 +56,5 @@ type ResolveResult = O extends | UpdateResult | DeleteResult | MergeResult - ? O + ? O[] : Simplify[] diff --git a/test/typings/test-d/infer-result.test-d.ts b/test/typings/test-d/infer-result.test-d.ts index 4ec7ed3a0..94fe12515 100644 --- a/test/typings/test-d/infer-result.test-d.ts +++ b/test/typings/test-d/infer-result.test-d.ts @@ -47,7 +47,7 @@ function testInferResultInsertQuery(db: Kysely) { }) const compiledQuery0 = query0.compile() - type Expected0 = InsertResult + type Expected0 = InsertResult[] expectType>>(true) expectType>>(true) @@ -74,7 +74,7 @@ function testInferResultUpdateQuery(db: Kysely) { .where('pet.id', '=', '1') const compiledQuery0 = query0.compile() - type Expected0 = UpdateResult + type Expected0 = UpdateResult[] expectType>>(true) expectType>>(true) @@ -111,7 +111,7 @@ function testInferResultDeleteQuery(db: Kysely) { const query0 = db.deleteFrom('pet').where('id', '=', '1') const compiledQuery0 = query0.compile() - type Expected0 = DeleteResult + type Expected0 = DeleteResult[] expectType>>(true) expectType>>(true) @@ -138,7 +138,7 @@ function testInferResultMergeQuery(db: Kysely) { .thenDelete() const compiledQuery0 = query0.compile() - type Expected0 = MergeResult + type Expected0 = MergeResult[] expectType>>(true) expectType>>(true) } From 2ace385aa97db659982890736c65f21b3d71799c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Koskim=C3=A4ki?= Date: Sun, 14 Jul 2024 14:59:58 +0300 Subject: [PATCH 02/28] Support update table1, table2, ... query support. Closes #192 (#1079) --- site/docs/examples/update/0010-single-row.js | 4 +- .../examples/update/0020-complex-values.js | 4 +- .../docs/examples/update/0030-my-sql-joins.js | 7 ++ .../examples/update/0030-my-sql-joins.mdx | 40 ++++++ src/operation-node/update-query-node.ts | 10 +- src/parser/update-set-parser.ts | 17 ++- .../with-schema/with-schema-transformer.ts | 17 +-- src/query-builder/update-query-builder.ts | 41 +++++- src/query-compiler/default-query-compiler.ts | 4 + src/query-creator.ts | 41 +++--- test/node/src/update.test.ts | 117 ++++++++++++++++++ 11 files changed, 261 insertions(+), 41 deletions(-) create mode 100644 site/docs/examples/update/0030-my-sql-joins.js create mode 100644 site/docs/examples/update/0030-my-sql-joins.mdx diff --git a/site/docs/examples/update/0010-single-row.js b/site/docs/examples/update/0010-single-row.js index e82a23a65..b30a98077 100644 --- a/site/docs/examples/update/0010-single-row.js +++ b/site/docs/examples/update/0010-single-row.js @@ -5,6 +5,4 @@ export const singleRow = `const result = await db last_name: 'Aniston' }) .where('id', '=', '1') - .executeTakeFirst() - -console.log(result.numUpdatedRows)` \ No newline at end of file + .executeTakeFirst()` \ No newline at end of file diff --git a/site/docs/examples/update/0020-complex-values.js b/site/docs/examples/update/0020-complex-values.js index 3b88fee1e..caad0ebb6 100644 --- a/site/docs/examples/update/0020-complex-values.js +++ b/site/docs/examples/update/0020-complex-values.js @@ -6,6 +6,4 @@ export const complexValues = `const result = await db last_name: 'updated', })) .where('id', '=', '1') - .executeTakeFirst() - -console.log(result.numUpdatedRows)` \ No newline at end of file + .executeTakeFirst()` \ No newline at end of file diff --git a/site/docs/examples/update/0030-my-sql-joins.js b/site/docs/examples/update/0030-my-sql-joins.js new file mode 100644 index 000000000..400b23c44 --- /dev/null +++ b/site/docs/examples/update/0030-my-sql-joins.js @@ -0,0 +1,7 @@ +export const mySqlJoins = `const result = await db + .updateTable(['person', 'pet']) + .set('person.first_name', 'Updated person') + .set('pet.name', 'Updated doggo') + .whereRef('person.id', '=', 'pet.owner_id') + .where('person.id', '=', '1') + .executeTakeFirst()` \ No newline at end of file diff --git a/site/docs/examples/update/0030-my-sql-joins.mdx b/site/docs/examples/update/0030-my-sql-joins.mdx new file mode 100644 index 000000000..82c4db139 --- /dev/null +++ b/site/docs/examples/update/0030-my-sql-joins.mdx @@ -0,0 +1,40 @@ +--- +title: 'MySQL joins' +--- + +# MySQL joins + +MySQL allows you to join tables directly to the "main" table and update +rows of all joined tables. This is possible by passing all tables to the +`updateTable` method as a list and adding the `ON` conditions as `WHERE` +statements. You can then use the `set(column, value)` variant to update +columns using table qualified names. + +The `UpdateQueryBuilder` also has `innerJoin` etc. join methods, but those +can only be used as part of a PostgreSQL `update set from join` query. +Due to type complexity issues, we unfortunately can't make the same +methods work in both cases. + +import { + Playground, + exampleSetup, +} from '../../../src/components/Playground' + +import { + mySqlJoins +} from './0030-my-sql-joins' + +
+ +
+ +:::info[More examples] +The API documentation is packed with examples. The API docs are hosted [here](https://kysely-org.github.io/kysely-apidoc/), +but you can access the same documentation by hovering over functions/methods/classes in your IDE. The examples are always +just one hover away! + +For example, check out these sections: + - [set method](https://kysely-org.github.io/kysely-apidoc/classes/UpdateQueryBuilder.html#set) + - [returning method](https://kysely-org.github.io/kysely-apidoc/classes/UpdateQueryBuilder.html#returning) + - [updateTable method](https://kysely-org.github.io/kysely-apidoc/classes/Kysely.html#updateTable) +::: diff --git a/src/operation-node/update-query-node.ts b/src/operation-node/update-query-node.ts index c4188205a..a815a45c9 100644 --- a/src/operation-node/update-query-node.ts +++ b/src/operation-node/update-query-node.ts @@ -12,6 +12,7 @@ import { ExplainNode } from './explain-node.js' import { LimitNode } from './limit-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' +import { ListNode } from './list-node.js' export type UpdateValuesNode = ValueListNode | PrimitiveValueListNode @@ -39,10 +40,15 @@ export const UpdateQueryNode = freeze({ return node.kind === 'UpdateQueryNode' }, - create(table: OperationNode, withNode?: WithNode): UpdateQueryNode { + create( + tables: ReadonlyArray, + withNode?: WithNode, + ): UpdateQueryNode { return freeze({ kind: 'UpdateQueryNode', - table, + // For backwards compatibility, use the raw table node when there's only one table + // and don't rename the property to something like `tables`. + table: tables.length === 1 ? tables[0] : ListNode.create(tables), ...(withNode && { with: withNode }), }) }, diff --git a/src/parser/update-set-parser.ts b/src/parser/update-set-parser.ts index c3ce3fe4c..248491956 100644 --- a/src/parser/update-set-parser.ts +++ b/src/parser/update-set-parser.ts @@ -12,12 +12,19 @@ import { parseReferenceExpression, ReferenceExpression, } from './reference-parser.js' +import { AnyColumn, DrainOuterGeneric } from '../util/type-utils.js' -export type UpdateObject = { - [C in UpdateKeys]?: - | ValueExpression> - | undefined -} +export type UpdateObject< + DB, + TB extends keyof DB, + UT extends keyof DB = TB, +> = DrainOuterGeneric<{ + [C in AnyColumn]?: { + [T in UT]: C extends keyof DB[T] + ? ValueExpression> | undefined + : never + }[UT] +}> export type UpdateObjectFactory< DB, diff --git a/src/plugin/with-schema/with-schema-transformer.ts b/src/plugin/with-schema/with-schema-transformer.ts index 04c241d28..571bd0277 100644 --- a/src/plugin/with-schema/with-schema-transformer.ts +++ b/src/plugin/with-schema/with-schema-transformer.ts @@ -2,6 +2,7 @@ import { AggregateFunctionNode } from '../../operation-node/aggregate-function-n import { AliasNode } from '../../operation-node/alias-node.js' import { FunctionNode } from '../../operation-node/function-node.js' import { IdentifierNode } from '../../operation-node/identifier-node.js' +import { ListNode } from '../../operation-node/list-node.js' import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js' import { OperationNode } from '../../operation-node/operation-node.js' import { ReferencesNode } from '../../operation-node/references-node.js' @@ -198,14 +199,14 @@ export class WithSchemaTransformer extends OperationNodeTransformer { node: OperationNode, schemableIds: Set, ): void { - const table = TableNode.is(node) - ? node - : AliasNode.is(node) && TableNode.is(node.node) - ? node.node - : null - - if (table) { - this.#collectSchemableId(table.table, schemableIds) + if (TableNode.is(node)) { + this.#collectSchemableId(node.table, schemableIds) + } else if (AliasNode.is(node) && TableNode.is(node.node)) { + this.#collectSchemableId(node.node.table, schemableIds) + } else if (ListNode.is(node)) { + for (const table of node.items) { + this.#collectSchemableIdsFromTableExpr(table, schemableIds) + } } } diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index 6e3de86c2..80e3f0df5 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -523,8 +523,6 @@ export class UpdateQueryBuilder * }) * .where('id', '=', 1) * .executeTakeFirst() - * - * console.log(result.numUpdatedRows) * ``` * * The generated SQL (PostgreSQL): @@ -548,8 +546,6 @@ export class UpdateQueryBuilder * })) * .where('id', '=', 1) * .executeTakeFirst() - * - * console.log(result.numUpdatedRows) * ``` * * The generated SQL (PostgreSQL): @@ -634,6 +630,43 @@ export class UpdateQueryBuilder * "last_name" = $3 || $4 * where "id" = $5 * ``` + * + * + * + * MySQL allows you to join tables directly to the "main" table and update + * rows of all joined tables. This is possible by passing all tables to the + * `updateTable` method as a list and adding the `ON` conditions as `WHERE` + * statements. You can then use the `set(column, value)` variant to update + * columns using table qualified names. + * + * The `UpdateQueryBuilder` also has `innerJoin` etc. join methods, but those + * can only be used as part of a PostgreSQL `update set from join` query. + * Due to type complexity issues, we unfortunately can't make the same + * methods work in both cases. + * + * ```ts + * const result = await db + * .updateTable(['person', 'pet']) + * .set('person.first_name', 'Updated person') + * .set('pet.name', 'Updated doggo') + * .whereRef('person.id', '=', 'pet.owner_id') + * .where('person.id', '=', '1') + * .executeTakeFirst() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * update + * `person`, + * `pet` + * set + * `person`.`first_name` = ?, + * `pet`.`name` = ? + * where + * `person`.`id` = `pet`.`owner_id` + * and `person`.`id` = ? + * ``` */ set( update: UpdateObjectExpression, diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 5c86395a0..22095172b 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -808,6 +808,10 @@ export class DefaultQueryCompiler } if (node.joins) { + if (!node.from) { + throw new Error("Joins in an update query are only supported as a part of a PostgreSQL 'update set from join' query. If you want to create a MySQL 'update join set' query, see https://kysely.dev/docs/examples/update/my-sql-joins") + } + this.append(' ') this.compileList(node.joins, ' ') } diff --git a/src/query-creator.ts b/src/query-creator.ts index 63df58180..52ca3dbc8 100644 --- a/src/query-creator.ts +++ b/src/query-creator.ts @@ -463,39 +463,48 @@ export class QueryCreator { * console.log(result.numUpdatedRows) * ``` */ - updateTable( - table: TR, + updateTable( + from: TE[], ): UpdateQueryBuilder< DB, - ExtractTableAlias, - ExtractTableAlias, + ExtractTableAlias, + ExtractTableAlias, UpdateResult > - updateTable>( - table: TR, + updateTable>( + from: TE[], ): UpdateQueryBuilder< - DB & PickTableWithAlias, - ExtractTableAlias, TR>, - ExtractTableAlias, TR>, + From, + FromTables, + FromTables, UpdateResult > - updateTable>( - table: TR, + updateTable( + from: TE, ): UpdateQueryBuilder< - From, - FromTables, - FromTables, + DB, + ExtractTableAlias, + ExtractTableAlias, UpdateResult > - updateTable>(table: TR): any { + updateTable>( + from: TE, + ): UpdateQueryBuilder< + DB & PickTableWithAlias, + ExtractTableAlias, TE>, + ExtractTableAlias, TE>, + UpdateResult + > + + updateTable(tables: TableExpressionOrList): any { return new UpdateQueryBuilder({ queryId: createQueryId(), executor: this.#props.executor, queryNode: UpdateQueryNode.create( - parseTableExpression(table), + parseTableExpressionOrList(tables), this.#props.withNode, ), }) diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index b4afdc650..adcc8849e 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -577,6 +577,123 @@ for (const dialect of DIALECTS) { expect(people).to.have.length(2) }) + + it('should update joined table using set(column, value) function', async () => { + const query = ctx.db + .updateTable(['person', 'pet']) + .set('person.first_name', 'Jennifer 2') + .set('pet.name', 'Doggo 2') + .where('person.first_name', '=', 'Jennifer') + .whereRef('person.id', '=', 'pet.owner_id') + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'update `person`, `pet` set `person`.`first_name` = ?, `pet`.`name` = ? where `person`.`first_name` = ? and `person`.`id` = `pet`.`owner_id`', + parameters: ['Jennifer 2', 'Doggo 2', 'Jennifer'], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + + const jennifer = await ctx.db + .selectFrom('person') + .select(['id', 'first_name']) + .where('first_name', '=', 'Jennifer 2') + .execute() + + const doggo = await ctx.db + .selectFrom('pet') + .select(['name', 'owner_id']) + .where('name', '=', 'Doggo 2') + .execute() + + expect(jennifer).to.have.length(1) + expect(doggo).to.have.length(1) + expect(doggo[0].owner_id).to.equal(jennifer[0].id) + }) + + it('should update joined aliased table using set(column, value) function and', async () => { + const query = ctx.db + .updateTable(['person as per', 'pet as p']) + .set('per.first_name', 'Jennifer 2') + .set('p.name', 'Doggo 2') + .where('per.first_name', '=', 'Jennifer') + .whereRef('per.id', '=', 'p.owner_id') + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'update `person` as `per`, `pet` as `p` set `per`.`first_name` = ?, `p`.`name` = ? where `per`.`first_name` = ? and `per`.`id` = `p`.`owner_id`', + parameters: ['Jennifer 2', 'Doggo 2', 'Jennifer'], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + + const jennifer = await ctx.db + .selectFrom('person') + .select(['id', 'first_name']) + .where('first_name', '=', 'Jennifer 2') + .execute() + + const doggo = await ctx.db + .selectFrom('pet') + .select(['name', 'owner_id']) + .where('name', '=', 'Doggo 2') + .execute() + + expect(jennifer).to.have.length(1) + expect(doggo).to.have.length(1) + expect(doggo[0].owner_id).to.equal(jennifer[0].id) + }) + + it('should join expressions', async () => { + const query = ctx.db + .updateTable([ + 'person', + ctx.db.selectFrom('pet').selectAll().as('pet'), + ]) + .set('person.first_name', 'Jennifer 2') + .where('person.first_name', '=', 'Jennifer') + .whereRef('person.id', '=', 'pet.owner_id') + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'update `person`, (select * from `pet`) as `pet` set `person`.`first_name` = ? where `person`.`first_name` = ? and `person`.`id` = `pet`.`owner_id`', + parameters: ['Jennifer 2', 'Jennifer'], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + }) + + it('should update joined table using set(object) function', async () => { + const query = ctx.db + .updateTable(['person', 'pet']) + .set({ name: 'Doggo 2' }) + .where('person.first_name', '=', 'Jennifer') + .whereRef('person.id', '=', 'pet.owner_id') + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'update `person`, `pet` set `name` = ? where `person`.`first_name` = ? and `person`.`id` = `pet`.`owner_id`', + parameters: ['Doggo 2', 'Jennifer'], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + }) } it('should create an update query that uses a CTE', async () => { From 6bbe836acb27d39a4cef27dc0e3fa5dca469cb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Koskim=C3=A4ki?= Date: Fri, 19 Jul 2024 12:34:50 +0300 Subject: [PATCH 03/28] Speedup types with huge databases. Fixes #867 (#1080) --- src/expression/expression-builder.ts | 31 +- src/parser/delete-from-parser.ts | 30 + src/parser/merge-into-parser.ts | 15 + src/parser/select-from-parser.ts | 29 + src/parser/table-parser.ts | 23 +- src/parser/update-parser.ts | 41 + src/query-creator.ts | 127 +- test/typings/test-d/generic.test-d.ts | 12 - test/typings/test-d/huge-db.test-d.ts | 3238 ++++++++++++++++++++++ test/typings/test-d/kysely-any.test-d.ts | 2 +- 10 files changed, 3380 insertions(+), 168 deletions(-) create mode 100644 src/parser/delete-from-parser.ts create mode 100644 src/parser/merge-into-parser.ts create mode 100644 src/parser/select-from-parser.ts create mode 100644 src/parser/update-parser.ts create mode 100644 test/typings/test-d/huge-db.test-d.ts diff --git a/src/expression/expression-builder.ts b/src/expression/expression-builder.ts index 913d8af77..318b0a8ec 100644 --- a/src/expression/expression-builder.ts +++ b/src/expression/expression-builder.ts @@ -5,13 +5,7 @@ import { import { SelectQueryNode } from '../operation-node/select-query-node.js' import { parseTableExpressionOrList, - TableExpression, - From, TableExpressionOrList, - FromTables, - ExtractTableAlias, - AnyAliasedTable, - PickTableWithAlias, parseTable, } from '../parser/table-parser.js' import { WithSchemaPlugin } from '../plugin/with-schema/with-schema-plugin.js' @@ -83,6 +77,7 @@ import { parseDataTypeExpression, } from '../parser/data-type-parser.js' import { CastNode } from '../operation-node/cast-node.js' +import { SelectFrom } from '../parser/select-from-parser.js' export interface ExpressionBuilder { /** @@ -294,29 +289,9 @@ export interface ExpressionBuilder { * that case Kysely typings wouldn't allow you to reference `pet.owner_id` * because `pet` is not joined to that query. */ - selectFrom( - from: TE[], - ): SelectQueryBuilder, {}> - - selectFrom>( - from: TE[], - ): SelectQueryBuilder, FromTables, {}> - - selectFrom( - from: TE, - ): SelectQueryBuilder, {}> - - selectFrom>( - from: TE, - ): SelectQueryBuilder< - DB & PickTableWithAlias, - TB | ExtractTableAlias, TE>, - {} - > - - selectFrom>( + selectFrom>( from: TE, - ): SelectQueryBuilder, FromTables, {}> + ): SelectFrom /** * Creates a `case` statement/operator. diff --git a/src/parser/delete-from-parser.ts b/src/parser/delete-from-parser.ts new file mode 100644 index 000000000..f46143277 --- /dev/null +++ b/src/parser/delete-from-parser.ts @@ -0,0 +1,30 @@ +import type { DeleteQueryBuilder } from '../query-builder/delete-query-builder.js' +import type { DeleteResult } from '../query-builder/delete-result.js' +import type { ShallowRecord } from '../util/type-utils.js' +import type { + ExtractTableAlias, + From, + FromTables, + TableExpressionOrList, +} from './table-parser.js' + +export type DeleteFrom> = + TE extends ReadonlyArray + ? DeleteQueryBuilder, FromTables, DeleteResult> + : TE extends keyof DB & string + ? // This branch creates a good-looking type for the most common case: + // deleteFrom('person') --> DeleteQueryBuilder. + // ExtractTableAlias is needed for the case where DB == any. Without it: + // deleteFrom('person as p') --> DeleteQueryBuilder + DeleteQueryBuilder, DeleteResult> + : // This branch creates a good-looking type for common aliased case: + // deleteFrom('person as p') --> DeleteQueryBuilder. + TE extends `${infer T} as ${infer A}` + ? T extends keyof DB + ? DeleteQueryBuilder, A, DeleteResult> + : never + : DeleteQueryBuilder< + From, + FromTables, + DeleteResult + > diff --git a/src/parser/merge-into-parser.ts b/src/parser/merge-into-parser.ts new file mode 100644 index 000000000..3a4df1fd3 --- /dev/null +++ b/src/parser/merge-into-parser.ts @@ -0,0 +1,15 @@ +import type { MergeQueryBuilder } from '../query-builder/merge-query-builder.js' +import type { MergeResult } from '../query-builder/merge-result.js' +import type { ShallowRecord } from '../util/type-utils.js' +import type { ExtractTableAlias, SimpleTableReference } from './table-parser.js' + +export type MergeInto< + DB, + TE extends SimpleTableReference, +> = TE extends keyof DB & string + ? MergeQueryBuilder, MergeResult> + : TE extends `${infer T} as ${infer A}` + ? T extends keyof DB + ? MergeQueryBuilder, A, MergeResult> + : never + : never diff --git a/src/parser/select-from-parser.ts b/src/parser/select-from-parser.ts new file mode 100644 index 000000000..521daafd5 --- /dev/null +++ b/src/parser/select-from-parser.ts @@ -0,0 +1,29 @@ +import type { SelectQueryBuilder } from '../query-builder/select-query-builder.js' +import type { ShallowRecord } from '../util/type-utils.js' +import type { + ExtractTableAlias, + From, + FromTables, + TableExpressionOrList, +} from './table-parser.js' + +export type SelectFrom< + DB, + TB extends keyof DB, + TE extends TableExpressionOrList, +> = + TE extends ReadonlyArray + ? SelectQueryBuilder, FromTables, {}> + : TE extends keyof DB & string + ? // This branch creates a good-looking type for the most common case: + // selectFrom('person') --> SelectQueryBuilder. + // ExtractTableAlias is needed for the case where DB == any. Without it: + // selectFrom('person as p') --> SelectQueryBuilder + SelectQueryBuilder, {}> + : // This branch creates a good-looking type for common aliased case: + // selectFrom('person as p') --> SelectQueryBuilder. + TE extends `${infer T} as ${infer A}` + ? T extends keyof DB + ? SelectQueryBuilder, TB | A, {}> + : never + : SelectQueryBuilder, FromTables, {}> diff --git a/src/parser/table-parser.ts b/src/parser/table-parser.ts index 9c665c16e..d9e9fed03 100644 --- a/src/parser/table-parser.ts +++ b/src/parser/table-parser.ts @@ -8,7 +8,7 @@ import { import { IdentifierNode } from '../operation-node/identifier-node.js' import { OperationNode } from '../operation-node/operation-node.js' import { AliasedExpression } from '../expression/expression.js' -import { DrainOuterGeneric, ShallowRecord } from '../util/type-utils.js' +import { DrainOuterGeneric } from '../util/type-utils.js' export type TableExpression = | AnyAliasedTable @@ -19,17 +19,9 @@ export type TableExpressionOrList = | TableExpression | ReadonlyArray> -export type TableReference = - | SimpleTableReference - | AliasedExpression - export type SimpleTableReference = AnyAliasedTable | AnyTable - export type AnyAliasedTable = `${AnyTable} as ${string}` - -export type TableReferenceOrList = - | TableReference - | ReadonlyArray> +export type AnyTable = keyof DB & string export type From = DrainOuterGeneric<{ [C in @@ -56,15 +48,6 @@ export type ExtractTableAlias = TE extends `${string} as ${infer TA}` ? TE : never -export type PickTableWithAlias< - DB, - T extends AnyAliasedTable, -> = T extends `${infer TB} as ${infer A}` - ? TB extends keyof DB - ? ShallowRecord - : never - : never - type ExtractAliasFromTableExpression = TE extends string ? TE extends `${string} as ${infer TA}` ? TA @@ -101,8 +84,6 @@ type ExtractRowTypeFromTableExpression< : never : never -type AnyTable = keyof DB & string - export function parseTableExpressionOrList( table: TableExpressionOrList, ): OperationNode[] { diff --git a/src/parser/update-parser.ts b/src/parser/update-parser.ts new file mode 100644 index 000000000..95f4a0863 --- /dev/null +++ b/src/parser/update-parser.ts @@ -0,0 +1,41 @@ +import type { UpdateQueryBuilder } from '../query-builder/update-query-builder.js' +import type { UpdateResult } from '../query-builder/update-result.js' +import type { ShallowRecord } from '../util/type-utils.js' +import type { + ExtractTableAlias, + From, + FromTables, + TableExpressionOrList, +} from './table-parser.js' + +export type UpdateTable> = + TE extends ReadonlyArray + ? UpdateQueryBuilder< + From, + FromTables, + FromTables, + UpdateResult + > + : TE extends keyof DB & string + ? // This branch creates a good-looking type for the most common case: + // updateTable('person') --> UpdateQueryBuilder. + // ExtractTableAlias is needed for the case where DB == any. Without it: + // updateTable('person as p') --> UpdateQueryBuilder + UpdateQueryBuilder< + DB, + ExtractTableAlias, + ExtractTableAlias, + UpdateResult + > + : // This branch creates a good-looking type for common aliased case: + // updateTable('person as p') --> UpdateQueryBuilder. + TE extends `${infer T} as ${infer A}` + ? T extends keyof DB + ? UpdateQueryBuilder, A, A, UpdateResult> + : never + : UpdateQueryBuilder< + From, + FromTables, + FromTables, + UpdateResult + > diff --git a/src/query-creator.ts b/src/query-creator.ts index 52ca3dbc8..1b403a715 100644 --- a/src/query-creator.ts +++ b/src/query-creator.ts @@ -11,17 +11,8 @@ import { SelectQueryNode } from './operation-node/select-query-node.js' import { UpdateQueryNode } from './operation-node/update-query-node.js' import { parseTable, - parseTableExpression, parseTableExpressionOrList, - TableExpression, - From, TableExpressionOrList, - FromTables, - TableReference, - TableReferenceOrList, - ExtractTableAlias, - AnyAliasedTable, - PickTableWithAlias, SimpleTableReference, parseAliasedTable, } from './parser/table-parser.js' @@ -52,6 +43,10 @@ import { import { MergeQueryBuilder } from './query-builder/merge-query-builder.js' import { MergeQueryNode } from './operation-node/merge-query-node.js' import { MergeResult } from './query-builder/merge-result.js' +import { SelectFrom } from './parser/select-from-parser.js' +import { DeleteFrom } from './parser/delete-from-parser.js' +import { UpdateTable } from './parser/update-parser.js' +import { MergeInto } from './parser/merge-into-parser.js' export class QueryCreator { readonly #props: QueryCreatorProps @@ -167,39 +162,17 @@ export class QueryCreator { * (select 1 as one) as "q" * ``` */ - selectFrom( - from: TE[], - ): SelectQueryBuilder, {}> - - selectFrom>( - from: TE[], - ): SelectQueryBuilder, FromTables, {}> - - selectFrom( - from: TE, - ): SelectQueryBuilder, {}> - - selectFrom>( + selectFrom>( from: TE, - ): SelectQueryBuilder< - DB & PickTableWithAlias, - ExtractTableAlias, TE>, - {} - > - - selectFrom>( - from: TE, - ): SelectQueryBuilder, FromTables, {}> - - selectFrom(from: TableExpressionOrList): any { + ): SelectFrom { return createSelectQueryBuilder({ queryId: createQueryId(), executor: this.#props.executor, queryNode: SelectQueryNode.createFrom( - parseTableExpressionOrList(from), + parseTableExpressionOrList(from as TableExpressionOrList), this.#props.withNode, ), - }) + }) as SelectFrom } /** @@ -413,31 +386,17 @@ export class QueryCreator { * where `person`.`id` = ? * ``` */ - deleteFrom( - from: TR[], - ): DeleteQueryBuilder, DeleteResult> - - deleteFrom>( - tables: TR[], - ): DeleteQueryBuilder, FromTables, DeleteResult> - - deleteFrom( - from: TR, - ): DeleteQueryBuilder, DeleteResult> - - deleteFrom>( - table: TR, - ): DeleteQueryBuilder, FromTables, DeleteResult> - - deleteFrom(tables: TableReferenceOrList): any { + deleteFrom>( + from: TE, + ): DeleteFrom { return new DeleteQueryBuilder({ queryId: createQueryId(), executor: this.#props.executor, queryNode: DeleteQueryNode.create( - parseTableExpressionOrList(tables), + parseTableExpressionOrList(from as TableExpressionOrList), this.#props.withNode, ), - }) + }) as DeleteFrom } /** @@ -463,51 +422,17 @@ export class QueryCreator { * console.log(result.numUpdatedRows) * ``` */ - updateTable( - from: TE[], - ): UpdateQueryBuilder< - DB, - ExtractTableAlias, - ExtractTableAlias, - UpdateResult - > - - updateTable>( - from: TE[], - ): UpdateQueryBuilder< - From, - FromTables, - FromTables, - UpdateResult - > - - updateTable( - from: TE, - ): UpdateQueryBuilder< - DB, - ExtractTableAlias, - ExtractTableAlias, - UpdateResult - > - - updateTable>( - from: TE, - ): UpdateQueryBuilder< - DB & PickTableWithAlias, - ExtractTableAlias, TE>, - ExtractTableAlias, TE>, - UpdateResult - > - - updateTable(tables: TableExpressionOrList): any { + updateTable>( + tables: TE, + ): UpdateTable { return new UpdateQueryBuilder({ queryId: createQueryId(), executor: this.#props.executor, queryNode: UpdateQueryNode.create( - parseTableExpressionOrList(tables), + parseTableExpressionOrList(tables as TableExpressionOrList), this.#props.withNode, ), - }) + }) as UpdateTable } /** @@ -593,19 +518,9 @@ export class QueryCreator { * then delete * ``` */ - mergeInto( - targetTable: TR, - ): MergeQueryBuilder - - mergeInto>( + mergeInto>( targetTable: TR, - ): MergeQueryBuilder< - DB & PickTableWithAlias, - ExtractTableAlias, TR>, - MergeResult - > - - mergeInto>(targetTable: TR): any { + ): MergeInto { return new MergeQueryBuilder({ queryId: createQueryId(), executor: this.#props.executor, @@ -613,7 +528,7 @@ export class QueryCreator { parseAliasedTable(targetTable), this.#props.withNode, ), - }) + }) as MergeInto } /** diff --git a/test/typings/test-d/generic.test-d.ts b/test/typings/test-d/generic.test-d.ts index 77e02a408..d961ba5f9 100644 --- a/test/typings/test-d/generic.test-d.ts +++ b/test/typings/test-d/generic.test-d.ts @@ -81,18 +81,6 @@ async function testGenericSelectHelper() { .execute() } -async function testGenericSelect( - db: Kysely, - table: T, -) { - const r1 = await db.selectFrom(table).select('id').executeTakeFirstOrThrow() - expectAssignable(r1.id) -} - -async function testGenericUpdate(db: Kysely, table: 'pet' | 'movie') { - await db.updateTable(table).set({ id: '123' }).execute() -} - async function testSelectsInVariable(db: Kysely) { const selects = [ 'first_name', diff --git a/test/typings/test-d/huge-db.test-d.ts b/test/typings/test-d/huge-db.test-d.ts new file mode 100644 index 000000000..a6d676f38 --- /dev/null +++ b/test/typings/test-d/huge-db.test-d.ts @@ -0,0 +1,3238 @@ +/** + * This file is more of a performance test. The tests in this file will become + * extremely slow if type performance regressions are introduced to the codebase. + */ + +import { expectError } from 'tsd' +import { ColumnType, Kysely } from '..' + +export type Decimal = ColumnType + +export type Generated = + T extends ColumnType + ? ColumnType + : ColumnType + +export interface Table0ec723c3d4ee7229ffba2a11539b0129 { + col_4ea20fbed0c11321d0d5e8c50873ad43: number + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tablec67d8be002857dd3a70a2c4edaa4671a { + col_869cdfb59eef781ebd6a3b78e43344e6: Generated + col_b8a21c4934fdc0366222c19c14b9e1b9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: Generated + id: Generated + col_40cee1e4d1c6b882095d5d343de8a0e2: Generated + col_458d5f1afc81ae7291a7aaad852dc12a: Generated + col_aeffdc77f4dde5fd265842bc848e92d1: Generated + col_105b287b6f42e3e08bccae5fa0bc1e57: Generated + col_b53dab55eaeda66574268ffea2dd4da0: Generated + col_0e46a6ad6b83de719c1b5b777e48c8c5: Generated + col_aff776838092862d398b58e380901753: Generated +} + +export interface Table3face097074d5d4ea47610accdd3ffc1 { + col_7d5c32a33ff432c746e4d4e37423df3a: string + col_b4e2525113738f1ed80758dbdfc77c51: string + col_d62f4d956a18fd3eec07e560acd55b59: number + col_f32428508672211d04f041977bc90510: number + col_072e823de1daac617e83074311b89962: string + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tableed617265d2d72c8e748ff8796bfa04c4 { + col_d9bc3ceaea0dddd80b93a2b5c5a7f827: number + col_3b04aeab47e304b0ae167274f324d5d3: string + col_b4e2525113738f1ed80758dbdfc77c51: string + col_7213e317986c96016a753182775fc7e0: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated +} + +export interface Table6dc0e4bf3b08bdf7391e5d833d1673d6 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_4ab5b1dd04a8cefd62107890b80103cd: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table2f01d1d7b29ae047871b62964df1ecc3 { + col_2ba95af0fa085c5e3cc3a55e48532985: number + col_7426b7174c1b7d22061329a60b94e63f: number + id: Generated +} + +export interface Table9f50dbe0b662b9cea0d016836eb72504 { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table4b8e90e811d962e132f9550bc90d5443 { + col_869cdfb59eef781ebd6a3b78e43344e6: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: Generated + id: Generated + col_40cee1e4d1c6b882095d5d343de8a0e2: Generated + col_458d5f1afc81ae7291a7aaad852dc12a: Generated + col_aeffdc77f4dde5fd265842bc848e92d1: Generated + col_105b287b6f42e3e08bccae5fa0bc1e57: Generated +} + +export interface Table0ae6c699b7b44b166093c052150a0d36 { + col_0dc6bca1e7fe092ff816894c1f2fcada: Generated + col_3f706bc8748ebe9b1edfcb4e2e03e4c5: Generated + col_e6892b0d5da4a0f546f52b29d82aab71: Generated + col_09d498d9fabfec1c421acb34c996c0f2: number + col_78a802ff9c1d651d6a168acdf039c4a7: number + id: Generated +} + +export interface Table2e45370c867ddf8f0ca7710b63dd2746 { + col_5e9ee2aa243c91c65fbf94a31806496d: string + col_7db3960deab1cd20d8b5b1f5501f3987: number + id: Generated + col_910c87c70d93d69a64538ffc358a99de: 'detalle' | 'referencias' +} + +export interface Table19f57540d944441abc6dcebc976adf96 { + col_5e9ee2aa243c91c65fbf94a31806496d: string + col_603225ce2d6e2ff429bde293c04ad734: number + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table1d1b84f13a74383d5b0324a011b609c6 { + col_bca1167f466e892a6e32edf82abd97bf: number + col_0edb7526b7143126ccfb957dd13caaeb: number + col_f9c22b71f730f5257411ff5461646fe8: number + col_e439bf701f76b768053c59bd57f81f7b: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date +} + +export interface Table42dbaa8105cb92599360aa5169283403 { + col_bca1167f466e892a6e32edf82abd97bf: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_b1dc1e3377a082304d817664243e7b17: Generated +} + +export interface Table2f81c25612f8fefd026b753a1753974a { + col_f9c22b71f730f5257411ff5461646fe8: Generated + col_6ccc67f5bce269cfd6973525010692a0: Generated + col_fb56864b5d334395e35ec45174902736: number + col_d812f85f291b30d4782f9accdc973515: number + col_fb3c6ec7d586e24e823cb17a11e0de45: Generated + col_a538a4b49163b8abcdcff07958d6290c: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: Generated + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fed6fd6b4a682868e85af10ef151f374: Generated +} + +export interface Tableded2bdb12664e58a26152ebb5fe676bb { + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_41bad005d542c027f208773d3f0f4a28: number +} + +export interface Table1c765aad5af6c98fb0ef475fadda911b { + col_6a59e77ab0765a9b1e48a245087e5bbd: Generated + col_f9c22b71f730f5257411ff5461646fe8: number + col_6ccc67f5bce269cfd6973525010692a0: Generated + col_fb3c6ec7d586e24e823cb17a11e0de45: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date +} + +export interface Table23e7a1e5ab953d2a85a5958458d0d824 { + col_fb3c6ec7d586e24e823cb17a11e0de45: number + col_3d54e846d74728347124d4872f3d61b4: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_910c87c70d93d69a64538ffc358a99de: Generated + col_41bad005d542c027f208773d3f0f4a28: number +} + +export interface Table41bb6a93b802fe08f0b054ae835e2fe4 { + col_603225ce2d6e2ff429bde293c04ad734: number + col_f9c22b71f730f5257411ff5461646fe8: number + id: Generated +} + +export interface Table8be6c29eada2e2ef68bef0c9c1401a97 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_c010021498d7345dadc4ec5be041f8d2: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_c37d5771c5258f10ea31baab67cba178: Generated +} + +export interface Tablee91e54a2c273ec3221b4d98ae8d79d12 { + col_c7062aaaa576936166196ab3c2e3342d: number + col_4ddfa5e839c6a6dacce57d66df206943: number + id: Generated +} + +export interface Tablebb08e5d0fa8fd6089eeeb1ba950944a1 { + col_d08c3aaad1d4c926a4bf283098fcc325: Generated + col_fb56864b5d334395e35ec45174902736: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_249c850f62ea50feb918b095fc56d763: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablecaf406ab258ff4b54413265206d55b45 { + col_f9c22b71f730f5257411ff5461646fe8: number + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_95a573b127ca2c23a2ed1dbc7d03d29e: string + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Table90ffcbc2bfc57b68acac037b0aeae3d4 { + col_6a59e77ab0765a9b1e48a245087e5bbd: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_15e729f544f76d3d0ad6bc3de492f077: string +} + +export interface Table1e2c9919a848fb2673ac28780aba041c { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_50cbd0336a73919113afcca87d9d1e48: string +} + +export interface Table4f240fba2b420e220c97e6e14c4819c6 { + col_869cdfb59eef781ebd6a3b78e43344e6: Generated + col_1d9d9279776c5549212200cb14336162: Generated + col_74bf7a1e74042c7a207b4e4d6144b8a1: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_4f84013a8b5e4c2b7529058c8fafcaa8: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_c55b5208e65984aac86ada39165d9d62: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_1f6eddf77651742ff1d83404930b09ae: string + col_ef1e84bf5d70755290854b1d0d60c91f: string + col_8b5da04bf1aacd462ab37eec2f0dea98: string + col_3917508388f24a50271f7088b657123c: Generated + col_eb6047359d3711dac3175270345f5c3b: string + col_0169235b1b604520a263cfa57a00b7a2: Generated +} + +export interface Tablec97e3cdd896ff553853d7817e4a866e2 { + col_08b4d026d42ae768f63552c6de119d71: Generated + col_066a120fe3abe924fdd105d2073b3a64: Generated + col_a4effcb9a007ae19664a1e3b0704e92f: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_555511eb2ff60ace56c0128985d10cb5: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_35772ed8414e25ad46fc4b92e07853a2: Generated + col_4a7f3834f24f1c5e95aded0726c807e2: Date + col_8f4a31364a65a9f539032a16e3f4ea8b: Date + col_ff9d87789325c168e0e8272e8d138a21: Generated + col_7a9cd04d755a1c1f269ce7184b943521: Date + col_7426b7174c1b7d22061329a60b94e63f: number + id: Generated + col_aac430b49cc975fb51fed9e6d9856548: Generated + col_2cf1ecc054fac526249f379db15f6114: number + col_89addf722cc48ff1df7954f97e20b10b: Generated + col_2fc68d3ca47f71df3c84961997e28d69: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_0191c0aa0d39de591b5236b304496123: number + col_f230bd30a85e2ec6bc2da7b727544ed6: number + col_f609bfdeed2c51dbb142fd03bddfd02a: number + col_2c4a4b433aedea3b93e0243fba4062f2: number + col_080cbfe43a25572bb2a66634b6d5dce8: number + col_5c63e943a6e825630ccaefcbed7a719b: number + col_a615c4f966221bfcfb8eac6397f4562e: number + col_a6925a86b76a07e17077ffd5f2990e7b: number + col_83750383555947e4ccbd9661a0948ffe: number + col_5c82df9ac5a4aec30ad4eb89769fbcd8: number + col_4ca76cee384ffac98c8a3e3c8a61bfeb: number + col_95b1eb6ceb5082a8b459a4722417922d: number + col_7d39847d54a6fc52735c6a250cfce5eb: string + col_345e4aa36e0fb44379bce371c87b70f4: Generated + col_910c87c70d93d69a64538ffc358a99de: number + col_3356a84dafce383906e79fffee87d8bf: Generated + col_b14411549804e01414d6ad1481680844: Generated + col_270d34ae82d8c18c7b46e83fd3556a59: Generated + col_9abb4592b230f6a6fa4214f5d86aaa4d: Generated + col_d512407d4f684d1284b36763038e0ed3: Generated + col_9b4ca322bec71dd7f0296cef261ae8ec: Generated + col_0aff87eb8d3d48fa89e873060f0715d4: Generated +} + +export interface Table4974bbdf9d728fb538d7507cb9f84842 { + col_77df54ec5f2bfd959a54c423b59c0d38: number + id: Generated + col_ee96bf5f8ef3f927a1a7fc723df4cd90: number + col_5607e33e4ae68a12af15c4fa9ada5c93: number +} + +export interface Tablea3c8286fc7df926fbf05e09a03e2ac77 { + col_77df54ec5f2bfd959a54c423b59c0d38: number + id: Generated + col_ee96bf5f8ef3f927a1a7fc723df4cd90: number + col_5607e33e4ae68a12af15c4fa9ada5c93: number +} + +export interface Table39bdacc8b55267ffc8bf75ff8ec564ce { + col_fee6d937834161cc73b3698fee871f5c: string + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_9b0c62311c53c898ce6699696d685a14: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_95a573b127ca2c23a2ed1dbc7d03d29e: string + col_8f4a31364a65a9f539032a16e3f4ea8b: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_25980343ed3b4c231a154e5e88396938: number + col_fb1ecce130681efa10fd55197773267d: number + col_d8ad55006c0efc8437cc487b5c5459f6: number + col_01abc2f9e295923216e8462e3a88bf5d: string + col_89ea1e31584f13b90dfa66a76569e69c: string + col_ea7e482493b72c75cf90057ba252352b: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_f8b0d195967998d8afabe1b39912ae34: string + col_ac339ab9133743c084a804cc83010c7a: string +} + +export interface Table3d68aac4deb5f76aaffa0df8c01e305a { + col_c50368ae5e839e9224eb0947f06fc605: number + col_a9564ebc3289b7a14551baf8ad5ec60a: Date + id: Generated + col_a7f4797b61012da3a9c1a8049c8a4974: number +} + +export interface Table90a64fc3cea5bc905374973771cafcd3 { + col_764a70ee8bb195d3475253200bf2e297: number + col_80ae6f72f9c170f9e1bcae48c1677802: number + id: Generated +} + +export interface Tablec479a580bb37024884d940eeacc9c4aa { + col_764a70ee8bb195d3475253200bf2e297: number + id: Generated + col_b2211a70f2ff7369336027ed47f18f7f: number +} + +export interface Table1d62a6c4a9763009cda042181b0c48af { + col_bd4e67d8c3f68e4b7117a7a88e22b287: string + id: Generated + col_e245eb517472187d10441eca2c4a9aeb: number +} + +export interface Table916f65bd1433290f26613ccd976c68a7 { + col_0c22ebe1ceea12bbe3dade54d797497d: number + id: Generated + col_53f18391d7b465f6918d73498af5abf0: string + col_bfd8f7ddc31266f25afd1b6ea43d8750: number + col_b9091a846b34e0fac6de153fa4634c7b: Generated + col_0e46a6ad6b83de719c1b5b777e48c8c5: string +} + +export interface Tabledb34593d7377d2007d44ba4b47dcec97 { + col_08de056b6d3139a95d0aaf99f8e3c52e: number + col_a538a4b49163b8abcdcff07958d6290c: number + id: Generated +} + +export interface Tablef7d7a7557d9db50bd40fe14bd830b9d4 { + col_25cd93f430d277509b60d4390636895f: string + col_352ea8d15082ec4cd9b5ffb25fc99c71: number + col_6d210c02b9f9e0fd7c6bb98e4b23d240: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_46e6bd185eb3c01ad5692d9ab01733b8: number + col_26286361ce53d0207945814927712ac2: number + col_2245275cb6562821fd22d2523a2667e8: Date + col_955e7ed9557ee9c44c59bf005ea23d2d: Date + col_1139ca108352fe62c38518aeb04c824d: string + col_699b1539895a99ae5b9de4f5e8006d7c: Generated + col_189501ea5ae0d0ec760d5848ef9b58e4: number +} + +export interface Table309c66af6ff8ccb2dcf123e1af40c9c4 { + col_821c248ea5f31b36e1fef43cbcf38495: number + col_49a7abbdc564bf92a27c31a936047cda: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_c010021498d7345dadc4ec5be041f8d2: number + col_7e53ed5979e336b304b1d25f30e1f246: Date + col_c6080ec65b20eed8dd083f2ae705fd28: string + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_1f98c1057055738f2125180731cb2d39: number + col_1d119125897e514751e695fab638cf81: number + col_ed9d312bfff3b171743cff4aa1954294: number +} + +export interface Table8921b63f9cb21e8293a8c1ddd9edc866 { + col_0a3a5156d1abf43e019d933129cf1d3d: number + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table2037e68416ae40369fba0a939e7ac693 { + col_6137cde4893c59f76f005a8123d8e8e6: Generated + col_a7743b12384fd798fdad7bff9e75bee3: Generated + id: Generated +} + +export interface Table2b3f160083004c6c92a2ce1faf87067d { + col_25cd93f430d277509b60d4390636895f: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_62bd695be89e0f07c72268b40df9c2db: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_34e7aea5cf0f74ee76b3f5445a3ac1cf: Date + col_c84d2025f9e13529cf7b4aa05275bdf6: Date + id: Generated + col_2fd6a67502ba2fc6e63b23903522d93b: Generated + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_97ec9689eca8dace826b37d2ed59250b: number + col_3f9be60473c4a309579563a7358c92f8: number + col_910c87c70d93d69a64538ffc358a99de: string + col_07b01206d9860575d30d3b2bdc4ef8b8: number + col_6200d8f84ce77a52a86aecf31d7ac101: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table5135075fea7b2d1c93603df216620907 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_47800dcca216b1b165c13199d66c3b63: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_e7fa32cb05ba9ddc8d5f75bdf1694790: string +} + +export interface Table5d4433ff31ef75511e70aa6e71495afe { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table9e258b1708bd64e877d6cca1467dee66 { + col_821c248ea5f31b36e1fef43cbcf38495: number + col_8a79872b2fd9eca20ec1e0823abe36fa: Generated + col_8ba45d6c9fbc6997e452dc62a60c32aa: Generated + col_66dc451da6180c375f5eccf6a178abbe: Generated + col_4a375049db156d117c5ad35214dfe424: number + col_535325947cf44473a2c18fad07fe9457: Generated + col_49a7abbdc564bf92a27c31a936047cda: number + col_030b50b01f2428db7db79395842e60de: Generated + col_50d92f9a5774b1f89abbbc4ac7f546f0: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_62bd695be89e0f07c72268b40df9c2db: number + col_cc9b81b3318668e7bd040b871602eafd: Generated + col_970dbef257f683fb01a38f38a4086cc5: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_c010021498d7345dadc4ec5be041f8d2: number + col_4649c587e02b1a41f7eb4967032e10c3: Generated + col_7e53ed5979e336b304b1d25f30e1f246: Date + col_60c9259051ec33ce67fb8dbca8cc0a49: Generated + id: Generated + col_e923cd6ce39f1ad88fcf58e070574612: Generated + col_6fcadaf6349923a75d54632571ccec9d: Generated + col_e1dfc335e98f95a54ebb2a3335b71817: Generated + col_a5846a54856ff3e7e3427506de9f791c: Generated + col_a846adea5ee689d2a11a692bafbc2dae: Generated + col_50eed02f38aae94f07e41395a64cf64b: Generated + col_5b75dbfc85a5e0870a34cc3bfa2cf2ae: Generated + col_2700d8a25d8d8e80c4ad5b622ea5dbec: number + col_7d39847d54a6fc52735c6a250cfce5eb: Generated + col_53a208b112254787b9ebe57338d2e92a: Generated + col_8f986281fe440bb551e6baa8d3ed2a28: Generated + col_7f4c1276d300132fccd6f6d32ccb3002: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated + col_d849e38a75aa2b36a69eff901e6af371: Generated<'auto' | 'validado'> +} + +export interface Table4ed17d356988e97fe63e4b4e3b060917 { + col_2e6b22e3aeade4be1854aa4191f7b786: number + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablea95d291bccf0be8becb18282bcbf93a9 { + col_a538a4b49163b8abcdcff07958d6290c: number + col_9dd1bc5c639dd9f27a30853ccf20cf02: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablee12f5b7476cba16c3b1480cd551d2b42 { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_2d1dc1e61d32d9314f690ef19eb8f9fd: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_1cbcac9ade6914a00f55838acea44222: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_806b0741c2f0a4352120014e6e8da433: Generated + col_0e5abe8ffb47ede536aabfba66a14e05: Generated + col_53a208b112254787b9ebe57338d2e92a: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablea6ca43be611a38117221fef4c761f97d { + col_a538a4b49163b8abcdcff07958d6290c: number + col_843fac686ca11587b97725cce2efc1d3: Date + col_2d1dc1e61d32d9314f690ef19eb8f9fd: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_1cbcac9ade6914a00f55838acea44222: number + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_59ec69fccca64cd638f1a7bf443b435c: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablef83eda9e66948a4da342fadf183e32c3 { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_983e58ca1a916d68762b2e241424e85c: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tabledfc2f0e19fe1c0e8dd04191a3035bac8 { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_be69efe0e6bc75464cc24f246e61a62d: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table93f030da0924f35ab2e57081dad1e2fb { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_22bd050ad65b2aa12639b75318b0864b: number + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table59fc5666d6be52faaeb255fb48a401ef { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_fd21606544b271feae0784633a57d457: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table29c7f8f8df026c49d8ea3ad088e8780a { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_a1625f77580ae88538a0bba96298b5ba: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table2e5e86f744edaeea91caedf4496cd21e { + col_8a79872b2fd9eca20ec1e0823abe36fa: Generated + col_8ba45d6c9fbc6997e452dc62a60c32aa: Generated + col_a538a4b49163b8abcdcff07958d6290c: Generated + col_50d92f9a5774b1f89abbbc4ac7f546f0: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_62bd695be89e0f07c72268b40df9c2db: number + col_0a1609965fcbf80a8d9847074a93618c: Generated + col_970dbef257f683fb01a38f38a4086cc5: Generated + col_95a573b127ca2c23a2ed1dbc7d03d29e: Generated + col_7e53ed5979e336b304b1d25f30e1f246: Date + col_a67abae8eff833edb8b78a2ded44f0b3: Date + id: Generated + col_a0102029c5185262158dcdb7c1ae4f51: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: Decimal + col_e85e859e6b11ce398d9c6ba4466648c2: Generated + col_6fcadaf6349923a75d54632571ccec9d: Generated + col_bd5c3d71904e9ae0f0e22b76c11590fd: Generated + col_5b75dbfc85a5e0870a34cc3bfa2cf2ae: Generated + col_910c87c70d93d69a64538ffc358a99de: string + col_ad3ab5438edd60f6c8638824072acd87: Generated + col_b1146d17cb361aeddbb1108e39afbfd5: Generated +} + +export interface Table27112b32955bc32b3cf3abc78f6041f7 { + col_f32428508672211d04f041977bc90510: string + col_2f76db193eac6ad0f152563313673ac9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_2f6e73ed4e950f4879aa2dc43e832206: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_e245eb517472187d10441eca2c4a9aeb: number +} + +export interface Table6a8129f282adc469ee9233bcf63f20b7 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aeffdc77f4dde5fd265842bc848e92d1: number +} + +export interface Table55cda7b389c3f5e26b58e806dc82d6cd { + col_0f38d1a543b96eb8d34b6fcdc9227445: Generated + col_aa0ae4d63455847b0b02a481f9f38ab7: number + col_05f720d878e84cf5edf8810ac56f2f47: Generated + col_b599f327b0f1350c83e6466016f68dee: string + col_3c21e15fe7f88da04c6eb6546f1f0c45: string + col_55caff057cf8c83fe28641f2903d6bcd: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_e3c09cb3d68235ebdf5820c49f6ef2e0: Generated + col_d48e0f172023094addf3ba3611199190: string + col_c010021498d7345dadc4ec5be041f8d2: number + col_c613b7206854402371564704dbd689db: string + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_d69c485b8a1572710f1a3736bd1ac46c: Generated<'no' | 'si'> + col_032711695edadad1acb9974d043ec642: Generated + col_ef1e84bf5d70755290854b1d0d60c91f: string + col_8b5da04bf1aacd462ab37eec2f0dea98: string +} + +export interface Tabledddb567693b67dedccc76feeaf305885 { + col_05f720d878e84cf5edf8810ac56f2f47: Generated + col_b599f327b0f1350c83e6466016f68dee: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_e3c09cb3d68235ebdf5820c49f6ef2e0: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_c613b7206854402371564704dbd689db: string + col_cd241d70370d7d244f5103c80efc4b61: Generated + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_d69c485b8a1572710f1a3736bd1ac46c: Generated + col_3cc361b85f6e092837024f5ab2bdc2fe: Generated + col_032711695edadad1acb9974d043ec642: Generated + col_ef1e84bf5d70755290854b1d0d60c91f: Generated + col_8b5da04bf1aacd462ab37eec2f0dea98: Generated +} + +export interface Tableb45f137169628885a5507a98a1fd203b { + col_f32428508672211d04f041977bc90510: number + col_f77bbbe9a817f1a5818e71efaa20b892: string + id: Generated +} + +export interface Table4d0fb43fef8f71d027e1c62ff7b83c1f { + col_f83b4cd2b2e7335a200c2da830132390: Date + id: Generated + col_fde60bdc79a24d633ee18347600cb41d: string +} + +export interface Table26d9e67842818bfccbbfca6020d3441a { + col_f32428508672211d04f041977bc90510: number + col_f77bbbe9a817f1a5818e71efaa20b892: string + id: Generated +} + +export interface Tablee6de3a3fdf132b2b794ad9358fe01719 { + col_748f83ef971bb7d351a413adbf1e8901: number + id: Generated + col_7bc8772e9a79ea91b67b0fe68827dd3b: number + col_428661c5270745369a9118d95829ff49: Generated + col_b200a854685602c04809e2fad82ee47b: Generated + col_54b8d01ca49e18a004dd65e012f28c4d: Generated +} + +export interface Table5bdeb7407d8776f9295b3ae2eb482855 { + col_f32428508672211d04f041977bc90510: number + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated +} + +export interface Table259a82d9d67d1b779666191753027e2d { + col_b606d1948094a48e695e46b18cf977b1: Generated + col_4f1b7b2483b9bb0a8b355ef69f09d65e: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_672a81e3795c2fd45450d85e0d3703e1: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tabledfcfa50cfe70d46b898ed0fe276855e4 { + col_53ca581ab7a5aef5b659bc27c81d5d51: string + id: Generated +} + +export interface Table11e00101e1b2f60d0a2acfcf46151908 { + col_f4dffeda79e0ee34f16d57241b615f53: Generated + col_b738ecc416fa96f41459da0404cdbf50: Generated + col_25cd93f430d277509b60d4390636895f: string + col_44381c5d8d2d9361dd21bb7a7facf4b0: Generated + col_0259f6499a0cb3f370a6119054eea3f3: Generated + col_b6fec9e01d482caca6ab785b9c508583: Generated + col_fe3144cfd0702470a18811a50b2a773a: Generated + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_4a2d849a39589cdab1bef664bbaf662f: Generated + col_5306bd08c84506786847c551137592aa: Generated + col_f901883e4b25324963cbb061fed4eaec: Generated + col_bb8f173dd39e2bbd2ca8bb502bdd5d7e: number + col_645e3472df388d4789ad6a12715cc7a9: Generated + col_eade66be3d5c19459dbb52d65e58a9ff: Generated + col_78f5ba4e82866b7d30d1c63b058b5042: Generated + col_ed68df9dcbc6be1e74aa52f2aaac27b3: Generated + col_9b0c62311c53c898ce6699696d685a14: Generated + col_a19773e01d56a8727f9b50bfd98b67a7: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_95a573b127ca2c23a2ed1dbc7d03d29e: Generated< + 'aceptado' | 'rechazado' | 'recibido' | null + > + col_7e208df123f1106ac3cf9c9ce582df87: Generated + col_78729121eb966294e69d86f8b656c99d: Generated + col_cac71ab1db07fe38f5915b92b9959348: Generated + col_6310539ac8c0b13a896e8b9634e1e0c8: Generated + col_8d66d088a1a8bcfb6a7477360f1a03e4: Generated + col_8f4a31364a65a9f539032a16e3f4ea8b: Date + col_bbeb11288ba33af45a8a7251cb87da2c: Generated + col_e199cc093d54f6c562ad8d547976c5df: Generated + col_e26de993b80283e73a98d2f5a7655e44: Generated + col_7a9cd04d755a1c1f269ce7184b943521: Generated + col_7426b7174c1b7d22061329a60b94e63f: number + col_1cc17f0951cd6972739a3cf858f985e2: Generated + col_0364c29fe7237f4f6ffba597544b4f46: Generated + id: Generated + col_2499572f76367bcbe5ea0e76d6a53cfa: Generated + col_3f38469c48f069fcc4f623bca8c587f6: Generated + col_d6ffe41368d46575efb6f237e05643f8: Generated + col_387852799fd39a75bb0ffbb3a088106e: Generated + col_ea14b84c81e7ebecb1faf4ce83b8826b: number + col_9a4570bd056ea2d9dd0a6dbb83206443: Generated + col_4002e95484dd522e663c6fac8757d914: Generated + col_01b47914fe373f6ade80d5d3afef6847: Generated + col_a3c438c6f272e35cd2f6e1a4f59dec67: Generated + col_ef8a0d1ea2ae246640e55a5f585324f4: Generated + col_256cd843e152753550cd6aa845f56fd8: Generated + col_6360ba5916d4e8415f464336177c02e7: Generated + col_5410929b23ebb3020d600feea427cdb7: Generated + col_2924351c3f723a33dfd4dc659a06be9c: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_83ca342b1dd50c4c7999010b9ad6683a: Generated + col_43c33f96a8b44d501f45cd6b9382f9df: Generated + col_7b4b1dd4aafa937e34cb562a048e4fbb: Generated + col_0191c0aa0d39de591b5236b304496123: number + col_2baa55cecfe99708f15031b74cdd7d36: Generated + col_9e2f281c14fe1c305bf0613edb1b5467: Generated + col_cb9ad148d424c04f44d3959334b394d9: Generated + col_9bba3bbc7094c759a61d84f8605ba3b7: Generated + col_5c63e943a6e825630ccaefcbed7a719b: number + col_41e744edec4d69e40ddd093f4e1b84aa: Generated + col_83750383555947e4ccbd9661a0948ffe: number + col_02f2a2f220f28476f9c5ae7f0edfc060: Generated + col_6052d6d42f76bc5ee164f458689e7274: Generated + col_1044fcff754bb0008ba077f4fdc005f4: Generated + col_bcbc405c655f95f2343eb1fb3288a05b: Generated + col_aeffdc77f4dde5fd265842bc848e92d1: number + col_870d427af1d948c352eea11ffd952d1d: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_ef1e84bf5d70755290854b1d0d60c91f: string + col_8ccb00b1ec3203ead93c89b315af7cfa: string + col_f8b0d195967998d8afabe1b39912ae34: string + col_0db72b58ea6b0b1a2468f44c3a154621: Generated + col_75afa9f93f59b9a0da5fc050cf61454d: Generated + col_3b24e1adfd3713e88eb334dd342f072b: Generated + col_8c36708787079d53e4bb54ddc6d4cc93: Generated + col_910c87c70d93d69a64538ffc358a99de: number + col_99b14a294256f4d99bff89feb7483caf: Generated + col_6dbc88c5b052629e050a099e04d7be3f: Generated + col_f1f2f079861a9877106a6209fe34974d: Generated + col_b96e29426fd3636dd5bc9ce5b03a13df: Generated + col_416bce439c6a2ac5ac9dbd1b0947e48a: Generated + col_079842fe7e15f5de8a65e411c9df3787: Generated + col_0aff87eb8d3d48fa89e873060f0715d4: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number + col_2923af0fb0193af631f34d210c0247df: Generated +} + +export interface Tableaa828347830464c8d6fb6070585f8786 { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_bbe02f946d5455d74616fc9777557c22: string + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date +} + +export interface Table5e1a250360f2e59810faaec5dec74bfb { + col_4f1b7b2483b9bb0a8b355ef69f09d65e: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_7db3960deab1cd20d8b5b1f5501f3987: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablecb67af6a70220cf4b2c4e1121d9351c6 { + col_c2794c3a6a63d8743b9aa8499f407b15: string + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table01687ac6f71bbab86d7fb8482bf71301 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_80ae6f72f9c170f9e1bcae48c1677802: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated + col_16f39dcb91dd929ebf8bea9f30568523: number + col_0e759e13c162a5f46e9c6ad11ff37c68: number +} + +export interface Tablee4612831426584576f2d39997ccac9c8 { + col_6a59e77ab0765a9b1e48a245087e5bbd: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: Generated + id: Generated + col_84267f80102ea16f25ffe6f5be92255e: Generated + col_c55b5208e65984aac86ada39165d9d62: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Tablebca1b3f3c2526216b2f01c01fa85f3f9 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_47800dcca216b1b165c13199d66c3b63: Generated + id: Generated + col_c55b5208e65984aac86ada39165d9d62: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Tablecac2eaeb16e94e019e7e98240c4bac17 { + col_3886a084aa5ac96babd5443af176b2fa: Generated + col_e91148e8d587cad1668a1e01d4701bd4: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_756c412732c9e787f483d35d939b8ef2: Generated +} + +export interface Table7eb5460a0c6690c1dff53fe4940240db { + col_2f76db193eac6ad0f152563313673ac9: Date + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table5c88bed5704d2054a84d44150bf10f21 { + col_f60a220f6974819d5fb5760aed1c4cd0: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_756c412732c9e787f483d35d939b8ef2: string +} + +export interface Table148eb9256ed9e01df75e345bd7e5c9f7 { + col_d109701b76ff710c8e7fddd648466bac: string + col_066a120fe3abe924fdd105d2073b3a64: Generated + col_08114ce1193bf6826c3277f27236a6d8: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_4f84013a8b5e4c2b7529058c8fafcaa8: string + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_3917508388f24a50271f7088b657123c: Generated +} + +export interface Tablee2c77a8b952bdea3bc2cb7261168ec45 { + col_c06fbc27fba97ced26a9d976caa41a75: number + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_a94fa2fac7c75e7343d9de23fa2ee836: number +} + +export interface Tableabd2380e09dc7a608bb7e5c33654540e { + col_6a59e77ab0765a9b1e48a245087e5bbd: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_2cf1ecc054fac526249f379db15f6114: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_f136803ab9c241079ba0cc1b5d02ee77: string +} + +export interface Table7117bf3bff290101f4fa6d6de627b58f { + col_6a59e77ab0765a9b1e48a245087e5bbd: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_8b5da04bf1aacd462ab37eec2f0dea98: Generated + col_f136803ab9c241079ba0cc1b5d02ee77: string + col_20be803fa76256ebff5a3090a351dce6: string +} + +export interface Table202a1f7684d2c58afd3885112de82158 { + col_6a59e77ab0765a9b1e48a245087e5bbd: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_f136803ab9c241079ba0cc1b5d02ee77: string +} + +export interface Table93aeee94c0d8d4e08a56c97185d68ae7 { + col_6a59e77ab0765a9b1e48a245087e5bbd: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_f136803ab9c241079ba0cc1b5d02ee77: string +} + +export interface Table70c085cf2d0edd14f7dbae474f064b1c { + col_495188e3a36574928230c0e978a9c24b: string + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated +} + +export interface Table2b11443e8af088b453d5119d135803ac { + col_c380dabd328dc1bc21400bb6319fe82e: Generated + col_46df0ccd51bb26538db287f8e4e6e4dd: Generated + col_f32428508672211d04f041977bc90510: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_6f2528e6259a627108655e46967927b1: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: Generated + col_40cee1e4d1c6b882095d5d343de8a0e2: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aeffdc77f4dde5fd265842bc848e92d1: Generated + col_de936835b986b7e9a72b8706204dc183: Generated + col_105b287b6f42e3e08bccae5fa0bc1e57: Generated + col_4bf016b629fc04e08a3080d945fff2e3: Generated + col_10c0a78ec67f5765bb129e53d403a805: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table0a542ca892cb0eea27538d7360f718ec { + col_6a59e77ab0765a9b1e48a245087e5bbd: Generated + col_76b9a01c6151dd633f8055cde973a800: Generated + col_1d9d9279776c5549212200cb14336162: string + col_e78e90b28ac15ec4ffca85e98e8c9ba3: Generated + col_13715b2fa41756ef623ff3afe3ad7061: Generated + col_3886a084aa5ac96babd5443af176b2fa: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_ed9b82a66d24b12f15e6c804b8e2987a: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_1f6eddf77651742ff1d83404930b09ae: string + col_18f2a0599c2957ceab7b1d9810c178af: Generated + col_c5afa3714e1eb36f2ef051adb19d53a1: string + col_50169df6c46157b06e457a2215bafaa3: string + col_22b038c1818e66252d7965200369f7b8: string +} + +export interface Tableb8378493b92a26626ae5745222c8ba69 { + col_535325947cf44473a2c18fad07fe9457: number + col_2f76db193eac6ad0f152563313673ac9: Generated + col_62bd695be89e0f07c72268b40df9c2db: number + col_34e7aea5cf0f74ee76b3f5445a3ac1cf: Date + col_c84d2025f9e13529cf7b4aa05275bdf6: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_2700d8a25d8d8e80c4ad5b622ea5dbec: number + col_3d1ca4769fb6a0b43de8fd6c85704dde: number +} + +export interface Table565a308ecf3782bb5eb0197187361913 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_e78e7faf2c91332782285f057698f37f: string +} + +export interface Tablede86128413dcd68bd6d1ca009b6bae17 { + col_821c248ea5f31b36e1fef43cbcf38495: Generated + col_49a7abbdc564bf92a27c31a936047cda: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated +} + +export interface Table57dadc25e9a34c53f66b5cb499d75904 { + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_d141deef0d42e47261b99f7b286068d8: Generated + col_f35d7efe3efb3e67af82d59c734bf437: string +} + +export interface Table366c7f7bccaea5ff455a9b9265448ebf { + col_535325947cf44473a2c18fad07fe9457: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_2598a0c1aa229293b50e76e77b92ab0e: Generated + col_8f4a31364a65a9f539032a16e3f4ea8b: Generated + col_7a9cd04d755a1c1f269ce7184b943521: Generated + col_6ea44f04ea696f044e04871607c145ab: Generated + col_7426b7174c1b7d22061329a60b94e63f: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: Generated + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_e66275f52358e1042acff2dc629ecf3d: number + col_888ef26444026e2cf7e456521447a5cf: number + col_0169d667d30453fc6d348ede68c6137e: number + col_0191c0aa0d39de591b5236b304496123: number + col_ea32f0629d5780f0b84ac3c2dc29354c: number + col_83750383555947e4ccbd9661a0948ffe: number + col_ef1e84bf5d70755290854b1d0d60c91f: Generated + col_53a208b112254787b9ebe57338d2e92a: Generated + col_0511961bf51aa24c76ca49e18e4395e9: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated +} + +export interface Tablea4ca5018c98bd7912524f97ccd0a8757 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_e7e1b5558e643c883f256414a76e28f7: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_aeffdc77f4dde5fd265842bc848e92d1: number +} + +export interface Table402dd89f94d2a88ccdf049224dfc8700 { + col_535325947cf44473a2c18fad07fe9457: Generated + col_066a120fe3abe924fdd105d2073b3a64: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_2598a0c1aa229293b50e76e77b92ab0e: Generated + col_8f4a31364a65a9f539032a16e3f4ea8b: Generated + col_7a9cd04d755a1c1f269ce7184b943521: Generated + col_6ea44f04ea696f044e04871607c145ab: Generated + col_7426b7174c1b7d22061329a60b94e63f: Generated + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_e66275f52358e1042acff2dc629ecf3d: number + col_888ef26444026e2cf7e456521447a5cf: number + col_0169d667d30453fc6d348ede68c6137e: number + col_0191c0aa0d39de591b5236b304496123: number + col_ea32f0629d5780f0b84ac3c2dc29354c: number + col_83750383555947e4ccbd9661a0948ffe: number + col_ef1e84bf5d70755290854b1d0d60c91f: Generated + col_53a208b112254787b9ebe57338d2e92a: Generated + col_0511961bf51aa24c76ca49e18e4395e9: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated +} + +export interface Table92498a6babb7032cdcad6e43158a3359 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_e7e1b5558e643c883f256414a76e28f7: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_aeffdc77f4dde5fd265842bc848e92d1: number +} + +export interface Tablee90356817b37536144c4cf962e9e5165 { + col_b44e675732d485b4ded87fd63200ab77: number + col_21e3dbb834dd41bd22e04928fef6a335: number + id: Generated + col_756c412732c9e787f483d35d939b8ef2: string +} + +export interface Table61a79fc440f7ca2d4190d47ee809f66c { + col_2f76db193eac6ad0f152563313673ac9: Date + col_7364a488f431ca7296b76a34417395b4: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_2598a0c1aa229293b50e76e77b92ab0e: Generated + col_8f4a31364a65a9f539032a16e3f4ea8b: Generated + col_1690e4ca8c11ef9d61ca7213686d5c40: Generated + col_6ea44f04ea696f044e04871607c145ab: Generated + col_7426b7174c1b7d22061329a60b94e63f: Generated + col_d3dfe95339547ab875d57cd71ab15f81: Generated + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_e66275f52358e1042acff2dc629ecf3d: number + col_888ef26444026e2cf7e456521447a5cf: number + col_0169d667d30453fc6d348ede68c6137e: number + col_835294398c028add3afb4e1cb897a4de: number + col_0191c0aa0d39de591b5236b304496123: number + col_ea32f0629d5780f0b84ac3c2dc29354c: number + col_83750383555947e4ccbd9661a0948ffe: number + col_ef1e84bf5d70755290854b1d0d60c91f: Generated + col_8b9c30bb3038e2306d0186e3b78f5f3a: Generated + col_53a208b112254787b9ebe57338d2e92a: Generated + col_0511961bf51aa24c76ca49e18e4395e9: Generated + col_0d15b3ba535af3fa98a18834ca71c71f: Generated + col_079842fe7e15f5de8a65e411c9df3787: Generated + col_0aff87eb8d3d48fa89e873060f0715d4: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated +} + +export interface Table7051320fedfb456818eb15cdf217186d { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_e7e1b5558e643c883f256414a76e28f7: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_aeffdc77f4dde5fd265842bc848e92d1: number +} + +export interface Table2d7cdf1ccac4544f81ac78a12d5fba7a { + col_caaf808b80e56775d03b800fc5edf429: number + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_369ce420bd39cdc51bfcfd33d861ba59: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_b1dc1e3377a082304d817664243e7b17: Decimal +} + +export interface Tablea8e3bfebcd001eac1185867f7dec94a5 { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_910c87c70d93d69a64538ffc358a99de: Generated +} + +export interface Table148ce21c70970d2809c4028c229445be { + col_f4dffeda79e0ee34f16d57241b615f53: Generated + col_b738ecc416fa96f41459da0404cdbf50: Generated + col_44381c5d8d2d9361dd21bb7a7facf4b0: Generated + col_0259f6499a0cb3f370a6119054eea3f3: Generated + col_08114ce1193bf6826c3277f27236a6d8: Generated + col_fe3144cfd0702470a18811a50b2a773a: Generated + col_8ce24ccf64416ee4c1737bf1ab0ea2f8: Generated + col_4f1b7b2483b9bb0a8b355ef69f09d65e: string + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_4a2d849a39589cdab1bef664bbaf662f: Generated + col_5306bd08c84506786847c551137592aa: Generated + col_f901883e4b25324963cbb061fed4eaec: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_bb8f173dd39e2bbd2ca8bb502bdd5d7e: number + col_645e3472df388d4789ad6a12715cc7a9: Generated + col_eade66be3d5c19459dbb52d65e58a9ff: Generated + col_78f5ba4e82866b7d30d1c63b058b5042: Generated + col_ed68df9dcbc6be1e74aa52f2aaac27b3: Generated + col_a222f017034f971d4a5004b53f0e9699: Generated + col_9b0c62311c53c898ce6699696d685a14: Generated + col_a19773e01d56a8727f9b50bfd98b67a7: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_0c7beee8ee787fec23562efa3adbf18a: number + col_2a5b75864f31fbe839a8f028ee554f53: Generated + col_78729121eb966294e69d86f8b656c99d: Generated + col_cac71ab1db07fe38f5915b92b9959348: Generated + col_6310539ac8c0b13a896e8b9634e1e0c8: Generated + col_1628a3796456964796ddec0bcb38d0e5: Generated + col_8f4a31364a65a9f539032a16e3f4ea8b: Date + col_d9d6c97f041fe7c54b65501f67fcfea9: Generated + col_bbeb11288ba33af45a8a7251cb87da2c: Generated + col_e199cc093d54f6c562ad8d547976c5df: Generated + col_e26de993b80283e73a98d2f5a7655e44: Generated + col_7a9cd04d755a1c1f269ce7184b943521: Generated + col_7426b7174c1b7d22061329a60b94e63f: number + col_1cc17f0951cd6972739a3cf858f985e2: Generated + col_0364c29fe7237f4f6ffba597544b4f46: Generated + id: Generated + col_3f38469c48f069fcc4f623bca8c587f6: Generated + col_d6ffe41368d46575efb6f237e05643f8: Generated + col_478cb1787a1e2a0d75235f14a78b7929: Generated + col_387852799fd39a75bb0ffbb3a088106e: Generated + col_ea14b84c81e7ebecb1faf4ce83b8826b: number + col_4002e95484dd522e663c6fac8757d914: Generated + col_01b47914fe373f6ade80d5d3afef6847: Generated + col_a3c438c6f272e35cd2f6e1a4f59dec67: Generated + col_ef8a0d1ea2ae246640e55a5f585324f4: Generated + col_256cd843e152753550cd6aa845f56fd8: Generated + col_6360ba5916d4e8415f464336177c02e7: Generated + col_5410929b23ebb3020d600feea427cdb7: Generated + col_2924351c3f723a33dfd4dc659a06be9c: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_83ca342b1dd50c4c7999010b9ad6683a: Generated + col_43c33f96a8b44d501f45cd6b9382f9df: Generated + col_7b4b1dd4aafa937e34cb562a048e4fbb: number + col_0191c0aa0d39de591b5236b304496123: number + col_3ad5d52faa1934f2661f8a9d39c48bcf: Generated + col_2baa55cecfe99708f15031b74cdd7d36: Generated + col_9e2f281c14fe1c305bf0613edb1b5467: Generated + col_cb9ad148d424c04f44d3959334b394d9: Generated + col_5c63e943a6e825630ccaefcbed7a719b: number + col_641b931cd172a7b80d81f8e994faed0f: Generated + col_83750383555947e4ccbd9661a0948ffe: number + col_32fae7a6a5f2cd2d02cbdfac22bb3aee: Generated + col_02f2a2f220f28476f9c5ae7f0edfc060: Generated + col_6052d6d42f76bc5ee164f458689e7274: Generated + col_1044fcff754bb0008ba077f4fdc005f4: Generated + col_bcbc405c655f95f2343eb1fb3288a05b: Generated + col_9e3a10e7f5449c1de35f219e8d4a0b43: Generated + col_aeffdc77f4dde5fd265842bc848e92d1: number + col_870d427af1d948c352eea11ffd952d1d: number + col_ef1e84bf5d70755290854b1d0d60c91f: string + col_8ccb00b1ec3203ead93c89b315af7cfa: string + col_8b5da04bf1aacd462ab37eec2f0dea98: string + col_a0714fe92c4ddead9939c0f656a43ba8: Generated + col_7d39847d54a6fc52735c6a250cfce5eb: Generated + col_0db72b58ea6b0b1a2468f44c3a154621: Generated + col_75afa9f93f59b9a0da5fc050cf61454d: Generated + col_3b24e1adfd3713e88eb334dd342f072b: Generated + col_8c36708787079d53e4bb54ddc6d4cc93: Generated + col_910c87c70d93d69a64538ffc358a99de: number + col_53a208b112254787b9ebe57338d2e92a: Generated + col_4ab5b1dd04a8cefd62107890b80103cd: Generated + col_6dbc88c5b052629e050a099e04d7be3f: Generated + col_f1f2f079861a9877106a6209fe34974d: Generated + col_972aaa3fbd7b8c3cb772ed1eb78becbb: Generated + col_fb70e10e97efd8bc28a6c9cb1e9f9ff8: Generated + col_b96e29426fd3636dd5bc9ce5b03a13df: Generated + col_5f544d6483d4ce30cf3145abb40f9441: Generated + col_bab5d43498aec1209e8ab958b766cb86: Generated + col_416bce439c6a2ac5ac9dbd1b0947e48a: Generated + col_079842fe7e15f5de8a65e411c9df3787: Generated + col_0aff87eb8d3d48fa89e873060f0715d4: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number + col_1e36674b6a9b366e7ac7dc0343291399: 'auto' | 'validado' + col_e7f37cd2de5c3de049e6ef13e7027769: 'auto' | 'validado' + col_2923af0fb0193af631f34d210c0247df: Generated + col_df82ddd36c8d6387edc8c1d15e9e6e8d: Generated + col_f990fa06e3da84de9a61af49e4b7957f: Generated +} + +export interface Tablef650c769203b8bd7eed5a8b7e249c0a5 { + col_a538a4b49163b8abcdcff07958d6290c: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_7db3960deab1cd20d8b5b1f5501f3987: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_1ba1f915f54c8d29d5766cc9b0efe0f2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table8b9070a970ee019dd472ce6c4a08418b { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_3924f75e4f7e65a64d2f06bad0a8da28: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tabled580c05f239408ff0392e235078a0ac9 { + col_a4513edcdb71f8647001257ec32ab4e1: string + col_2404b63b8a201d13a8735e0fa9b53478: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_1df5ee28c9955544f2ebe06a6c829b57: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_18697449d7c48cf32cdd4f14857e68ee: string + col_8368a8c92f592e349b57fc53fe6f98f4: Generated + col_f57dbda547fc6e900a0f8a585ddd9b49: string + col_910c87c70d93d69a64538ffc358a99de: Generated + col_a94fa2fac7c75e7343d9de23fa2ee836: number +} + +export interface Tabledd7d780df70068dcb36a8d8b86bd5298 { + col_a4c065a7262f640f0ac2b61885f9671c: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_b4226c098cbf79023e9a779b83ccd3c9: string + col_8a6902c3de19dedef52742ed22cff3ca: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_6177dfccf6065b1ec558e457a24dea33: string + col_aeffdc77f4dde5fd265842bc848e92d1: number + col_b1bac0f8e81aa2bc396eb0eacaf93f7d: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table48df920caa8cc60fd47820e9d94ea940 { + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_216979ad27e40ff49f5d477cb3ebad3c: string + col_1430120f960285493d5121f4e9f1d924: string +} + +export interface Table9bb0301f7ca02953fac2add8dcd7630c { + col_1d726898491fbca9a8dac855d2be1be8: Generated + col_d109701b76ff710c8e7fddd648466bac: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_4f84013a8b5e4c2b7529058c8fafcaa8: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_8b5da04bf1aacd462ab37eec2f0dea98: Generated +} + +export interface Table5492e76278a71210d7231f4bf905ec2f { + col_6a59e77ab0765a9b1e48a245087e5bbd: Generated + col_fa769b4ad2c48f942621b456579a2224: Generated + col_0e26753c5fe14eae84568b5a83e79ded: Generated + col_08b4d026d42ae768f63552c6de119d71: Generated + col_60f7d4dabf428b3a6d7d624e3e8f92ef: Generated + col_05f720d878e84cf5edf8810ac56f2f47: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_e3c09cb3d68235ebdf5820c49f6ef2e0: Generated + col_e8597efec888f22158cf26c4a354b45e: Generated + col_c68408fb5945c7ed27cfe1fb9ac4c2ea: Generated<'afecta' | 'exenta'> + col_151fd0d998f5df50fcc7d39143f704be: + | 'acepta' + | 'agilice' + | 'bsale' + | 'iconstruye' + | 'mipyme' + | 'nubox' + | 'otro' + | 'simpledte' + col_99a8177c44470ec20b0097b78d30d2ab: number + col_99f35f96dabaa98303aaa135bfdc0404: Date + col_a4ce5705c5f18b69e4add952a187bdde: Date + col_c613b7206854402371564704dbd689db: Generated + col_355ea60efdcf57aa297299ab210b4e7d: Generated + col_b2272775d9787c60c9479000c8b8088a: Generated + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_a766059eaf3814aa9e73448453e9270c: Generated + col_aba4dc94d2f01540b5dbb57d1096c59b: Date + col_7dab8a0fad43f3c1c0c352252aac807f: Generated + col_98d8d6a239ae64c6f2fc402b7a7847d8: Generated + col_9f7b242c1ab922eb24d0a3ae403e9676: Generated + col_d5a21723ffc4d83c5ab428a5878324b9: string + col_15deb4ee2f0424b3885142c477ba88b9: string + col_042f170682938c181254695986457b1b: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_f639c93a74ec468c5609505b10e90eea: Generated + col_ba39ea3526b3c7a1ab364b9298d40d78: Generated + col_ad014db14bea88133e71209061b6bd74: Generated + col_90e4c91b2d55ab42c9930ec06e506ee4: Generated + col_d420d89b7ce762fa312a4437bccb99c6: number + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_e245eb517472187d10441eca2c4a9aeb: Generated + col_651324890c5c5b8c2193ad39bfa36c05: string + col_164b7896ec8e770207febe0812c5f052: Generated + col_40772ba813d97356c66a42e44c29f2f2: Generated + col_4e893eedef52baaf8f70770064c00f91: Generated + col_1d958835f7ab52daf74459d71c0c66d1: Generated + col_ef1e84bf5d70755290854b1d0d60c91f: string + col_91d2a3cd2d79ad40574b4c2ec58584c1: Generated + col_e85a2e013135b7b178241e6fc32327a8: Generated + col_8b5da04bf1aacd462ab37eec2f0dea98: string + col_cb72fdda441d8ab0ee9b88e25961fd83: Generated + col_f9dd44edd9eab46a126cfec00a028907: Generated + col_7c2b7d5981088f354990946bb421e222: string + col_70b0729c3bbf27533b98759ae98fb40e: Generated +} + +export interface Tableea8e45026529937b0cecd544d8801cfd { + col_bdaef2864913fee6a3be8029d46830a6: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_2414ddf934cafab5eb8e6fbe3478710e: string +} + +export interface Table574ff9acd571619424cd55716f5f1dfb { + col_95a573b127ca2c23a2ed1dbc7d03d29e: string + id: Generated +} + +export interface Tablea79aeca54482635868f02356a51fa369 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tablee7a44a84d410fa64377504e3aa7f72b6 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_99a8177c44470ec20b0097b78d30d2ab: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tabledcc9250fc52d5fbc25a56cf9c4089073 { + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_95a573b127ca2c23a2ed1dbc7d03d29e: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string +} + +export interface Table4d82bf6c52ed1355768c73593994c5c2 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated +} + +export interface Table73f359bc2658d254af39135aed8c3bf2 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_4377992db876783f2aced4af1b7cc195: number +} + +export interface Table8cf85c63dc3200ef8145fc0344d00f16 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated +} + +export interface Table401ef7ed8ddaa97932ede0de0b157c04 { + col_95a573b127ca2c23a2ed1dbc7d03d29e: string + id: Generated +} + +export interface Table3e875ede7feab48a89082e0c87c364f0 { + col_d08c3aaad1d4c926a4bf283098fcc325: string + col_2f76db193eac6ad0f152563313673ac9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_d141deef0d42e47261b99f7b286068d8: Generated +} + +export interface Table7822bcc86140bf34bc6cdacbd734c0ef { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_a15701e29e0d577f5252d616c1f13273: number + col_e6c19fe14ec531d7b295d1723a4b8e03: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_72f96160fd66ba04a6c4d17116faca2a: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_6eabf912118c656e74717bf86ec69f73: number + col_b2211a70f2ff7369336027ed47f18f7f: Generated + col_923c1ce4be0acf2cf56890bbd41ec804: Generated +} + +export interface Table459dc31349051f20a755552adaf60c81 { + col_460c9eed028ead37612e14da1dd10330: number + col_2f76db193eac6ad0f152563313673ac9: Generated + col_6c5493e52a59d7de52511ba52276ddcd: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_112f837ef3cfed0e17742e284a1769e7: Generated +} + +export interface Table1b8610f190607dfbdafa9c71f72acd43 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_fb697b0790ebaa204d9bffad490998af: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date +} + +export interface Table1edc1b921ba66176c3ec62961c248f78 { + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table9b72afad4afd63d81ab6ef56c926765d { + col_fc00b65a31d011aa9c5cac6511de984e: Generated + col_4f1b7b2483b9bb0a8b355ef69f09d65e: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_d2508118d0d39e198d1129d87d692d59: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number + col_756c412732c9e787f483d35d939b8ef2: Generated +} + +export interface Table309e0b9a80a4ad911a672f7174429e35 { + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated +} + +export interface Tablea0eb46b195e5cbd5e85384a9a1622c14 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_eeec804da0fd1aeeb188a033dc67b8a2: Generated + col_a2485bfd6e0a49a04be5fbb9ed0123e9: Generated + col_76ef16690bb4ed0aba9768175f242b63: Generated + col_1e23790fd5accaa0afa87349f9791709: Generated + col_f0d54e8da1328799154329bc93eb4b55: string + col_f1f2f079861a9877106a6209fe34974d: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tableea5b8f9c56ff4ae4cb3c8f22541b33ba { + col_6a59e77ab0765a9b1e48a245087e5bbd: number + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_1f7ca930a1d20b8732208df17a3f204a: Generated + col_74acc07e501e952b631264318b0a5ba9: Generated + id: Generated + col_c55b5208e65984aac86ada39165d9d62: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: Generated + col_df15c6be5f4fc31ef5a0a4fe0ae82481: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aeffdc77f4dde5fd265842bc848e92d1: Generated + col_aff776838092862d398b58e380901753: Generated +} + +export interface Table3a87f98012aa2362e8eb64ed4d9e8f65 { + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_0ba81706c32fe49f7691637abff566ac: Generated +} + +export interface Tablefe2e32091534caf00911fc7435cbc97b { + col_6a59e77ab0765a9b1e48a245087e5bbd: number + col_512820308ce62e853b7c389614ef5adb: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_57785655fe648f6c416a0d3024567b5e: number + id: Generated + col_805df4d52c1afca7f1fe90a63e932648: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_3326c052986bfda9670541e3279e2a4d: Generated +} + +export interface Table3cdcee0d77b622e8a2ec654a21270877 { + col_57785655fe648f6c416a0d3024567b5e: number + id: Generated + col_169ab89ce8908eabce9ec3b6c5350b3a: Generated + col_e82af1a7ad08756ecd516aef27dd0510: number + col_e245eb517472187d10441eca2c4a9aeb: number + col_e7fa32cb05ba9ddc8d5f75bdf1694790: Generated + col_ed735d6320ed39d39ecf94e63bcbbe78: Generated +} + +export interface Table8077f242ad5e05394a3dd81ce79565f7 { + col_90412006b8822d49b8306f1cebb4b244: Generated + col_57785655fe648f6c416a0d3024567b5e: number + id: Generated + col_3326c052986bfda9670541e3279e2a4d: Generated + col_40772ba813d97356c66a42e44c29f2f2: number +} + +export interface Table52a8644bfd17799495525f2fd8adc480 { + col_25cd93f430d277509b60d4390636895f: Generated + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_c010021498d7345dadc4ec5be041f8d2: number + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_c8cdc4633fd519ba66ab3e84fdb9e5b3: Generated + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_eeec804da0fd1aeeb188a033dc67b8a2: string + col_a2485bfd6e0a49a04be5fbb9ed0123e9: Generated + col_53a208b112254787b9ebe57338d2e92a: Generated + col_0ba2161c945373e22c9989c68a51937c: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated +} + +export interface Table74bb684c41a71a06f561e10ffa4e3323 { + col_fc64eb6054f462d6d323dd7c2e2ff768: Decimal + col_2f76db193eac6ad0f152563313673ac9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Generated + col_f609bfdeed2c51dbb142fd03bddfd02a: Generated + col_5c63e943a6e825630ccaefcbed7a719b: Generated + col_83750383555947e4ccbd9661a0948ffe: Generated + col_b2211a70f2ff7369336027ed47f18f7f: Generated + col_6092393019ca918cd006522bd308d5e8: number +} + +export interface Table64ac7b9a171e9af222924b693d44887c { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablefd230cdd0c0d7bc04b06cffa3bc3fa3e { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_9f4a15cb3f14b10397b11f224b181199: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aff776838092862d398b58e380901753: Generated +} + +export interface Table2bf8003c7e2a7344f4d30c3a351926c5 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table5c1f23359d009924d75dac9200da99ab { + col_fee6d937834161cc73b3698fee871f5c: string + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_9b0c62311c53c898ce6699696d685a14: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_95a573b127ca2c23a2ed1dbc7d03d29e: string + col_8f4a31364a65a9f539032a16e3f4ea8b: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_25980343ed3b4c231a154e5e88396938: number + col_fb1ecce130681efa10fd55197773267d: number + col_d8ad55006c0efc8437cc487b5c5459f6: number + col_01abc2f9e295923216e8462e3a88bf5d: string + col_ea7e482493b72c75cf90057ba252352b: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_f8b0d195967998d8afabe1b39912ae34: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablea6cdaf7c2c1c27d8389fb8ec7831b240 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_c69fd88c6f0d5f2db866eaf69baa2554: string + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: Generated + col_00b4288b12a81b04c5f2479addcec521: Generated +} + +export interface Table618b14672aa41a4ddbaa07b4668e9ceb { + col_066a120fe3abe924fdd105d2073b3a64: number + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_7364a488f431ca7296b76a34417395b4: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_e842d3a41f6119b9ea97d9966c87fe4a: number + id: Generated + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_b2211a70f2ff7369336027ed47f18f7f: number +} + +export interface Tablee0ac1a12db533e45722bf75854c5f68f { + col_25cd93f430d277509b60d4390636895f: string + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_9b0c62311c53c898ce6699696d685a14: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_910c87c70d93d69a64538ffc358a99de: Generated< + 'F20' | 'F22' | 'F29' | 'F30' | 'F50' + > + col_91b6293c3ca6646fe348ce8a4f4b4c85: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table6b64130a9ac5e9179051a15684e76956 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_0f8a9471827ba9a9ddd4ce0e2ddd1a00: number + id: Generated + col_22bd050ad65b2aa12639b75318b0864b: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number +} + +export interface Tablec3c4b9ba46ff23ee52e9fa59ba4cbd42 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_38a15d7f839694e0cbb6b3feacd63ea8: number + id: Generated + col_22bd050ad65b2aa12639b75318b0864b: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number +} + +export interface Tableac8f964f7407177bed83d5a1b563c4be { + id: Generated + col_e245eb517472187d10441eca2c4a9aeb: number + col_91b6293c3ca6646fe348ce8a4f4b4c85: number + col_b1dc1e3377a082304d817664243e7b17: number +} + +export interface Table0e3703752e6fb26a7c95ffbf9ba62354 { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table051ebef2507af0ef791ed665f9df9f69 { + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_20be803fa76256ebff5a3090a351dce6: Generated +} + +export interface Tablea85ed4b894e81fcae5b5f32832c0a4aa { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_2cf1ecc054fac526249f379db15f6114: number + col_d2508118d0d39e198d1129d87d692d59: Date +} + +export interface Tablef2e554fc6e599ca882cad3f9c8944c5f { + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_2cf1ecc054fac526249f379db15f6114: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_a086a44c8be387291cb7ec589a8d144c: number +} + +export interface Table92318b69e7164d55b1815a563a4f0fda { + col_f32428508672211d04f041977bc90510: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_89bd8e97120bfa5bcc0fdbcf4f96365b: Generated + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_ea3e5046a32b090e4e792bb30890a129: number + col_98d8d6a239ae64c6f2fc402b7a7847d8: Generated + col_c94ff636d78c16fd4972b7df1ed9f7f2: Generated + col_fe999c04c29b51909b8ec56ecfbabcb8: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_e245eb517472187d10441eca2c4a9aeb: number + col_20be803fa76256ebff5a3090a351dce6: Generated +} + +export interface Table11c3ad790c98516ecb794068e8fe4332 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_ed9174294dbcdc54c009c8087f24fd4e: number + id: Generated + col_c55b5208e65984aac86ada39165d9d62: number + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Table0b9fd044caf91b1c9c0decf463f48479 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_6c5493e52a59d7de52511ba52276ddcd: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_47800dcca216b1b165c13199d66c3b63: number + col_3d40d536360063946092d61d8fb4af97: Generated + id: Generated + col_c55b5208e65984aac86ada39165d9d62: number + col_fe999c04c29b51909b8ec56ecfbabcb8: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Tablecb5f0e2e2a6bb82fe0ec713f3001adae { + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table05f7a75fd9ce14b6ff3abafe6a0315cd { + col_f606ffbc02200e165ccbe5bd6e0ce11a: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_6137cde4893c59f76f005a8123d8e8e6: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_61ee96e36f87193202f69079c2a248fc: string + col_c010021498d7345dadc4ec5be041f8d2: Generated + col_9d73dfc1b706d61e28cf50df23ad34be: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_910c87c70d93d69a64538ffc358a99de: Generated + col_f136803ab9c241079ba0cc1b5d02ee77: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablef9d0cc88ca4af2fc064f9fe28c1cae51 { + col_258def64c72dae45f3e4c8516e2111f2: string + col_c50368ae5e839e9224eb0947f06fc605: number + col_26fc2a6aec4599af1b0a36cbe6e3939a: Generated + col_aed19f4c9c98622d6661990a56b5ad95: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_ca55f5b1d19aae0a019b8afb8d995ca9: string + col_f9c55d34dd1668d1fc4a576fc2f8c64b: string + id: Generated + col_d7cc8cbac63ee8761aabf7792b5160db: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_665cebec3ce1380ce4e3c90b1bedcbf5: Generated + col_76ef16690bb4ed0aba9768175f242b63: string +} + +export interface Tabledf515f554daadb410af199b9d2af4968 { + col_26fc2a6aec4599af1b0a36cbe6e3939a: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_665cebec3ce1380ce4e3c90b1bedcbf5: Generated +} + +export interface Tablee7013b1aa66ff80026b9594533e73417 { + col_0e9af2c1d8b2bdc39279709d960ee616: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_de2b14ae7499f90736fc4a92327553a5: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_55d0da66c8f6617ffc31f4d7e9b9cd6a: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table6fc94749b5f65b04e1364bc84a3b675a { + col_1019e8dbd935e7e1fbaee27cb45c81c5: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_1590c1c1103e53135f348374eda74d92: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_2cf1ecc054fac526249f379db15f6114: Generated + col_2c0609d963e4d588c6b53d93845d6661: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_74af50ad445ae874663c514314c04397: number + col_a1625f77580ae88538a0bba96298b5ba: number + col_36e816a9244e63bedbd7bac69c3208ca: number + col_3a4c1cab8214c2a1f85432d018ac0548: number +} + +export interface Table7e5a6e895e47f444ce7042734df8ce92 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table03fcd39a79bd9fbbc8f2f32371eb6a9b { + col_7844a93ad4b97169834dade975b5beff: string + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table3edc0e801929e10d8dd381561aa2bd31 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_e8597efec888f22158cf26c4a354b45e: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Table75f7cd025a872d2698d2a6e53a8cd953 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated +} + +export interface Table075f9410d29eaec83d4f68c3dfa82a10 { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_2fd6a67502ba2fc6e63b23903522d93b: number + col_aaff2deb17374d1ac30c040f6f0552b5: Generated + col_2931dbf568c51f5a95e2e3081491a702: string + col_3bf01fe7a9112bfabe3af0e2afea3e59: string + col_f7b98604d9635076cd6f4dd654792206: string +} + +export interface Tableb91d0a254d43344084dc0efd98b90946 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_95a573b127ca2c23a2ed1dbc7d03d29e: 'activada' | 'desactivada' + id: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated +} + +export interface Table4d4605d2d071f927c338880fcda7b540 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_88e292b9447efefa8460bb2b17d043c0: Generated + col_ee681610cf80275688440d62a3060f1d: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table14a5541144254091c320909a95c4c614 { + col_484a106f0719881cf489e0fbd80bfc43: Generated + col_c9774140535fe111b94e064a8c420b98: Generated + col_662faa62e8249d797277f109de5dbfc2: Date + col_141e21b657ebc0212411bf8df49a57cf: Date + col_ce3cb2352c9bc7bfe791dc286b11e4c3: Generated + col_d8e32557636967bba571034b4660818b: Generated + col_5d2dad9c812862769b4a934b13695d02: Generated + col_9b93b26c60773f04b795b6accea9014e: Generated + col_a1cad862e5cc1c937323caa84259cefb: Generated + col_e413d6c84d788644dfbcf3bf6fefaabd: Generated + col_4a375049db156d117c5ad35214dfe424: number + col_e6a4dce7716be656ec5e052771f82d7d: Generated + col_62b99d59be819ae40b4b89dc38d01b6b: Generated + col_c053226db4df10c61509baef89a07cf2: Generated + col_f9338a582beb27844ddff5a2491b29d3: Generated + col_d3e721d3f9c12db0276ec46a3921d52b: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d86bec40de826529ed04c5d5bc0f975e: 'historica' | 'provisoria' +} + +export interface Tableb16695c526d6fa5e20b329d5eabfd364 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_2257daef930a33efc1b422d4f3d41890: string + col_a7743b12384fd798fdad7bff9e75bee3: Date + id: number + col_f136803ab9c241079ba0cc1b5d02ee77: string + col_00b4288b12a81b04c5f2479addcec521: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table9eb67e79e0e2abbb0a295904fd81d805 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: Generated + id: Generated + col_84267f80102ea16f25ffe6f5be92255e: number + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablea700a17706246884b5a04cafcebeaa55 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_7426b7174c1b7d22061329a60b94e63f: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Generated + col_249c850f62ea50feb918b095fc56d763: Generated + col_910c87c70d93d69a64538ffc358a99de: number +} + +export interface Table6a853060686c878e7535e29bc3772d73 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_2a5b75864f31fbe839a8f028ee554f53: number + col_7426b7174c1b7d22061329a60b94e63f: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_6177dfccf6065b1ec558e457a24dea33: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_910c87c70d93d69a64538ffc358a99de: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated +} + +export interface Table2c1e8df56b265a10929de25e4593033b { + col_2f76db193eac6ad0f152563313673ac9: Date + col_973428953d32ee6d05ce5873c2f6c139: number + id: Generated + col_2228c8ff8da091a3ba7fe63f7bf0d58f: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table3c8b015b7697cce06653b215afc8acd5 { + col_3886a084aa5ac96babd5443af176b2fa: number + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablef058a40da08114d390fcea4b81b4f0a8 { + col_cd0e40950118e419d8215d52940c0b52: string + col_2f76db193eac6ad0f152563313673ac9: Date + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablea6a7b2d92feb037592b7ed49c34d1e54 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated +} + +export interface Table4aba31682ab92f6f2baae3f68f9dc1ad { + col_17472f76c85ecf1c942560d9cf5511ba: Generated + id: Generated + col_68df1b4eeab1a558d607590173e97fbf: number + col_bfd8f7ddc31266f25afd1b6ea43d8750: Generated + col_aeffdc77f4dde5fd265842bc848e92d1: Generated + col_e7fa32cb05ba9ddc8d5f75bdf1694790: Generated + col_9b1db7c4b5f517844f202bf7ffc3bc5a: Generated + col_b53dab55eaeda66574268ffea2dd4da0: Generated + col_0e46a6ad6b83de719c1b5b777e48c8c5: string + col_62f5fcc9ea1e44509eaecb6e081a39a1: string +} + +export interface Table75d4ed2c1cedc7a0f7e36059177a8837 { + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tablec3ba8dac5adcbeb93ed2dd458b6ad4a1 { + col_4ea20fbed0c11321d0d5e8c50873ad43: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tablee53ad4b421c58913d023a3296c13419e { + col_61949c8545b648ea08e9494e262449b0: number + id: Generated + col_d2a00107fe464a6f02f9c6d81c6921f0: string + col_71eeffd42498c613691768b775cbf251: Generated + col_f88bdb43cdad67e0a8f355a543132f62: string + col_5e1fe85ed4421393b4f7a7f36d69f6fc: string + col_b1dc1e3377a082304d817664243e7b17: number +} + +export interface Table980786dce470ebfd2621534ebcd5e6e9 { + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_f88bdb43cdad67e0a8f355a543132f62: string + col_b1dc1e3377a082304d817664243e7b17: number +} + +export interface Table1a270e99026f39aaf8e33546e1c2c39e { + col_47aba655fea8599b302f71d06d0bab66: Generated + col_fc64eb6054f462d6d323dd7c2e2ff768: number + col_066a120fe3abe924fdd105d2073b3a64: number + col_08114ce1193bf6826c3277f27236a6d8: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_bb8f173dd39e2bbd2ca8bb502bdd5d7e: number + col_edc2332d18c4df92bd6c29a42e54c63c: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated + col_6ded30065c525d1945f38fc1b77df3fb: Generated + col_f88bdb43cdad67e0a8f355a543132f62: 'CLP' | 'UF' | 'USD' + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_3924f75e4f7e65a64d2f06bad0a8da28: number + col_885fa91590a5518eb66fcca9a6f11849: Generated + col_9eed1c8dafecf36e8f11783f0f9ee8d3: number + col_b2211a70f2ff7369336027ed47f18f7f: number + col_83d043db4fdfe6882fb7f01a09d92b11: Date + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number + col_4856cff2db9bf179e997e2f57c61c649: Generated +} + +export interface Table380b3be3b993931074af40f4e0af76f3 { + col_08de056b6d3139a95d0aaf99f8e3c52e: number + col_a538a4b49163b8abcdcff07958d6290c: Generated + col_d87b10b303465ebc76c15dccc5cb2b83: Generated + col_ed49a01549ef9030b8a17635d2c0d411: number + col_6d210c02b9f9e0fd7c6bb98e4b23d240: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_d7f9c190010492e516de3eebbcf3bfdc: string + col_d8f6edda3ad9cc2d89df66f5b6df3e25: Generated + col_221bd929021f3685ea46aaa5369685fc: Generated + col_15af44699327f9a6bb2f12da27fdf0ce: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_22b7922d2402e25805b18c7fa98d093c: Date + col_633cdd5118945d8664ed78acc276604f: Date + id: Generated + col_4a1aa938bcb545cd35f50b5fb8b8851f: Generated + col_ea14b84c81e7ebecb1faf4ce83b8826b: Generated + col_5bff9cec94f5ab89567a1d0a24c1bfa9: string + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: Generated + col_46e6bd185eb3c01ad5692d9ab01733b8: number + col_e66275f52358e1042acff2dc629ecf3d: Generated + col_ac23115c19aea6a256fe04d9a8b6b6fb: Generated + col_0191c0aa0d39de591b5236b304496123: Generated + col_68dcfd901c5f8c7d467fdf74b4ac45ac: Generated + col_d8ad55006c0efc8437cc487b5c5459f6: Generated + col_207b11ca5edb8c143e5afe1712ecbb6e: number + col_8476076b3459ded88a2a4a1863b65a17: Generated + col_1b6b0183f1a79f6d53cc5f8bd5f315d7: Generated + col_7b7ed03cfd1bca988b56026735632df9: Generated + col_ff66cc4c53cea80f652f8e4aa9d4ce6b: Generated + col_6b5cc62667cadeeda2fa4f4be845e772: Generated + col_dc449cf2403d7e1681944d9f4529b394: Generated + col_b96e29426fd3636dd5bc9ce5b03a13df: string + col_195367e97caee2b9ed726514b7a38efc: string +} + +export interface Table4a03b332b99d0dab4e359b9b2e9132f3 { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tableb690efdc73ab53d741824e87ddf41a0a { + col_066a120fe3abe924fdd105d2073b3a64: number + col_4f1b7b2483b9bb0a8b355ef69f09d65e: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_8f4a31364a65a9f539032a16e3f4ea8b: Date + col_7a9cd04d755a1c1f269ce7184b943521: Date + col_7426b7174c1b7d22061329a60b94e63f: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_ea32f0629d5780f0b84ac3c2dc29354c: number + col_83750383555947e4ccbd9661a0948ffe: number + col_53a208b112254787b9ebe57338d2e92a: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table80586ee503601f161507ee3f207fa19a { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table138bb88f13d65a10a42882c2578d7839 { + col_90ac24349411e6954fc47a05040d032c: Generated + col_a538a4b49163b8abcdcff07958d6290c: Generated + col_9dd1bc5c639dd9f27a30853ccf20cf02: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_62bd695be89e0f07c72268b40df9c2db: number + col_f514791973dfe8ce52d05f653fff009e: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_973428953d32ee6d05ce5873c2f6c139: number + col_74050a371c9d77e377772310c43bb55d: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: Generated + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_48a86579578d404ad2da71aa0b2b99f4: Generated + col_f61843551e5d8934a2b3afa31e66a5d0: Generated + col_a48faef66299d05e03534b08993a3c80: Generated + col_84169a44f95f5e872347716abd594909: number + col_ada72f8f1643e5c694dbf2a5636f1a64: string +} + +export interface Tablef8ad778cddf738c853992b7f5446b4f1 { + col_25cd93f430d277509b60d4390636895f: string + col_82a9e3b076bbedd5c288e8b1364c000d: Generated + col_ebd1ae5512e18049f0c867b187ad74b7: number + col_c43595b745f255b38d7a1da6a7b6d5aa: number + col_066a120fe3abe924fdd105d2073b3a64: Generated + col_08114ce1193bf6826c3277f27236a6d8: Generated + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_c1beb04a7642970cf4e59164d0b162ab: Decimal + col_a222f017034f971d4a5004b53f0e9699: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_2a5b75864f31fbe839a8f028ee554f53: Generated + col_7e53ed5979e336b304b1d25f30e1f246: Date + col_d9d6c97f041fe7c54b65501f67fcfea9: Generated + col_1690e4ca8c11ef9d61ca7213686d5c40: Date + col_7426b7174c1b7d22061329a60b94e63f: number + col_0fcee78e15438d90e5191b85092b83bf: Generated + id: Generated + col_ea14b84c81e7ebecb1faf4ce83b8826b: number + col_7f2eeb75f4d42d42268e3dcf985ea311: number + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_5c63e943a6e825630ccaefcbed7a719b: Decimal + col_9c345463e1fec644c6eee8e6158d953f: Generated + col_d69c485b8a1572710f1a3736bd1ac46c: string + col_99454795142dc6752632d6afc35ccb31: string + col_885fa91590a5518eb66fcca9a6f11849: Generated + col_b6492f8aa398e19ecf4295c9aaf0e72c: string + col_08970091b0d2ad51d515d8a311e9acae: number + col_31f22cda5030a6e16556c8d64f4d229a: number + col_eb4b47c599bd30509bef47d1ea3a29e0: Decimal + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table9f6e2daac23f8ae268d4557cb47cb202 { + col_5a1d3fcf6b0964812cb1d9ac632408d6: Generated + col_d9a3bd4bbae376e6fe20396874ab1f84: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_79bb38e8889d83eccbfe56dcb56f7c05: Generated + col_1690e4ca8c11ef9d61ca7213686d5c40: Generated + id: Generated + col_817f7773b53a20deae52a267974f26be: Generated + col_678f2cb7abb9b9f59779889d38a6522e: Generated + col_d0fa5f84c5a77f9a1cef6148c1a10afe: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: Generated + col_40772ba813d97356c66a42e44c29f2f2: number + col_3a5ccab1310685a8dc5a8ecf5d187e56: Generated + col_f136803ab9c241079ba0cc1b5d02ee77: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table7ff09e5ace1dbf00f39b6358c5aa3817 { + col_1d726898491fbca9a8dac855d2be1be8: Generated + id: Generated + col_3cd6b216e8b82554eaa2f6c45b0a3c14: string + col_29b35b7caf7d5ae4d43b9b80ca0f9187: string + col_efcaf7b2db6ef497a009d28351e75abe: number + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table3527de031c6623c0eba6355dcdd45a6b { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_52acd46049d4e8c9c8596637256408f5: number + col_65a3660424a0ab0c1ef22059940bd5e8: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_98c9253c0973d0e076532038838b0ad9: number + col_a0108f4039ad5ef8af15629186ff5d13: number + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: Generated + col_0268179270db272c8d70e400380d7c05: number +} + +export interface Table09612d694974820d8807b91ecde8f26e { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5d4dc010ae24f1dadd43aa67593d0f7c: number +} + +export interface Table38195ba8de22a862c0f40f70ebec25bb { + col_36c8e89b7e4cfc448f69c971486cb123: number + col_4f1b7b2483b9bb0a8b355ef69f09d65e: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_34e7aea5cf0f74ee76b3f5445a3ac1cf: Date + col_c84d2025f9e13529cf7b4aa05275bdf6: Date + id: Generated + col_97ec9689eca8dace826b37d2ed59250b: number + col_3f9be60473c4a309579563a7358c92f8: number + col_83d043db4fdfe6882fb7f01a09d92b11: Date + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table93037210a648b88105c5ee2473337821 { + col_46df0ccd51bb26538db287f8e4e6e4dd: Generated + col_f32428508672211d04f041977bc90510: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_e245eb517472187d10441eca2c4a9aeb: number + col_4bf016b629fc04e08a3080d945fff2e3: number +} + +export interface Table0b5857bb9e98480275f017b7af173e3f { + col_72f96160fd66ba04a6c4d17116faca2a: number + id: Generated + col_6eabf912118c656e74717bf86ec69f73: number + col_32a8fa725b782b014f4fff3183a2de16: number + col_4289648e222b26e875a681db53d46c71: number + col_923c1ce4be0acf2cf56890bbd41ec804: Generated +} + +export interface Tablef27ebdbd2754995818491c1018692ce5 { + col_1d726898491fbca9a8dac855d2be1be8: Generated + col_fe7105552b616f510178c40c861ba761: Generated + col_c5b4c43869f38cb5e5ca94ba8e823f70: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_b170288bf1f61b26a648358866f4d6c6: Generated + col_de2b14ae7499f90736fc4a92327553a5: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: Generated + col_2fcbb818ea112e40bf6f722d975e231b: Generated + col_1840e20d17569bbdcae6da23c2d4efa4: Generated + col_d538cbec9201a31573be8bf3b9e0e331: Generated + col_a29ab34fee08d552ceddc06095b0881c: Generated + id: Generated + col_84267f80102ea16f25ffe6f5be92255e: Generated + col_c55b5208e65984aac86ada39165d9d62: Generated + col_c2e6b054ac1da5187e54d089c9065cad: Generated + col_169ab89ce8908eabce9ec3b6c5350b3a: Generated + col_6fb9b267c35b1007e304854b2349ed7a: Generated + col_ff89eee0bc7c681adc9bc0c956a6e018: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_e245eb517472187d10441eca2c4a9aeb: number + col_d8b2388384ce4e4a14053e9e3c6defd0: Generated + col_6c2d1e1a5e7c824f93773960c28e09f0: Generated + col_4e3f97631a05a2833f3a08434a1677d1: Generated + col_b53dab55eaeda66574268ffea2dd4da0: Generated + col_502fd6332679b81831dba4f9517a555d: Generated + col_85b27642c10d4a6c72c3b6b4d02c8d4d: number + col_3b571dce50f96585528355c99d1c3479: Generated + col_1014c2beb9f5ef346656f1056d0885ed: Generated +} + +export interface Table6009b7b4db0ba922eead74b775f40f8c { + col_066a120fe3abe924fdd105d2073b3a64: number + col_aa0ae4d63455847b0b02a481f9f38ab7: number + id: Generated + col_b6492f8aa398e19ecf4295c9aaf0e72c: string +} + +export interface Tableb6b0fa946d389417c5c3f14aa8ff718a { + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_ca2f68c7e7befa1bb272d18ca7a4c73e: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table3edf238f7262a23abe0751a2ac0af6ff { + col_f32428508672211d04f041977bc90510: string + col_2f76db193eac6ad0f152563313673ac9: Generated + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_9de9baea781a4571ef4ec283c5bfba6a: Generated + col_4649c587e02b1a41f7eb4967032e10c3: Generated + id: Generated + col_6ded30065c525d1945f38fc1b77df3fb: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_885fa91590a5518eb66fcca9a6f11849: Generated + col_9eed1c8dafecf36e8f11783f0f9ee8d3: number + col_910c87c70d93d69a64538ffc358a99de: Generated< + 'Deducción' | 'Otros' | 'Percepción' | 'Producto' | 'Servicio' + > + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table55ac07dee59e4a2d00136b025cc01d9e { + col_f32428508672211d04f041977bc90510: string + col_2f76db193eac6ad0f152563313673ac9: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated +} + +export interface Table09e8ea41829a99131a2920761d339204 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_dcd12b62e76b17d69e313b8897f4f7ea: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_6ded30065c525d1945f38fc1b77df3fb: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table7a616917b50007f39137fa41d2f51ab3 { + col_05f720d878e84cf5edf8810ac56f2f47: string + col_b599f327b0f1350c83e6466016f68dee: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_e3c09cb3d68235ebdf5820c49f6ef2e0: string + col_e714c1cd11c5b7d1de0c5f6e076020b4: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_3e68cb4bb944a2eb1873d121e2c1da9f: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated + col_032711695edadad1acb9974d043ec642: Generated + col_ef1e84bf5d70755290854b1d0d60c91f: string + col_8b5da04bf1aacd462ab37eec2f0dea98: Generated + col_c29343eaf65c23d827ee1a8df41c4c6d: Generated +} + +export interface Table5d8df59e64a9eeca85daebca610e21a7 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_7db3960deab1cd20d8b5b1f5501f3987: number + col_15cf093f2ffd2ac39ed7a55e9ff65d04: number + col_60145e143f1a86609b62f248f3001b9f: number + id: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table526124c9359353a7e56271d12d9fcf0c { + col_53ca581ab7a5aef5b659bc27c81d5d51: string + col_c010021498d7345dadc4ec5be041f8d2: number + col_7e53ed5979e336b304b1d25f30e1f246: Date + col_335290b198cde5bc8fcea40f61749ec1: Generated + col_f8356769c300813cfa84768123cbe45a: Generated + col_7426b7174c1b7d22061329a60b94e63f: string + col_33cee772b203aed80ee06d3dcb2cbcd0: number + col_4be00234d2a8d9d5271b91c49e484dc9: Generated + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_9f63df725d4f28c580e46c10f2df8a03: string +} + +export interface Table6bba2bdcbf233fb7649d1f614417da41 { + col_7844a93ad4b97169834dade975b5beff: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + col_8495897860c0b6e16a8d5c7a149d9d7f: Generated + col_1f6ae20514b228ff1f188515cde39b4e: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table390f512e2274731b2d168d8f19da171c { + col_2f76db193eac6ad0f152563313673ac9: Date + col_80ae6f72f9c170f9e1bcae48c1677802: number + col_5ba4f2b0c4324a2a400b0ae7224889c2: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_a67abae8eff833edb8b78a2ded44f0b3: Date + col_33cee772b203aed80ee06d3dcb2cbcd0: number + col_e9820863f3ff0a31be16598cc3058d78: Generated + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Table1899f801ed13e5910d18c6b08028d7bc { + col_030b50b01f2428db7db79395842e60de: Generated + col_2f76db193eac6ad0f152563313673ac9: Generated + col_7364a488f431ca7296b76a34417395b4: number + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_ec9fee4cc043466cd4926f8982fbc146: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_fb1ecce130681efa10fd55197773267d: number + col_2a8da7b65d4d1b4f0cd0a3160b6706db: Date + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table1082c28dec8a8ffd8ce641335442372f { + col_b0ad63f3936782807cb0b2b171280669: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_62bd695be89e0f07c72268b40df9c2db: number + col_c010021498d7345dadc4ec5be041f8d2: number + col_b7f9bf58bcb05c0cf59083ffb79e366b: Generated + col_7426b7174c1b7d22061329a60b94e63f: number + id: Generated + col_cd52da97b57b116541458d23792b0f11: number + col_1d119125897e514751e695fab638cf81: number +} + +export interface Table72909a5ac37995f967b619c9cafa07d6 { + col_d109701b76ff710c8e7fddd648466bac: string + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_8b5da04bf1aacd462ab37eec2f0dea98: string +} + +export interface Table81a2bed79d9365d1982e76dca5a676bd { + col_7d00e7af094c860f729814df43ab537d: number + col_2f76db193eac6ad0f152563313673ac9: Date + col_5ba4f2b0c4324a2a400b0ae7224889c2: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_33cee772b203aed80ee06d3dcb2cbcd0: number + col_e9820863f3ff0a31be16598cc3058d78: Generated + id: Generated + col_1cbcac9ade6914a00f55838acea44222: number + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_42efda2df902eef7402eecf653a5e144: number + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated + col_8a56730bf7a7298784c796e101f92d75: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table9b567a9f0bdf26ccd8202eff33ff637b { + col_6e6c2420ff71849263a56ddabc5a1731: string +} + +export interface Tablecb69558ad9c32a83ec1e61169d89de2f { + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string +} + +export interface Table69cb4926463c11065ab61382047dd490 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table3d90cba85411f05feed564dbcd2e6e55 { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aeffdc77f4dde5fd265842bc848e92d1: Generated + col_10c0a78ec67f5765bb129e53d403a805: number +} + +export interface Table32254855b1a9a81426acafd370a0edc3 { + col_53ca581ab7a5aef5b659bc27c81d5d51: string + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tableccc5e7a07cecf45f43c5c4efb6bbcd8a { + col_a538a4b49163b8abcdcff07958d6290c: number + col_6dc961f33219b65a0c2a9287c4565f24: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_c010021498d7345dadc4ec5be041f8d2: Generated + col_9d5c513c42957815f422347ca6268eae: Generated + col_33cee772b203aed80ee06d3dcb2cbcd0: number + id: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_145e62065965d2b6e7190139880f1bcb: string +} + +export interface Tablef9ce3585ff20a0b1dd731bdd34cf0ad9 { + col_2f76db193eac6ad0f152563313673ac9: Date + col_4c349d7184176bbf16b24181c7d83b03: number + col_697b738640d40c3284f56dfa8de48189: Date + col_1561c874fb6b23d0cb37194fe3d8f99c: Generated + id: Generated + col_6d2b226b7f4e0e1fbb6ec47efc9fee7a: Generated + col_9a115ebc25a0a303b3ffee1a7437146d: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_ded1e7d25d95c7702086a588883f1a40: number +} + +export interface Table1e275afc8276112b99cfc6f49842958c { + col_754b198717fa5cebdfd613f47444b7b4: Generated + col_86a5806c9362be2f139e52bcd5e35d54: Generated + col_6fbe4a892ea3ffb941c0ecf6176cb249: Generated + col_00970b22f199c1f1097c435cdd756efb: Generated + col_50c315b1fbd8484b7d31c7bbba602733: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_707742b6518f4d5b912446969bfc2288: Generated + col_314858149774214e5c87f0dc2de8a7e5: number +} + +export interface Table24139be0f90bb33600380f06b12b7f1e { + id: Generated + col_663662bf9e825d1b24e8262136d58845: Generated + col_eb6047359d3711dac3175270345f5c3b: string +} + +export interface Tablee0ce7ca1270b4541c302833574025ff0 { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_1c732b74da092461cf485a899f72089c: number + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_aff776838092862d398b58e380901753: Generated +} + +export interface Table129722fe7124ca893b45b3f11aa2797c { + col_f32428508672211d04f041977bc90510: string + col_c2673967f655e97fa0e3f979f0af8bc6: Generated + col_53ca581ab7a5aef5b659bc27c81d5d51: string + id: Generated + col_e245eb517472187d10441eca2c4a9aeb: Generated + col_2af39739cc4e3b5910c918468bb89828: Generated +} + +export interface Table361ca453b125de0694d45757ce574ac8 { + col_ed9174294dbcdc54c009c8087f24fd4e: number + id: Generated + col_0511961bf51aa24c76ca49e18e4395e9: number +} + +export interface Table682bfd8f39f1d3c2f141f8d1bfaba332 { + col_53ca581ab7a5aef5b659bc27c81d5d51: string + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_e245eb517472187d10441eca2c4a9aeb: number + col_acfd3b262b59e5fc131811193f9fc57f: Generated +} + +export interface Tablea4885bc4409d5d6aeca7a333deb32f36 { + id: Generated + col_910c87c70d93d69a64538ffc358a99de: string +} + +export interface Tabled0b3106159632181125a90621e656efe { + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tablef8d58e2b9ad74400d9d3b73e1c56b24c { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table7f3edc307df1ebe46a26c3cdcce01825 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table4cb9cce2545216afbbe27fd7f598670d { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table075be2c4bc1569d9e2a0f43c706845cd { + id: Generated + col_146c07ef2479cedcd54c7c2af5cf3a80: string + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tableb0475c12dae1800e6f9bed0ecc74d8ac { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table9273711f2c37ed469c438c6e604205ad { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table674852753d920a7b8c85e3d7f0741d69 { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Table93fcd1b734496b580b42aff395ed8deb { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: string +} + +export interface Tablee8de821cfe0519477927d4aceb53aedb { + id: Generated + col_454ff479a3b5a9ef082d9be9ac02a6f4: Generated +} + +export interface Tabled97726553a6203a2e1fe24edf923c1bf { + col_2f76db193eac6ad0f152563313673ac9: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_72f96160fd66ba04a6c4d17116faca2a: number + col_7e53ed5979e336b304b1d25f30e1f246: Generated + id: Generated + col_024c93b9757242c6dd6b55cd2d0dd019: Generated + col_9c10dc3d09f0912a7e8dc3a34d7f4cd7: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table10a7b3d08bae6a280681dd4b1b4d03ea { + col_2f76db193eac6ad0f152563313673ac9: Date + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated + col_b1dc1e3377a082304d817664243e7b17: Decimal +} + +export interface MyTable { + col_6f5e1903664b084bf6197f2b86849d5e: Generated + col_1d726898491fbca9a8dac855d2be1be8: number + col_aa4ec89520d642ef7dfed01d47c97c02: Generated + col_2e66a5c7e24d1d066230f368ce8b094e: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_4d742b2f247bec99b41a60acbebc149a: Generated + col_af4e225b70a9bbd83cc3bc0e7ef24cfa: Generated + col_4f84013a8b5e4c2b7529058c8fafcaa8: string + col_40e8a963a35b093731af6c3581d35bd2: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_454ff479a3b5a9ef082d9be9ac02a6f4: string + col_286755fad04869ca523320acce0dc6a4: Generated + col_164b7896ec8e770207febe0812c5f052: Generated + col_3917508388f24a50271f7088b657123c: string + col_6f7a0a5f582c69dd4c6be0a819e862cb: Generated +} + +export interface Table5e096b65e80874785e32076304380404 { + col_033f9699cf99d7dbb6abab6ddadb5984: Generated + col_2f76db193eac6ad0f152563313673ac9: Date + col_de2b14ae7499f90736fc4a92327553a5: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_9d73dfc1b706d61e28cf50df23ad34be: number + id: Generated + col_1cbcac9ade6914a00f55838acea44222: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number + col_83f7d7a27d3135c03186f950e68e1f50: Generated +} + +export interface Tablef3ed8107e22455983dfec1061f270c5b { + col_2f76db193eac6ad0f152563313673ac9: Generated + id: Generated + col_d2508118d0d39e198d1129d87d692d59: Generated + col_c5cfca6ce5a95940c658d8d972f0ba57: number + col_cb1fa9fed74f1879504442547a142c1d: string + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tablec051ae09c6cf6d7d3ea3732241b7ec9d { + col_5e0ed9fb00ecd0c6dd65a18eb7e113d1: Generated + id: Generated + col_d8549244769c9dc8a8cb8382a54feace: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table58d873381dd856073dc8af727f75a83c { + col_a538a4b49163b8abcdcff07958d6290c: number + col_066a120fe3abe924fdd105d2073b3a64: Generated + col_08114ce1193bf6826c3277f27236a6d8: Generated + col_4f1b7b2483b9bb0a8b355ef69f09d65e: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_65412d203d0de8b88fb8688cc686a44e: string + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated + col_6ded30065c525d1945f38fc1b77df3fb: Generated + col_d2508118d0d39e198d1129d87d692d59: Date + col_5607e33e4ae68a12af15c4fa9ada5c93: number + col_a7d0a8fb2a80ce50003c37428903c871: Generated + col_d721d91f1a957bbc273b111830ea9355: string + col_aedc0cb96fca54b979311e9caaf1345b: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Table2efbbc557d528fa0179d8200f20bd375 { + col_066a120fe3abe924fdd105d2073b3a64: Generated + col_08114ce1193bf6826c3277f27236a6d8: Generated + col_4f1b7b2483b9bb0a8b355ef69f09d65e: string + col_2f76db193eac6ad0f152563313673ac9: Date + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + col_7e53ed5979e336b304b1d25f30e1f246: Date + col_33cee772b203aed80ee06d3dcb2cbcd0: number + col_35ccfc3446d7aa0609cab70e86d2b448: Generated + id: Generated + col_9859f83dd80f56deebeb616b6b575ee3: Generated + col_1cbcac9ade6914a00f55838acea44222: Generated + col_6ded30065c525d1945f38fc1b77df3fb: Generated + col_b114935b452a2d927bd5ac81ad9a3d67: string + col_d2508118d0d39e198d1129d87d692d59: Date + col_fa367b08ef9acfa2b65fc6150dee916f: Generated + col_5607e33e4ae68a12af15c4fa9ada5c93: Generated + col_aedc0cb96fca54b979311e9caaf1345b: number + col_aa6b7c7a9c7a177e3f1ba452783eb63b: number +} + +export interface Tableb1c2cd01d00c7351140f576d84e64fc1 { + col_7e53ed5979e336b304b1d25f30e1f246: Date + id: Generated + col_fa367b08ef9acfa2b65fc6150dee916f: number + col_e245eb517472187d10441eca2c4a9aeb: Generated + col_b1dc1e3377a082304d817664243e7b17: Decimal +} + +export interface Table173d3cc6670ed190157f57b6fc5f789f { + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_a7f4797b61012da3a9c1a8049c8a4974: Generated + col_6c58e53058059892b26b3562a7fafa9d: Generated +} + +export interface Tableea7fc406759eeeabe58177f5dc5b2749 { + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_a7f4797b61012da3a9c1a8049c8a4974: Generated + col_6c58e53058059892b26b3562a7fafa9d: Generated +} + +export interface Table5d31333676303c399518c2fc4cf6ce6b { + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_a7f4797b61012da3a9c1a8049c8a4974: Generated + col_6c58e53058059892b26b3562a7fafa9d: Generated +} + +export interface Table411a4da56c483124eeb78d27496b6bcc { + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_a7f4797b61012da3a9c1a8049c8a4974: Generated + col_6c58e53058059892b26b3562a7fafa9d: Generated +} + +export interface Table90b86be76e1fe1dcbfeb23aac1268f60 { + col_80ae6f72f9c170f9e1bcae48c1677802: Generated + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_a7f4797b61012da3a9c1a8049c8a4974: Generated + col_6c58e53058059892b26b3562a7fafa9d: Generated +} + +export interface Tablead803551807f7dd640a7431281e60024 { + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: Generated + id: Generated + col_0169d667d30453fc6d348ede68c6137e: Generated + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Tableacd9c0e145638abbf38c6c9982b5d978 { + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_0169d667d30453fc6d348ede68c6137e: number + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Table03d5e3f6a5a1305f36b6f5e7b40f8607 { + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_0169d667d30453fc6d348ede68c6137e: number + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Table1ad14a94caab8e7087a497085d7136ee { + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_0169d667d30453fc6d348ede68c6137e: number + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Table13e2dfe71a6146bf0f7286309cee804f { + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_0169d667d30453fc6d348ede68c6137e: number + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Tablec227d3e74b291218da01d96ad8e3cbf3 { + col_b44e675732d485b4ded87fd63200ab77: Generated + col_c010021498d7345dadc4ec5be041f8d2: number + id: Generated + col_0169d667d30453fc6d348ede68c6137e: number + col_2371c6548aea76f2e1ac529a3543b7b2: Generated + col_966c0adb6eb8c16d11aacb1b968763ed: Generated +} + +export interface Tabled861babbb2f5fa7cd7537704e5000a83 { + col_c010021498d7345dadc4ec5be041f8d2: Generated + col_739f751f517ccb895cb948a88ad78e43: Generated + col_1a1bd05bcd142859a62686952940e09e: Generated + col_b0da1cb2ad234bc279511b1fb66f6fbf: Generated + col_07b081464f537fa90b1de74e33d9f823: Generated + col_2b9cb9ef1a2977920c1ffd5df4bd4817: Generated + col_9d73dfc1b706d61e28cf50df23ad34be: Generated + col_b29a1ebd32df20c0b7928f43baa738ee: Generated + col_1aba85bbebef5ff4eee2f59cb2d9d7b0: Generated + col_0b683b909d42220ecd33ed3752ac705a: Generated + col_1e8e291fd8d42e9948004ae41f09dece: Generated + col_4241cfa1d3605ca1ae3b0559e88e1eb2: Generated + col_aa6b7c7a9c7a177e3f1ba452783eb63b: Generated + col_93fbfd598e9de1272e10942d7a5cb6ad: Generated + col_13a88bf9e5fc7bc33fd7d95a8bce65ba: Generated + col_484ada59e3bc5009053ee3de7238e81a: Generated +} + +export interface DB { + table_87d275947b3f2a646cd8ee94f591ef86: Table0ec723c3d4ee7229ffba2a11539b0129 + table_cc84d0da913ff5e201cd2498cd11bb90: Tablec67d8be002857dd3a70a2c4edaa4671a + table_49830d6d58c2b4f748ffc67b8b96d2ca: Table3face097074d5d4ea47610accdd3ffc1 + table_808edb85a0b912066126738aee81e217: Tableed617265d2d72c8e748ff8796bfa04c4 + table_53425a83056f97f81ebbed0e3a7c3de9: Table6dc0e4bf3b08bdf7391e5d833d1673d6 + table_736d84cc80e257dc78371b1eb0fb6b20: Table2f01d1d7b29ae047871b62964df1ecc3 + table_487e1ea19a552a812641fce4b25ca0e4: Table9f50dbe0b662b9cea0d016836eb72504 + table_a54c27458b6b5713e6fb731d2d54c2c4: Table4b8e90e811d962e132f9550bc90d5443 + table_ed9c00f680d2f0160414e2616694a04a: Table0ae6c699b7b44b166093c052150a0d36 + table_9996a55495fbffd1989dc4542469aaeb: Table2e45370c867ddf8f0ca7710b63dd2746 + table_426cfbb1cb47118dee5d9aeb9ebc9811: Table19f57540d944441abc6dcebc976adf96 + table_e5cf1dcc3993bf7c0ca2ce7e2f1260a5: Table1d1b84f13a74383d5b0324a011b609c6 + table_c5a8b59778733a9ed1ff55b76feb5aee: Table42dbaa8105cb92599360aa5169283403 + table_4f88999bf83d95515d96be5a0060a387: Table2f81c25612f8fefd026b753a1753974a + table_e0290dd8ecb360c47044a3a2a882dd6f: Tableded2bdb12664e58a26152ebb5fe676bb + table_2fec9061cff5d6a33870e59fcdb06c31: Table23e7a1e5ab953d2a85a5958458d0d824 + table_aae67cbd807d09b0d66e062e70f886dd: Table41bb6a93b802fe08f0b054ae835e2fe4 + table_d1bd6a4f6d21595e92f85d647b668e64: Table8be6c29eada2e2ef68bef0c9c1401a97 + table_8be60df8bd508aa3b21a02c30f2d540e: Tablee91e54a2c273ec3221b4d98ae8d79d12 + table_a489899852f5f4a4a8dd760a53cadf3f: Tablebb08e5d0fa8fd6089eeeb1ba950944a1 + table_157a3bc9c27097f32f4faeb89768cfb1: Tablecaf406ab258ff4b54413265206d55b45 + table_975382265709d29c3f2442e3bfb74935: Table90ffcbc2bfc57b68acac037b0aeae3d4 + table_ecaa5c884efb077761e66b71833b253a: Table1e2c9919a848fb2673ac28780aba041c + table_6c06b705ee850005c02ffbe38262ef25: Table1c765aad5af6c98fb0ef475fadda911b + table_1b322cd0c444c827dac7f30ae058a2be: Table4f240fba2b420e220c97e6e14c4819c6 + table_3ae4d1acdab97e1ef170f779f41ffcce: Tablec97e3cdd896ff553853d7817e4a866e2 + table_4a9207b1eeb4f40952b69f3479a48037: Table4974bbdf9d728fb538d7507cb9f84842 + table_610862f4ec87086144fc1485e6838505: Tablea3c8286fc7df926fbf05e09a03e2ac77 + table_fe1a1676504a5a83e3fbf40eec955a58: Table39bdacc8b55267ffc8bf75ff8ec564ce + table_9d71d7aaffb66d31bc2b9b42a8ff2edf: Table3d68aac4deb5f76aaffa0df8c01e305a + table_6c94f280d7ed171b4ae6f181c9731c34: Table90a64fc3cea5bc905374973771cafcd3 + table_d8dae30aa04afffe6c328708f46ec44d: Tablec479a580bb37024884d940eeacc9c4aa + table_9c3e72d53ff7007af15c6f696fb71812: Tablef7d7a7557d9db50bd40fe14bd830b9d4 + table_81985582f225312b575afc02878085dd: Table1d62a6c4a9763009cda042181b0c48af + table_aa3267f05fecf061f13699e786a51f9e: Table916f65bd1433290f26613ccd976c68a7 + table_11ede743d16afa28cc84bbeadfaa538b: Tabledb34593d7377d2007d44ba4b47dcec97 + table_af0437a4a6189de08d668a2d0ce97b54: Table309c66af6ff8ccb2dcf123e1af40c9c4 + table_98573ae89696f94f48a44b2b079b64f8: Table8921b63f9cb21e8293a8c1ddd9edc866 + table_7980538dcc93de25b4d83b4b3261ed3e: Table2037e68416ae40369fba0a939e7ac693 + table_6d3142ac2a560a1c5c32e699f81bf7ba: Table2b3f160083004c6c92a2ce1faf87067d + table_b37880f0bb08f83da5de92e1efa19943: Table5135075fea7b2d1c93603df216620907 + table_59aae399ad0526272d45ea6c030d0daf: Table5d4433ff31ef75511e70aa6e71495afe + table_6fbe4a892ea3ffb941c0ecf6176cb249: Table9e258b1708bd64e877d6cca1467dee66 + table_fd70d2cc35269b186dc738d8a6ba8324: Table4ed17d356988e97fe63e4b4e3b060917 + table_8d8d4b505b94c6f16ecea33984d5c60c: Tablea95d291bccf0be8becb18282bcbf93a9 + table_fe15a427dc1402f37002fb019dcb3607: Tablee12f5b7476cba16c3b1480cd551d2b42 + table_e36a272a216738299e30a9823c9b0502: Tablea6ca43be611a38117221fef4c761f97d + table_977e92e9417068febe7a1207f0ee014f: Tablef83eda9e66948a4da342fadf183e32c3 + table_5f9639fff5d140e880cfe248f297d49c: Tabledfc2f0e19fe1c0e8dd04191a3035bac8 + table_d5c1fdd37d3bf68fe20c43ca4520f7b8: Table93f030da0924f35ab2e57081dad1e2fb + table_03c2b698e69f760082f5d99cf50fc7cb: Table59fc5666d6be52faaeb255fb48a401ef + table_2e7acdf58c80018a69ea3ff6d311d5e9: Table29c7f8f8df026c49d8ea3ad088e8780a + table_5e34805f74fb904a4f6c1cb475154d11: Table2e5e86f744edaeea91caedf4496cd21e + table_26eae7010f00790dcb9fcea51b458b20: Table27112b32955bc32b3cf3abc78f6041f7 + table_5a7765fcdd3e8de9d430693787b3d7e8: Table6a8129f282adc469ee9233bcf63f20b7 + table_0be8b4549e9512252af1761de32647d1: Table55cda7b389c3f5e26b58e806dc82d6cd + table_5a161ff0fcf3e07c472290ed26b22b88: Tabledddb567693b67dedccc76feeaf305885 + table_1787f968ce9e996c38ea93516367e7d9: Tableb45f137169628885a5507a98a1fd203b + table_59aa2c8b41281fdb8918df77e2d7f549: Table4d0fb43fef8f71d027e1c62ff7b83c1f + table_7c73f0112ea394abeaac85d07c32c96d: Table26d9e67842818bfccbbfca6020d3441a + table_1c0b897074552cc11aeffb1cd2023038: Tablee6de3a3fdf132b2b794ad9358fe01719 + table_7232d71e7a31c3959d4f9758b01bd87b: Table5bdeb7407d8776f9295b3ae2eb482855 + table_c5b4c43869f38cb5e5ca94ba8e823f70: Table259a82d9d67d1b779666191753027e2d + table_ed7ed6a3c5b4edbc5cb93ba6ccf845b0: Tabledfcfa50cfe70d46b898ed0fe276855e4 + table_00970b22f199c1f1097c435cdd756efb: Table11e00101e1b2f60d0a2acfcf46151908 + table_a7991c3173c68564fa7034c40ed76e50: Tableaa828347830464c8d6fb6070585f8786 + table_81342351d9b0750a6a1867e61a69f57d: Table5e1a250360f2e59810faaec5dec74bfb + table_20453ddb146769fa6cfbba4518a94e35: Tablecb67af6a70220cf4b2c4e1121d9351c6 + table_34062ca76ff4de600cd82d2867d743d5: Table01687ac6f71bbab86d7fb8482bf71301 + table_5bc008425c0529ce7f1bf46b23bb12df: Tablee4612831426584576f2d39997ccac9c8 + table_44b3b2fbc1525a37fef79e3ac36ae137: Tablebca1b3f3c2526216b2f01c01fa85f3f9 + table_ac000b4f6f0f6a63f812a54a2fc1d54d: Tablecac2eaeb16e94e019e7e98240c4bac17 + table_d9240bae841332419073d023ec9d7da3: Table7eb5460a0c6690c1dff53fe4940240db + table_7d2894dd74c1eb98e683d83cd9f5e554: Table5c88bed5704d2054a84d44150bf10f21 + table_3c21e15fe7f88da04c6eb6546f1f0c45: Table148eb9256ed9e01df75e345bd7e5c9f7 + table_1f2e67e4f6060a2f4483f85e11e9a4a0: Tablee2c77a8b952bdea3bc2cb7261168ec45 + table_9eaad0288ab657d15b0b89a9b118295d: Tableabd2380e09dc7a608bb7e5c33654540e + table_1a1463b3e26cc157b5bbbb8fde616025: Table7117bf3bff290101f4fa6d6de627b58f + table_7349c3b05f3dfb291a9a73aecd2d61a0: Table202a1f7684d2c58afd3885112de82158 + table_2e34d0c29c1107b75c385543999c12eb: Table93aeee94c0d8d4e08a56c97185d68ae7 + table_06d62f58421acbf930a661bbd3ce7452: Table70c085cf2d0edd14f7dbae474f064b1c + table_aad0e25ecf17545e7bec374e476f9b47: Table2b11443e8af088b453d5119d135803ac + table_ea23a0a1382ceb403c98dfa1a709b361: Table0a542ca892cb0eea27538d7360f718ec + table_f84e347fd6617acc6a0f6a8973248874: Tableb8378493b92a26626ae5745222c8ba69 + table_1adc14dc0a7ec969c3e77d40758cd493: Table565a308ecf3782bb5eb0197187361913 + table_292a9ebdda857f983a6fe42a2c434e05: Tablede86128413dcd68bd6d1ca009b6bae17 + table_36992d0f70a50b1b2ffabb5eea57bd1b: Table57dadc25e9a34c53f66b5cb499d75904 + table_268590e60e51ec5d0b870c82481f2b2e: Table366c7f7bccaea5ff455a9b9265448ebf + table_132d4b3547909bc6256d34163fb57eb7: Tablea4ca5018c98bd7912524f97ccd0a8757 + table_c7c8d495ce776873a2da62c508138d34: Table402dd89f94d2a88ccdf049224dfc8700 + table_70aaa597ccbbd5cd92d99b1cfa7c8b1e: Table92498a6babb7032cdcad6e43158a3359 + table_028c49ccb95cbef10c800198d6d80928: Tablee90356817b37536144c4cf962e9e5165 + table_3d3048db19a6783cbcd9a6ced2755859: Table61a79fc440f7ca2d4190d47ee809f66c + table_b900a90f5f896392fbb4c9055bf75465: Table7051320fedfb456818eb15cdf217186d + table_c1beb04a7642970cf4e59164d0b162ab: Table2d7cdf1ccac4544f81ac78a12d5fba7a + table_8cdd0973d9ca586415d14a8f279149a7: Tablea8e3bfebcd001eac1185867f7dec94a5 + table_50c315b1fbd8484b7d31c7bbba602733: Table148ce21c70970d2809c4028c229445be + table_a4492608b8eebc6dfc51867d1829953f: Tablef650c769203b8bd7eed5a8b7e249c0a5 + table_46cb140f325195b7e28886910ba67990: Table8b9070a970ee019dd472ce6c4a08418b + table_ae8048f57d533f1206ddaff81f6258d5: Tabled580c05f239408ff0392e235078a0ac9 + table_dad30ddb3ecff3c395383e834f041676: Tabledd7d780df70068dcb36a8d8b86bd5298 + table_d6303bbcbeb3da317670fbb8167bfa1d: Table48df920caa8cc60fd47820e9d94ea940 + table_6611f9ee209aef523c4dcde78383a3f1: Table9bb0301f7ca02953fac2add8dcd7630c + table_8d9107a07a519a3e02e2ce5e0f68bd8b: Table5492e76278a71210d7231f4bf905ec2f + table_9c0640720d7b649f506ca4860e443f35: Tableea8e45026529937b0cecd544d8801cfd + table_ab478c8f5bc3681081805bcdc6e4d0c5: Table574ff9acd571619424cd55716f5f1dfb + table_92917d401ca6fb8bf405fd92c407fc13: Tablea79aeca54482635868f02356a51fa369 + table_a68acda553f5f089df441d59eeff1d9c: Tablee7a44a84d410fa64377504e3aa7f72b6 + table_99e321b176fbf3589a92e6084f6c14d4: Tabledcc9250fc52d5fbc25a56cf9c4089073 + table_7518c800f93e5459daa1ff73c7fba821: Table4d82bf6c52ed1355768c73593994c5c2 + table_49781987f16dbfbb517b355b5e7f82b9: Table73f359bc2658d254af39135aed8c3bf2 + table_76d040ce360ee5b120a40f7066084137: Table8cf85c63dc3200ef8145fc0344d00f16 + table_e89504d58e9f3da99aa4c0e6c4284088: Table401ef7ed8ddaa97932ede0de0b157c04 + table_5ceb5499052a2078ef4fce93f69467f8: Table3e875ede7feab48a89082e0c87c364f0 + table_025ad7e58da04ed8536959ba4103b6ae: Table7822bcc86140bf34bc6cdacbd734c0ef + table_312cefebb95f0e122bc9915cc4159167: Table459dc31349051f20a755552adaf60c81 + table_fff4c6195261874920bc7ce92d67d2c2: Table1b8610f190607dfbdafa9c71f72acd43 + table_02bed0f193a8ec672c01e4c7da096850: Table1edc1b921ba66176c3ec62961c248f78 + table_a5fe391b0937a21fdd489d49b271167a: Table9b72afad4afd63d81ab6ef56c926765d + table_17f5074d56437c231c6541a447d3ec23: Table309e0b9a80a4ad911a672f7174429e35 + table_1d97d2be8972551ff4d686c373055915: Tablea0eb46b195e5cbd5e85384a9a1622c14 + table_7025bf2bf5e2e0d6a4487fc0cfbda364: Tableea5b8f9c56ff4ae4cb3c8f22541b33ba + table_50b53baf2b58394268ae89bddaf7c095: Table3a87f98012aa2362e8eb64ed4d9e8f65 + table_50430e0942ebf25480aa9da4b89279bf: Tablefe2e32091534caf00911fc7435cbc97b + table_86e46f16f4e6a386a521c67241d2d42f: Table3cdcee0d77b622e8a2ec654a21270877 + table_a697494b3fdab0b4e126bd3a64e9f9c7: Table8077f242ad5e05394a3dd81ce79565f7 + table_6e128f9931a1632c258c1281fdf4636e: Table52a8644bfd17799495525f2fd8adc480 + table_b43f07de70bf895db779f5f97cb8d105: Table74bb684c41a71a06f561e10ffa4e3323 + table_dfdf6321467f7b66dd8ad7b08e1d54e1: Table64ac7b9a171e9af222924b693d44887c + table_6c334f6291be7fa62a9aca0feca9da96: Tablefd230cdd0c0d7bc04b06cffa3bc3fa3e + table_d4391454b38c50461bc8606d5dc6e203: Table2bf8003c7e2a7344f4d30c3a351926c5 + table_d35939386c5c0d22ce7f7c32e8646e0a: Table5c1f23359d009924d75dac9200da99ab + table_82a555b6d488646ce24524e71bb056f5: Tablea6cdaf7c2c1c27d8389fb8ec7831b240 + table_bb0744a0bc7973cbcca7a79bad78451e: Table618b14672aa41a4ddbaa07b4668e9ceb + table_707742b6518f4d5b912446969bfc2288: Tablee0ac1a12db533e45722bf75854c5f68f + table_99ebe26a1b9b9b2bf0ae1e00435dbebd: Table6b64130a9ac5e9179051a15684e76956 + table_0f9e8815806e611d4c51af6a6e4f1a0f: Tablec3c4b9ba46ff23ee52e9fa59ba4cbd42 + table_81ae6fb42f5779dfbad0a06795277f3b: Tableac8f964f7407177bed83d5a1b563c4be + table_ac0e4fab912414bf6c4d82ca5e679862: Table0e3703752e6fb26a7c95ffbf9ba62354 + table_6cbf92b1a3e27faab6982aafe47bc0fd: Table051ebef2507af0ef791ed665f9df9f69 + table_ffd3383867c4ae4d8c200d3c09006427: Tablea85ed4b894e81fcae5b5f32832c0a4aa + table_4566a5569c4fa2c2ba07bfa6b8397135: Tablef2e554fc6e599ca882cad3f9c8944c5f + table_8bf9d631744222f061702b22effbed68: Table92318b69e7164d55b1815a563a4f0fda + table_fe3fd6febea537a97cc193cfaef7aa05: Table11c3ad790c98516ecb794068e8fe4332 + table_af3d9c9d6bb7af18c920bc8963996ba1: Table0b9fd044caf91b1c9c0decf463f48479 + table_eab40d2792fcf58cdf6d56df7c676d02: Tablecb5f0e2e2a6bb82fe0ec713f3001adae + table_81859a5a7cf340e00333e133ee83c6a3: Table05f7a75fd9ce14b6ff3abafe6a0315cd + table_5b6c09d8090e38e65507dd7138348724: Tablef9d0cc88ca4af2fc064f9fe28c1cae51 + table_1084015020d8bed5c99852780cd060b5: Tabledf515f554daadb410af199b9d2af4968 + table_56bb19565d266b3c5db4b36b54121de1: Tablee7013b1aa66ff80026b9594533e73417 + table_fc8beec5010138f14cac210396ce946a: Table6fc94749b5f65b04e1364bc84a3b675a + table_b1de95565fefb12c1b5e9ced0c31aaba: Table7e5a6e895e47f444ce7042734df8ce92 + table_7ee33378e7a9581242b177c22d55035c: Table03fcd39a79bd9fbbc8f2f32371eb6a9b + table_889380bbad7db8828028ff1756840b4e: Table3edc0e801929e10d8dd381561aa2bd31 + table_a89ef28d2e368372d285517fbcd8c8ff: Table75f7cd025a872d2698d2a6e53a8cd953 + table_3176e3c369a24003ab8f6bdeb5e36fb7: Table075f9410d29eaec83d4f68c3dfa82a10 + table_28536ba4b4a961d66303d15d863e9bbf: Tableb91d0a254d43344084dc0efd98b90946 + table_905ec07246a5e10d75f1ec0c71c1cbb8: Table4d4605d2d071f927c338880fcda7b540 + table_649ac5d5790cfd996132f99bcae54699: Table14a5541144254091c320909a95c4c614 + table_9034bb7c1383ffc26a086c9da4db9f44: Table9eb67e79e0e2abbb0a295904fd81d805 + table_5cc5636d8577bebe57cb65d9f80ccf60: Tableb16695c526d6fa5e20b329d5eabfd364 + table_943bf2c7f95a1de4c96d117762255431: Tablea700a17706246884b5a04cafcebeaa55 + table_a99e53e8b0ea6fb028b8335f6879b541: Table6a853060686c878e7535e29bc3772d73 + table_dbdc90d2efd6251b1dc89255b5547d58: Table2c1e8df56b265a10929de25e4593033b + table_011ff64548a9d77b1672d61c71b144cd: Table3c8b015b7697cce06653b215afc8acd5 + table_7dd9f7bddf3aaf04099d008f1615d84d: Tablef058a40da08114d390fcea4b81b4f0a8 + table_91ae77857e698e702b1d36d8086e94a4: Tablea6a7b2d92feb037592b7ed49c34d1e54 + table_5e350d0500edbf693ecf31bc420dce10: Table4aba31682ab92f6f2baae3f68f9dc1ad + table_986fcbfdf73cf8637d5863efcc4a23b7: Table75d4ed2c1cedc7a0f7e36059177a8837 + table_87b2787be746d9af6fa1f02c7174b6de: Tablec3ba8dac5adcbeb93ed2dd458b6ad4a1 + table_adebe9d523b0e904d5a23244df8acc86: Tablee53ad4b421c58913d023a3296c13419e + table_0e3a2fd2e1be1fb139d982cc4cf27a37: Table980786dce470ebfd2621534ebcd5e6e9 + table_044c3ac3bb3a8f36c94c6914fec4c640: Table1a270e99026f39aaf8e33546e1c2c39e + table_0b5ac72e03509e06683edcba4b3887ab: Table380b3be3b993931074af40f4e0af76f3 + table_2746d6a0415f4e97db91054eaad8f549: Table4a03b332b99d0dab4e359b9b2e9132f3 + table_c3489d7044badb74bfa78892e5af3c50: Tableb690efdc73ab53d741824e87ddf41a0a + table_27157dcf07c6222108aee0ad07fc3c84: Table80586ee503601f161507ee3f207fa19a + table_9616c4051420073951c9dadfe4cfecd8: Table138bb88f13d65a10a42882c2578d7839 + table_382969f15d228ca40eaaf6ae3af42dd0: Tablef8ad778cddf738c853992b7f5446b4f1 + table_ba15c662ce189e01e69a2426db41e884: Table9f6e2daac23f8ae268d4557cb47cb202 + table_ef0c79ab9a79c6eedf5a3738c59b4e35: Table7ff09e5ace1dbf00f39b6358c5aa3817 + table_737ecf944683adc260346e8cc807a707: Table3527de031c6623c0eba6355dcdd45a6b + table_38773f13c7c7902bd4b3717a240873e7: Table09612d694974820d8807b91ecde8f26e + table_1a55586b73d1a759c1eadbafd3e87c2e: Table38195ba8de22a862c0f40f70ebec25bb + table_de936835b986b7e9a72b8706204dc183: Table93037210a648b88105c5ee2473337821 + table_1ae34354f4623d6118be8b39a6c33401: Table0b5857bb9e98480275f017b7af173e3f + table_b1d00f154cae4b5689f1c60fd746cc6c: Tablef27ebdbd2754995818491c1018692ce5 + table_5dd25bad4af1a1b2e85d63d182300992: Table6009b7b4db0ba922eead74b775f40f8c + table_d119c7e31c78a3c779aae46a89f48594: Tableb6b0fa946d389417c5c3f14aa8ff718a + table_1c9d336338cae6fb14a0990bd1c54bc7: Table3edf238f7262a23abe0751a2ac0af6ff + table_6b6b30980569c916119c335ee3ecd6fd: Table55ac07dee59e4a2d00136b025cc01d9e + table_c5c92cab080fe1234767a8be5814e634: Table09e8ea41829a99131a2920761d339204 + table_0556e22da4d46d9d6f4f679de33c729b: Table7a616917b50007f39137fa41d2f51ab3 + table_d1b04d199a69310996ab11fa49aebf4d: Table5d8df59e64a9eeca85daebca610e21a7 + table_03fd35f2b6a928e66a8894f914fdeb1a: Table526124c9359353a7e56271d12d9fcf0c + table_18563b6f693d0c3e17d63aad91681eba: Table6bba2bdcbf233fb7649d1f614417da41 + table_5d1d4bbd5ec1f145b6fe12b78aa0a2ee: Table390f512e2274731b2d168d8f19da171c + table_c1069472c40f0480a45ee4dee787c4d1: Table1899f801ed13e5910d18c6b08028d7bc + table_b20ae4cbae25a95b31299dc9033fd532: Table1082c28dec8a8ffd8ce641335442372f + table_5761eb47c363bdcf7bbf68c1801bf699: Table72909a5ac37995f967b619c9cafa07d6 + table_404d5ee6f26bda1b4be093317b500097: Table81a2bed79d9365d1982e76dca5a676bd + table_e8e1d616dac945e0ee0d05480ab77957: Table9b567a9f0bdf26ccd8202eff33ff637b + table_de8db519594b7d21efea9f31260b2423: Tablecb69558ad9c32a83ec1e61169d89de2f + table_735c26d97cd342c6161c22f66cc99511: Table3d90cba85411f05feed564dbcd2e6e55 + table_cf38d23f2daeff354349485b97299972: Table32254855b1a9a81426acafd370a0edc3 + table_92df6eeac3e4b00edeee9dfe99d6640c: Table69cb4926463c11065ab61382047dd490 + table_fefdea6d099a9810a67b711700a35a8d: Tableccc5e7a07cecf45f43c5c4efb6bbcd8a + table_a3cb20d19c33e8fb3b00b07fba07def0: Table1e275afc8276112b99cfc6f49842958c + table_d5db6cc36112076d805e58cb5ec13850: Tablef9ce3585ff20a0b1dd731bdd34cf0ad9 + table_dc93e59ae64c8cdae881b6f25d64564b: Table24139be0f90bb33600380f06b12b7f1e + table_6efc03d13cdcfd25a9ce3e87fc5456e7: Tablee0ce7ca1270b4541c302833574025ff0 + table_6dbc88c5b052629e050a099e04d7be3f: Table129722fe7124ca893b45b3f11aa2797c + table_4a4402f5d23fa4f9df3b8bb8e3dc2d32: Table361ca453b125de0694d45757ce574ac8 + table_8e94e6bf050f60606c5da22bcc6bb9b2: Table682bfd8f39f1d3c2f141f8d1bfaba332 + table_170edf3aa8f832f0f36d9b9a0c549ed4: Tablea4885bc4409d5d6aeca7a333deb32f36 + table_8320808a7934e9fa32c17c6499414fb1: Tablee8de821cfe0519477927d4aceb53aedb + table_ff3ba17c4964fdcaa9d663b6fb483dd3: Tabled0b3106159632181125a90621e656efe + table_8f9d849161a30cb7431d221283f29c43: Tablef8d58e2b9ad74400d9d3b73e1c56b24c + table_593cef89801d5094cdd7ad51ea3eb29c: Table7f3edc307df1ebe46a26c3cdcce01825 + table_e021bdbeb32ee1578e7462ebf75dfb7a: Table4cb9cce2545216afbbe27fd7f598670d + table_2d8fef6ee4574f9180a0c7f593f47442: Table075be2c4bc1569d9e2a0f43c706845cd + table_cdf7f8adce5092d3805a7526a2551c2a: Tableb0475c12dae1800e6f9bed0ecc74d8ac + table_ea38a156b8018fa2d1034393d595c1b5: Table9273711f2c37ed469c438c6e604205ad + table_871b4c9fc59eb1005340453c9936c38d: Table674852753d920a7b8c85e3d7f0741d69 + table_000a8a0cb7f265a624c851d3e7f8b946: Table93fcd1b734496b580b42aff395ed8deb + table_e868614c9689160521a0f7cd8fe444f9: Tabled97726553a6203a2e1fe24edf923c1bf + table_eb4b47c599bd30509bef47d1ea3a29e0: Table10a7b3d08bae6a280681dd4b1b4d03ea + my_table: MyTable + table_1474a7e0348b1ca363ee4a2a5dc4a1ec: Table5e096b65e80874785e32076304380404 + table_921d1c0613d8e78089f21fdc086c6254: Tablef3ed8107e22455983dfec1061f270c5b + table_8f856811783bf2c6dc1cd3fafa065e91: Tablec051ae09c6cf6d7d3ea3732241b7ec9d + table_276c7389f255e47fa56f3db3b7b203f4: Table58d873381dd856073dc8af727f75a83c + table_f0e61821517de97ae60ce2db4211f6de: Table2efbbc557d528fa0179d8200f20bd375 + table_444765b0f61d176f3e075fcd0aba7ac9: Tableb1c2cd01d00c7351140f576d84e64fc1 + table_7a27c425067f969bf5207fe7646fd5c7: Table173d3cc6670ed190157f57b6fc5f789f + table_b4454c9867da1740a95d198a0309bb1c: Tableea7fc406759eeeabe58177f5dc5b2749 + table_d7b45912e5ca97cdbb9a13ddeae017e9: Table5d31333676303c399518c2fc4cf6ce6b + table_ede804aecaaa0ad1a5afffb9b2a0d927: Table411a4da56c483124eeb78d27496b6bcc + table_18c53fd6b99123cd1d5416c633d1d257: Table90b86be76e1fe1dcbfeb23aac1268f60 + table_3b5daebf6b386d473d143fa615a63b30: Tablead803551807f7dd640a7431281e60024 + table_3e32e29fb3ace788a139de83f6baf360: Tableacd9c0e145638abbf38c6c9982b5d978 + table_6b330a3b9c49a6e7867514797facf15e: Table03d5e3f6a5a1305f36b6f5e7b40f8607 + table_0193342fd1dd6f0d57893e54a6235090: Table1ad14a94caab8e7087a497085d7136ee + table_c5f1901de2a3954057ebdea18ec92851: Table13e2dfe71a6146bf0f7286309cee804f + table_b5e6a54f6ffa9f4314e45fcf5ddfc499: Tablec227d3e74b291218da01d96ad8e3cbf3 + table_da9bc7793b7e3784c03d55128145b7e3: Tabled861babbb2f5fa7cd7537704e5000a83 +} + +function testHugeDBSelect(db: Kysely) { + db.selectFrom((eb) => + eb + .selectFrom('my_table') + .where((eb) => eb('my_table.id', 'in', [1, 2, 3])) + .selectAll() + .as('test'), + ).selectAll() + + expectError( + db + .selectFrom((eb) => + eb + .selectFrom('not_a_table') + .where('my_table.id', 'in', [1, 2, 3]) + .selectAll() + .as('test'), + ) + .selectAll(), + ) +} + +function testHugeDBInsert(db: Kysely) { + db.insertInto('my_table').defaultValues() + + expectError(db.insertInto('not_a_table').defaultValues()) +} + +function testHugeDBUpdate(db: Kysely) { + db.updateTable('my_table').where((eb) => eb('my_table.id', 'in', [1, 2, 3])) + db.updateTable('my_table') + .from('table_000a8a0cb7f265a624c851d3e7f8b946') + .where((eb) => eb('my_table.id', 'in', [1, 2, 3])) + .set('my_table.col_164b7896ec8e770207febe0812c5f052', 1) + + expectError( + db.updateTable('not_a_table').where('my_table.id', 'in', [1, 2, 3]), + ) +} + +function testHugeDBDelete(db: Kysely) { + db.deleteFrom('my_table').where((eb) => eb('my_table.id', 'in', [1, 2, 3])) + + expectError( + db.deleteFrom('not_a_table').where('my_table.id', 'in', [1, 2, 3]), + ) +} diff --git a/test/typings/test-d/kysely-any.test-d.ts b/test/typings/test-d/kysely-any.test-d.ts index 74c849380..2cd376411 100644 --- a/test/typings/test-d/kysely-any.test-d.ts +++ b/test/typings/test-d/kysely-any.test-d.ts @@ -21,7 +21,7 @@ async function testKyselyAnySelects(db: Kysely) { .selectFrom(['foo1', 'foo2']) .select(['spam', 'foo1.bar', 'foo2.baz', 'doesnotexists.fux']) .execute() - expectType<{ spam: any; bar: any; baz: any; fux: never }[]>(r5) + expectType<{ spam: any; bar: any; baz: any; fux: any }[]>(r5) const r6 = await db .selectFrom('foo') From 14f5dbb7eac9d006c06f38e8fb0eaf08bd030d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Koskim=C3=A4ki?= Date: Mon, 22 Jul 2024 15:18:49 +0300 Subject: [PATCH 04/28] add reusable helpers recipe and implement missing expression features (#1085) * add reusable helpers recipe and implement missing expression features * force node 22.4.1 in CI because of an npm bug --- .github/workflows/test.yml | 12 +- site/docs/recipes/0001-reusable-helpers.md | 238 +++++++++++++++++++++ site/docs/recipes/0006-expressions.md | 42 ++-- src/expression/expression-builder.ts | 12 +- src/query-builder/select-query-builder.ts | 66 +++++- src/raw-builder/sql.ts | 16 +- test/typings/test-d/expression.test-d.ts | 21 +- test/typings/test-d/select.test-d.ts | 7 + 8 files changed, 377 insertions(+), 37 deletions(-) create mode 100644 site/docs/recipes/0001-reusable-helpers.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a0a17600..405a26f89 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.4.1] steps: - uses: actions/checkout@v4 @@ -44,7 +44,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.4.1] steps: - uses: actions/checkout@v4 @@ -79,7 +79,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.x + node-version: 22.4.1 cache: 'npm' - name: Use Deno ${{ matrix.deno-version }} @@ -114,7 +114,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.x + node-version: 22.4.1 cache: 'npm' - name: Install dependencies @@ -141,7 +141,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.x + node-version: 22.4.1 cache: 'npm' - name: Install dependencies @@ -163,7 +163,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.x + node-version: 22.4.1 cache: 'npm' - name: Install dependencies diff --git a/site/docs/recipes/0001-reusable-helpers.md b/site/docs/recipes/0001-reusable-helpers.md new file mode 100644 index 000000000..2cce66949 --- /dev/null +++ b/site/docs/recipes/0001-reusable-helpers.md @@ -0,0 +1,238 @@ +# Reusable helpers + +:::info +[Here's](https://kyse.link/qm67s) a playground link containing all the code in this recipe. +::: + +Let's say you want to write the following query: + +```sql +SELECT id, first_name +FROM person +WHERE upper(last_name) = $1 +``` + +Kysely doesn't have a built-in `upper` function but there are at least three ways you could write this: + +```ts +const lastName = 'STALLONE' + +const persons = await db + .selectFrom('person') + .select(['id', 'first_name']) + // 1. `sql` template tag. This is the least type-safe option. + // You're providing the column name without any type-checking, + // and plugins won't affect it. + .where( + sql`upper(last_name)`, '=', lastName + ) + // 2. `sql` template tag with `ref`. Anything passed to `ref` + // gets type-checked against the accumulated query context. + .where(({ eb, ref }) => eb( + sql`upper(${ref('last_name')})`, '=', lastName + )) + // 3. The `fn` function helps you avoid missing parentheses/commas + // errors and uses refs as 1st class arguments. + .where(({ eb, fn }) => eb( + fn('upper', ['last_name']), '=', lastName + )) + .execute() +``` + +but each option could be more readable or type-safe. + +Fortunately Kysely allows you to easily create composable, reusable and type-safe helper functions: + +```ts +import { Expression, sql } from 'kysely' + +function upper(expr: Expression) { + return sql`upper(${expr})` +} + +function lower(expr: Expression) { + return sql`lower(${expr})` +} + +function concat(...exprs: Expression[]) { + return sql.join(exprs, sql`||`) +} +``` + +Using the `upper` helper, our query would look like this: + +```ts +const lastName = 'STALLONE' + +const persons = await db + .selectFrom('person') + .select(['id', 'first_name']) + .where(({ eb, ref }) => eb( + upper(ref('last_name')), '=', lastName + )) + .execute() +``` + +The recipe for helper functions is simple: take inputs as `Expression` instances where `T` is the type of the expression. For example `upper` takes in any `string` expression since it transforms strings to upper case. If you implemented the `round` function, it'd take in `Expression` since you can only round numbers. + +The helper functions should then use the inputs to create an output that's also an `Expression`. Everything you can create using the expression builder is an instance of `Expression`. So is the output of the `sql` template tag and all methods under the `sql` object. Same goes for `SelectQueryBuilder` and pretty much everything else in Kysely. Everything's an expression. + +See [this recipe](https://kysely.dev/docs/recipes/expressions) to learn more about expressions. + +So we've learned that everything's an expression and that expressions are composable. Let's put this idea to use: + +```ts +const persons = await db + .selectFrom('person') + .select(['id', 'first_name']) + .where(({ eb, ref, val }) => eb( + concat( + lower(ref('first_name')), + val(' '), + upper(ref('last_name')) + ), + '=', + 'sylvester STALLONE' + )) + .execute() +``` + +So far we've only used our helper functions in the first argument of `where` but you can use them anywhere: + +```ts +const persons = await db + .selectFrom('person') + .innerJoin('pet', (join) => join.on(eb => eb( + 'person.first_name', '=', lower(eb.ref('pet.name')) + ))) + .select(({ ref, val }) => [ + 'first_name', + // If you use a helper in `select`, you need to always provide an explicit + // name for it using the `as` method. + concat(ref('person.first_name'), val(' '), ref('pet.name')).as('name_with_pet') + ]) + .orderBy(({ ref }) => lower(ref('first_name'))) + .execute() +``` + +## Reusable helpers using `ExpressionBuilder` + +Here's an example of a helper function that uses the expression builder instead of raw SQL: + +```ts +import { Expression, expressionBuilder } from 'kysely' + +function idsOfPersonsThatHaveDogNamed(name: Expression) { + const eb = expressionBuilder() + + // A subquery that returns the identifiers of all persons + // that have a dog named `name`. + return eb + .selectFrom('pet') + .select('pet.owner_id') + .where('pet.species', '=', 'dog') + .where('pet.name', '=', name) +} +``` + +And here's how you could use it: + +```ts +const dogName = 'Doggo' + +const persons = await db + .selectFrom('person') + .selectAll('person') + .where((eb) => eb( + 'person.id', 'in', idsOfPersonsThatHaveDogNamed(eb.val(dogName)) + )) + .execute() +``` + +Note that `idsOfPersonsThatHaveDogNamed` doesn't execute a separate query but instead returns a subquery expression that's compiled as a part of the parent query: + +```sql +select + person.* +from + person +where + person.id in ( + select pet.owner_id + from pet + where pet.species = 'dog' + and pet.name = ? + ) +``` + +In all our examples we've used the following syntax: + +```ts +.where(eb => eb(left, operator, right)) +``` + +When the expression builder `eb` is used as a function, it creates a binary expression. All binary expressions with a comparison operator are represented as a `Expression`. You don't always need to return `eb(left, operator, right)` from the callback though. Since `Expressions` are composable and reusable, you can return any `Expression`. + +This means you can create helpers like this: + +```ts +function isOlderThan(age: Expression) { + return sql`age > ${age}` +} +``` + +```ts +const persons = await db + .selectFrom('person') + .select(['id', 'first_name']) + .where(({ val }) => isOlderThan(val(60))) + .execute() +``` + +## Dealing with nullable expressions + +If you want your helpers to work with nullable expressions (nullable columns etc.), you can do something like this: + +```ts +import { Expression } from 'kysely' + +// This function accepts both nullable and non-nullable string expressions. +function toInt(expr: Expression) { + // This returns `Expression` if `expr` is nullable + // and `Expression` otherwise. + return sql`(${expr})::integer` +} +``` + +## Passing select queries as expressions + +Let's say we have the following query: + +```ts +const expr: Expression<{ name: string }> = db + .selectFrom('pet') + .select('pet.name') +``` + +The expression type of our query is `Expression<{ name: string }>` but SQL allows you to use a query like that as an `Expression`. In other words, SQL allows you to use single-column record types like scalars. Most of the time Kysely is able to automatically handle this case but with helper functions you need to use `$asScalar()` to convert the type. Here's an example: + +```ts +const persons = await db + .selectFrom('person') + .select((eb) => [ + 'id', + 'first_name', + upper( + eb.selectFrom('pet') + .select('name') + .whereRef('person.id', '=', 'pet.owner_id') + .limit(1) + .$asScalar() // <-- This is needed + .$notNull() + ).as('pet_name') + ]) +``` + +The subquery is an `Expression<{ name: string }>` but our `upper` function only accepts `Expression`. That's why we need to call `$asScalar()`. `$asScalar()` has no effect on the generated SQL. It's simply a type-level helper. + +We also used `$notNull()` in the example because our simple `upper` function doesn't support nullable expressions. \ No newline at end of file diff --git a/site/docs/recipes/0006-expressions.md b/site/docs/recipes/0006-expressions.md index 3ac86cead..154eb88a6 100644 --- a/site/docs/recipes/0006-expressions.md +++ b/site/docs/recipes/0006-expressions.md @@ -6,9 +6,9 @@ An [`Expression`](https://kysely-org.github.io/kysely-apidoc/interfaces/Expre ## Expression builder -Expressions are usually built using an instance of [`ExpressionBuilder`](https://kysely-org.github.io/kysely-apidoc/interfaces/ExpressionBuilder.html). `DB` is the same database type you give to `Kysely` when you create an instance. `TB` is the union of all table names that are visible in the context. For example `ExpressionBuilder` means you can access `person` and `pet` tables and all their columns in the expression. +Expressions are usually built using an instance of [`ExpressionBuilder`](https://kysely-org.github.io/kysely-apidoc/interfaces/ExpressionBuilder.html). `DB` is the same database type you give to `Kysely` when you create an instance. `TB` is the union of all table names that are visible in the context. For example `ExpressionBuilder` means you can reference `person` and `pet` columns in the created expressions. -You can get an instance of the expression builder by using a callback: +You can get an instance of the expression builder using a callback: ```ts const person = await db @@ -28,7 +28,13 @@ const person = await db .as('pet_name'), // Select a boolean expression - eb('first_name', '=', 'Jennifer').as('is_jennifer') + eb('first_name', '=', 'Jennifer').as('is_jennifer'), + + // Select a static string value + eb.val('Some value').as('string_value'), + + // Select a literal value + eb.lit(42).as('literal_value'), ]) // You can also destructure the expression builder like this .where(({ and, or, eb, not, exists, selectFrom }) => or([ @@ -63,19 +69,21 @@ select limit 1 ) as "pet_name", - "first_name" = $1 as "is_jennifer" + "first_name" = $1 as "is_jennifer", + $2 as "string_value", + 42 as "literal_value" from "person" where ( ( - "first_name" = $2 - and "last_name" = $3 + "first_name" = $3 + and "last_name" = $4 ) or not exists ( select "pet.id" from "pet" where "pet"."owner_id" = "person"."id" - and "pet"."species" in ($4, $5) + and "pet"."species" in ($5, $6) ) ) ``` @@ -91,11 +99,17 @@ There's also a global function `expressionBuilder` you can use to create express ```ts import { expressionBuilder } from 'kysely' -// `eb1` has type `ExpressionBuilder` -const eb1 = expressionBuilder() +// `eb1` has type `ExpressionBuilder` which means there are no tables in the +// context. This variant should be used most of the time in helper functions since you +// shouldn't make assumptions about the calling context. +const eb1 = expressionBuilder() + +// `eb2` has type `ExpressionBuilder`. You can reference `person` columns +// directly in all expression builder methods. +const eb2 = expressionBuilder() // In this one you'd have access to tables `person` and `pet` and all their columns. -const eb2 = expressionBuilder() +const eb3 = expressionBuilder() let qb = query .selectFrom('person') @@ -141,7 +155,7 @@ const doggoPersons = await db .execute() ``` -The above helper is not very type-safe. The following code would compile, but fail at runtime: +However, the above helper is not very type-safe. The following code would compile, but fail at runtime: ```ts const bigFatFailure = await db @@ -160,7 +174,7 @@ in arbitrary expressions instead of just values. function hasDogNamed(name: Expression, ownerId: Expression) { // Create an expression builder without any tables in the context. // This way we make no assumptions about the calling context. - const eb = expressionBuilder() + const eb = expressionBuilder() return eb.exists( eb.selectFrom('pet') @@ -182,11 +196,13 @@ const doggoPersons = await db .execute() ``` +Learn more about reusable helper functions [here](https://kysely.dev/docs/recipes/reusable-helpers). + ## Conditional expressions In the following, we'll only cover `where` expressions. The same logic applies to `having`, `on`, `orderBy`, `groupBy` etc. -> This section should not be confused with conditional selections in `select` clauses, which is a whole 'nother topic we discuss in [this recipe](https://www.kysely.dev/docs/recipes/conditional-selects). +> This section should not be confused with conditional selections in `select` clauses, which is a whole 'nother topic we discuss in [this recipe](https://kysely.dev/docs/recipes/conditional-selects). Having a set of optional filters you want to combine using `and`, is the most basic and common use case of conditional `where` expressions. Since the `where`, `having` and other filter functions are additive, most of the time this is enough: diff --git a/src/expression/expression-builder.ts b/src/expression/expression-builder.ts index 318b0a8ec..b9c9ad584 100644 --- a/src/expression/expression-builder.ts +++ b/src/expression/expression-builder.ts @@ -194,9 +194,9 @@ export interface ExpressionBuilder { * ```ts * const result = await db.selectFrom('person') * .where(({ eb, exists, selectFrom }) => - * eb('first_name', '=', 'Jennifer').and( - * exists(selectFrom('pet').whereRef('owner_id', '=', 'person.id').select('pet.id')) - * ) + * eb('first_name', '=', 'Jennifer').and(exists( + * selectFrom('pet').whereRef('owner_id', '=', 'person.id').select('pet.id') + * )) * ) * .selectAll() * .execute() @@ -1379,10 +1379,10 @@ export function expressionBuilder( _: SelectQueryBuilder, ): ExpressionBuilder -export function expressionBuilder(): ExpressionBuilder< +export function expressionBuilder< DB, - TB -> + TB extends keyof DB = never, +>(): ExpressionBuilder export function expressionBuilder( _?: unknown, diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index da098a5ad..b61c2650d 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -243,7 +243,7 @@ export interface SelectQueryBuilder * import { sql } from 'kysely' * * const persons = await db.selectFrom('person') - * .select(({ eb, selectFrom, or }) => [ + * .select(({ eb, selectFrom, or, val, lit }) => [ * // Select a correlated subquery * selectFrom('pet') * .whereRef('person.id', '=', 'pet.owner_id') @@ -260,7 +260,13 @@ export interface SelectQueryBuilder * ]).as('is_jennifer_or_arnold'), * * // Select a raw sql expression - * sql`concat(first_name, ' ', last_name)`.as('full_name') + * sql`concat(first_name, ' ', last_name)`.as('full_name'). + * + * // Select a static string value + * val('Some value').as('string_value'), + * + * // Select a literal value + * lit(42).as('literal_value'), * ]) * .execute() * ``` @@ -277,7 +283,9 @@ export interface SelectQueryBuilder * limit $1 * ) as "pet_name", * ("first_name" = $2 or "first_name" = $3) as "jennifer_or_arnold", - * concat(first_name, ' ', last_name) as "full_name" + * concat(first_name, ' ', last_name) as "full_name", + * $4 as "string_value", + * 42 as "literal_value" * from "person" * ``` * @@ -1928,6 +1936,54 @@ export interface SelectQueryBuilder ? ExpressionWrapper : KyselyTypeError<'$asTuple() call failed: All selected columns must be provided as arguments'> + /** + * Plucks the value type of the output record. + * + * In SQL, any record type that only has one column can be used as a scalar. + * For example a query like this works: + * + * ```sql + * select + * id, + * first_name + * from + * person as p + * where + * -- This is ok since the query only selects one row + * -- and one column. + * (select name from pet where pet.owner_id = p.id limit 1) = 'Doggo' + * ``` + * + * In many cases Kysely handles this automatically and picks the correct + * scalar type instead of the record type, but sometimes you need to give + * Kysely a hint. + * + * One such case are custom helper functions that take `Expression` + * instances as inputs: + * + * ```ts + * function doStuff(expr: Expression) { + * ... + * } + * + * // Error! This is not ok because the expression type is + * // `{ first_name: string }` instead of `string`. + * doStuff(db.selectFrom('person').select('first_name')) + * + * // Ok! This is ok since we've plucked the `string` type of the + * // only column in the output type. + * doStuff(db.selectFrom('person').select('first_name').$asScalar()) + * ``` + * + * This function has absolutely no effect on the generated SQL. It's + * purely a type-level helper. + * + * This method returns an `ExpressionWrapper` instead of a `SelectQueryBuilder` + * since the return value should only be used as a part of an expression + * and never executed as the main query. + */ + $asScalar(): ExpressionWrapper + /** * Narrows (parts of) the output type of the query. * @@ -2561,6 +2617,10 @@ class SelectQueryBuilderImpl return new ExpressionWrapper(this.toOperationNode()) } + $asScalar(): ExpressionWrapper { + return new ExpressionWrapper(this.toOperationNode()) + } + withPlugin(plugin: KyselyPlugin): SelectQueryBuilder { return new SelectQueryBuilderImpl({ ...this.#props, diff --git a/src/raw-builder/sql.ts b/src/raw-builder/sql.ts index 3320d4e1b..b50b54ba8 100644 --- a/src/raw-builder/sql.ts +++ b/src/raw-builder/sql.ts @@ -230,7 +230,7 @@ export interface Sql { * select first_name from "public"."person" * ``` */ - table(tableReference: string): RawBuilder + table(tableReference: string): RawBuilder /** * This can be used to add arbitrary identifiers to SQL snippets. @@ -273,7 +273,7 @@ export interface Sql { * select "public"."person"."first_name" from "public"."person" * ``` */ - id(...ids: readonly string[]): RawBuilder + id(...ids: readonly string[]): RawBuilder /** * This can be used to add literal values to SQL snippets. @@ -383,10 +383,10 @@ export interface Sql { * BEFORE $1::varchar, (1 == 1)::varchar, (select * from "person")::varchar, false::varchar, "first_name" AFTER * ``` */ - join( + join( array: readonly unknown[], separator?: RawBuilder, - ): RawBuilder + ): RawBuilder } export const sql: Sql = Object.assign( @@ -421,14 +421,14 @@ export const sql: Sql = Object.assign( return this.val(value) }, - table(tableReference: string): RawBuilder { + table(tableReference: string): RawBuilder { return createRawBuilder({ queryId: createQueryId(), rawNode: RawNode.createWithChild(parseTable(tableReference)), }) }, - id(...ids: readonly string[]): RawBuilder { + id(...ids: readonly string[]): RawBuilder { const fragments = new Array(ids.length + 1).fill('.') fragments[0] = '' @@ -458,10 +458,10 @@ export const sql: Sql = Object.assign( }) }, - join( + join( array: readonly unknown[], separator: RawBuilder = sql`, `, - ): RawBuilder { + ): RawBuilder { const nodes = new Array(2 * array.length - 1) const sep = separator.toOperationNode() diff --git a/test/typings/test-d/expression.test-d.ts b/test/typings/test-d/expression.test-d.ts index 8c2180c4b..0b88fc81e 100644 --- a/test/typings/test-d/expression.test-d.ts +++ b/test/typings/test-d/expression.test-d.ts @@ -4,7 +4,13 @@ import { expectError, expectType, } from 'tsd' -import { Expression, ExpressionBuilder, Kysely, SqlBool } from '..' +import { + Expression, + ExpressionBuilder, + Kysely, + SqlBool, + expressionBuilder, +} from '..' import { Database } from '../shared' import { KyselyTypeError } from '../../../dist/cjs/util/type-error' @@ -260,3 +266,16 @@ function testExpressionBuilderTuple(db: Kysely) { .$asTuple('first_name', 'last_name', 'last_name'), ) } + +function testExpressionBuilderConstructor(db: Kysely) { + const eb1 = expressionBuilder() + expectType>(eb1) + + const eb2 = expressionBuilder() + expectType>(eb2) + + const eb3 = expressionBuilder( + db.selectFrom('action').innerJoin('pet', (join) => join.onTrue()), + ) + expectType>(eb3) +} diff --git a/test/typings/test-d/select.test-d.ts b/test/typings/test-d/select.test-d.ts index d3b0837e8..fd5997951 100644 --- a/test/typings/test-d/select.test-d.ts +++ b/test/typings/test-d/select.test-d.ts @@ -1,5 +1,6 @@ import { Expression, + ExpressionWrapper, Kysely, NotNull, RawBuilder, @@ -132,6 +133,12 @@ async function testSelectSingle(db: Kysely) { expectType(r17.callback_url) expectType(r17.queue_id) + + const expr1 = db.selectFrom('person').select('first_name').$asScalar() + expectType>(expr1) + + const expr2 = db.selectFrom('person').select('deleted_at').$asScalar() + expectType>(expr2) } async function testSelectAll(db: Kysely) { From 4a9d5c6e41b3741aee5df3cc922134bc87ca7901 Mon Sep 17 00:00:00 2001 From: Dapper Mink Date: Fri, 2 Aug 2024 19:55:48 +0900 Subject: [PATCH 05/28] feat: support refresh materialized view (#990) * feat: add postgres range types (#1086) * feat: support refresh naterialized view * fix tests by adding .materialized() to remove the matview * fix failing test * fix: References typo (#1092) * chore: refresh-view-node.ts => refresh-materialized-view-node.ts * chore: export node in index.ts --------- Co-authored-by: Isak <16906089+kansson@users.noreply.github.com> Co-authored-by: Jonathan Wu --- src/index.ts | 2 + .../operation-node-transformer.ts | 11 ++ src/operation-node/operation-node-visitor.ts | 3 + src/operation-node/operation-node.ts | 1 + .../refresh-materialized-view-node.ts | 41 +++++++ .../with-schema/with-schema-transformer.ts | 1 + src/query-compiler/default-query-compiler.ts | 17 +++ src/query-compiler/query-compiler.ts | 2 + .../refresh-materialized-view-builder.ts | 103 ++++++++++++++++++ src/schema/schema.ts | 22 ++++ test/node/src/schema.test.ts | 81 ++++++++++++++ 11 files changed, 284 insertions(+) create mode 100644 src/operation-node/refresh-materialized-view-node.ts create mode 100644 src/schema/refresh-materialized-view-builder.ts diff --git a/src/index.ts b/src/index.ts index 9818f1131..b467d871c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,6 +53,7 @@ export * from './schema/column-definition-builder.js' export * from './schema/foreign-key-constraint-builder.js' export * from './schema/alter-table-builder.js' export * from './schema/create-view-builder.js' +export * from './schema/refresh-materialized-view-builder.js' export * from './schema/drop-view-builder.js' export * from './schema/alter-column-builder.js' @@ -133,6 +134,7 @@ export * from './operation-node/create-schema-node.js' export * from './operation-node/create-table-node.js' export * from './operation-node/create-type-node.js' export * from './operation-node/create-view-node.js' +export * from './operation-node/refresh-materialized-view-node.js' export * from './operation-node/data-type-node.js' export * from './operation-node/default-insert-value-node.js' export * from './operation-node/default-value-node.js' diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index 2e2574760..4ccabaa88 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -94,6 +94,7 @@ import { CastNode } from './cast-node.js' import { FetchNode } from './fetch-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' +import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' /** * Transforms an operation node tree into another one. @@ -195,6 +196,7 @@ export class OperationNodeTransformer { DropConstraintNode: this.transformDropConstraint.bind(this), ForeignKeyConstraintNode: this.transformForeignKeyConstraint.bind(this), CreateViewNode: this.transformCreateView.bind(this), + RefreshMaterializedViewNode: this.transformRefreshMaterializedView.bind(this), DropViewNode: this.transformDropView.bind(this), GeneratedNode: this.transformGenerated.bind(this), DefaultValueNode: this.transformDefaultValue.bind(this), @@ -802,6 +804,15 @@ export class OperationNodeTransformer { }) } + protected transformRefreshMaterializedView(node: RefreshMaterializedViewNode): RefreshMaterializedViewNode { + return requireAllProps({ + kind: 'RefreshMaterializedViewNode', + name: this.transformNode(node.name), + concurrently: node.concurrently, + withNoData: node.withNoData, + }) + } + protected transformDropView(node: DropViewNode): DropViewNode { return requireAllProps({ kind: 'DropViewNode', diff --git a/src/operation-node/operation-node-visitor.ts b/src/operation-node/operation-node-visitor.ts index f88f12539..bbe02da53 100644 --- a/src/operation-node/operation-node-visitor.ts +++ b/src/operation-node/operation-node-visitor.ts @@ -96,6 +96,7 @@ import { CastNode } from './cast-node.js' import { FetchNode } from './fetch-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' +import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' export abstract class OperationNodeVisitor { protected readonly nodeStack: OperationNode[] = [] @@ -166,6 +167,7 @@ export abstract class OperationNodeVisitor { DropConstraintNode: this.visitDropConstraint.bind(this), ForeignKeyConstraintNode: this.visitForeignKeyConstraint.bind(this), CreateViewNode: this.visitCreateView.bind(this), + RefreshMaterializedViewNode: this.visitRefreshMaterializedView.bind(this), DropViewNode: this.visitDropView.bind(this), GeneratedNode: this.visitGenerated.bind(this), DefaultValueNode: this.visitDefaultValue.bind(this), @@ -277,6 +279,7 @@ export abstract class OperationNodeVisitor { protected abstract visitPrimitiveValueList(node: PrimitiveValueListNode): void protected abstract visitOperator(node: OperatorNode): void protected abstract visitCreateView(node: CreateViewNode): void + protected abstract visitRefreshMaterializedView(node: RefreshMaterializedViewNode): void protected abstract visitDropView(node: DropViewNode): void protected abstract visitGenerated(node: GeneratedNode): void protected abstract visitDefaultValue(node: DefaultValueNode): void diff --git a/src/operation-node/operation-node.ts b/src/operation-node/operation-node.ts index 6476a24eb..7786b7ec1 100644 --- a/src/operation-node/operation-node.ts +++ b/src/operation-node/operation-node.ts @@ -58,6 +58,7 @@ export type OperationNodeKind = | 'AddConstraintNode' | 'DropConstraintNode' | 'CreateViewNode' + | 'RefreshMaterializedViewNode' | 'DropViewNode' | 'GeneratedNode' | 'DefaultValueNode' diff --git a/src/operation-node/refresh-materialized-view-node.ts b/src/operation-node/refresh-materialized-view-node.ts new file mode 100644 index 000000000..e421a087b --- /dev/null +++ b/src/operation-node/refresh-materialized-view-node.ts @@ -0,0 +1,41 @@ +import { freeze } from '../util/object-utils.js' +import { OperationNode } from './operation-node.js' +import { SchemableIdentifierNode } from './schemable-identifier-node.js' + +export type RefreshMaterializedViewNodeParams = Omit< + Partial, + 'kind' | 'name' +> + +export interface RefreshMaterializedViewNode extends OperationNode { + readonly kind: 'RefreshMaterializedViewNode' + readonly name: SchemableIdentifierNode + readonly concurrently?: boolean + readonly withNoData?: boolean +} + +/** + * @internal + */ +export const RefreshMaterializedViewNode = freeze({ + is(node: OperationNode): node is RefreshMaterializedViewNode { + return node.kind === 'RefreshMaterializedViewNode' + }, + + create(name: string): RefreshMaterializedViewNode { + return freeze({ + kind: 'RefreshMaterializedViewNode', + name: SchemableIdentifierNode.create(name), + }) + }, + + cloneWith( + createView: RefreshMaterializedViewNode, + params: RefreshMaterializedViewNodeParams, + ): RefreshMaterializedViewNode { + return freeze({ + ...createView, + ...params, + }) + }, +}) diff --git a/src/plugin/with-schema/with-schema-transformer.ts b/src/plugin/with-schema/with-schema-transformer.ts index 571bd0277..a6c4046ee 100644 --- a/src/plugin/with-schema/with-schema-transformer.ts +++ b/src/plugin/with-schema/with-schema-transformer.ts @@ -24,6 +24,7 @@ const ROOT_OPERATION_NODES: Record = freeze({ CreateTableNode: true, CreateTypeNode: true, CreateViewNode: true, + RefreshMaterializedViewNode: true, DeleteQueryNode: true, DropIndexNode: true, DropSchemaNode: true, diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 22095172b..f2c4e2379 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -111,6 +111,7 @@ import { CastNode } from '../operation-node/cast-node.js' import { FetchNode } from '../operation-node/fetch-node.js' import { TopNode } from '../operation-node/top-node.js' import { OutputNode } from '../operation-node/output-node.js' +import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' export class DefaultQueryCompiler extends OperationNodeVisitor @@ -1253,6 +1254,22 @@ export class DefaultQueryCompiler this.visitNode(node.as) } } + + protected override visitRefreshMaterializedView(node: RefreshMaterializedViewNode): void { + this.append('refresh materialized view ') + + if (node.concurrently) { + this.append('concurrently ') + } + + this.visitNode(node.name) + + if (node.withNoData) { + this.append(' with no data') + } else { + this.append(' with data') + } + } protected override visitDropView(node: DropViewNode): void { this.append('drop ') diff --git a/src/query-compiler/query-compiler.ts b/src/query-compiler/query-compiler.ts index ec3ed9a63..8a14b7a81 100644 --- a/src/query-compiler/query-compiler.ts +++ b/src/query-compiler/query-compiler.ts @@ -12,6 +12,7 @@ import { DropViewNode } from '../operation-node/drop-view-node.js' import { MergeQueryNode } from '../operation-node/merge-query-node.js' import { QueryNode } from '../operation-node/query-node.js' import { RawNode } from '../operation-node/raw-node.js' +import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' import { CompiledQuery } from './compiled-query.js' export type RootOperationNode = @@ -20,6 +21,7 @@ export type RootOperationNode = | CreateIndexNode | CreateSchemaNode | CreateViewNode + | RefreshMaterializedViewNode | DropTableNode | DropIndexNode | DropSchemaNode diff --git a/src/schema/refresh-materialized-view-builder.ts b/src/schema/refresh-materialized-view-builder.ts new file mode 100644 index 000000000..6691dd120 --- /dev/null +++ b/src/schema/refresh-materialized-view-builder.ts @@ -0,0 +1,103 @@ +import { OperationNodeSource } from '../operation-node/operation-node-source.js' +import { CompiledQuery } from '../query-compiler/compiled-query.js' +import { Compilable } from '../util/compilable.js' +import { preventAwait } from '../util/prevent-await.js' +import { QueryExecutor } from '../query-executor/query-executor.js' +import { QueryId } from '../util/query-id.js' +import { freeze } from '../util/object-utils.js' +import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' + +export class RefreshMaterializedViewBuilder implements OperationNodeSource, Compilable { + readonly #props: RefreshMaterializedViewBuilderProps + + constructor(props: RefreshMaterializedViewBuilderProps) { + this.#props = freeze(props) + } + + /** + * Adds the "concurrently" modifier. + * + * Use this to refresh the view without locking out concurrent selects on the materialized view. + * + * WARNING! + * This cannot be used with the "with no data" modifier. + */ + concurrently(): RefreshMaterializedViewBuilder { + return new RefreshMaterializedViewBuilder({ + ...this.#props, + node: RefreshMaterializedViewNode.cloneWith(this.#props.node, { + concurrently: true, + withNoData: false, + }), + }) + } + + /** + * Adds the "with data" modifier. + * + * If specified (or defaults) the backing query is executed to provide the new data, and the materialized view is left in a scannable state + */ + withData(): RefreshMaterializedViewBuilder { + return new RefreshMaterializedViewBuilder({ + ...this.#props, + node: RefreshMaterializedViewNode.cloneWith(this.#props.node, { + withNoData: false, + }), + }) + } + + /** + * Adds the "with no data" modifier. + * + * If specified, no new data is generated and the materialized view is left in an unscannable state. + * + * WARNING! + * This cannot be used with the "concurrently" modifier. + */ + withNoData(): RefreshMaterializedViewBuilder { + return new RefreshMaterializedViewBuilder({ + ...this.#props, + node: RefreshMaterializedViewNode.cloneWith(this.#props.node, { + withNoData: true, + concurrently: false, + }), + }) + } + + /** + * Simply calls the provided function passing `this` as the only argument. `$call` returns + * what the provided function returns. + */ + $call(func: (qb: this) => T): T { + return func(this) + } + + toOperationNode(): RefreshMaterializedViewNode { + return this.#props.executor.transformQuery( + this.#props.node, + this.#props.queryId, + ) + } + + compile(): CompiledQuery { + return this.#props.executor.compileQuery( + this.toOperationNode(), + this.#props.queryId, + ) + } + + async execute(): Promise { + await this.#props.executor.executeQuery(this.compile(), this.#props.queryId) + } +} + +preventAwait( + RefreshMaterializedViewBuilder, + "don't await RefreshMaterializedViewBuilder instances directly. To execute the query you need to call `execute`", +) + +export interface RefreshMaterializedViewBuilderProps { + readonly queryId: QueryId + readonly executor: QueryExecutor + readonly node: RefreshMaterializedViewNode +} diff --git a/src/schema/schema.ts b/src/schema/schema.ts index 555483fb8..1f8fe414d 100644 --- a/src/schema/schema.ts +++ b/src/schema/schema.ts @@ -26,6 +26,8 @@ import { DropTypeBuilder } from './drop-type-builder.js' import { CreateTypeNode } from '../operation-node/create-type-node.js' import { DropTypeNode } from '../operation-node/drop-type-node.js' import { parseSchemableIdentifier } from '../parser/identifier-parser.js' +import { RefreshMaterializedViewBuilder } from './refresh-materialized-view-builder.js' +import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' /** * Provides methods for building database schema. @@ -234,6 +236,26 @@ export class SchemaModule { }) } + /** + * Refresh a materialized view. + * + * ### Examples + * + * ```ts + * await db.schema + * .refreshMaterializedView('my_view') + * .concurrently() + * .execute() + * ``` + */ + refreshMaterializedView(viewName: string): RefreshMaterializedViewBuilder { + return new RefreshMaterializedViewBuilder({ + queryId: createQueryId(), + executor: this.#executor, + node: RefreshMaterializedViewNode.create(viewName), + }) + } + /** * Drop a view. * diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index 624b77267..3816e4084 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -1949,6 +1949,87 @@ for (const dialect of DIALECTS) { } }) + describe('refresh materialized view', () => { + beforeEach(async () => { + await ctx.db.schema + .createView('materialized_dogs') + .materialized() + .as(ctx.db.selectFrom('pet').selectAll().where('species', '=', 'dog')) + .execute() + }) + + afterEach(async () => { + await ctx.db.schema + .dropView('materialized_dogs') + .materialized() + .ifExists() + .execute() + }) + + if (dialect === 'postgres') { + it('should refresh a materialized view', async () => { + const builder = ctx.db.schema + .refreshMaterializedView('materialized_dogs') + + testSql(builder, dialect, { + postgres: { + sql: `refresh materialized view "materialized_dogs" with data`, + parameters: [], + }, + mssql: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await builder.execute() + }) + + it('should refresh a materialized view concurrently', async () => { + // concurrent refreshes require a unique index + await ctx.db.schema + .createIndex('materialized_dogs_index') + .unique() + .on('materialized_dogs') + .columns(['id']) + .execute() + + const builder = ctx.db.schema + .refreshMaterializedView('materialized_dogs') + .concurrently() + + testSql(builder, dialect, { + postgres: { + sql: `refresh materialized view concurrently "materialized_dogs" with data`, + parameters: [], + }, + mssql: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await builder.execute() + }) + + it('should refresh a materialized view with no data', async () => { + const builder = ctx.db.schema + .refreshMaterializedView('materialized_dogs') + .withNoData() + + testSql(builder, dialect, { + postgres: { + sql: `refresh materialized view "materialized_dogs" with no data`, + parameters: [], + }, + mssql: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await builder.execute() + }) + } + }) + describe('drop view', () => { beforeEach(async () => { await ctx.db.schema From cb85120d45871689a9d3323aedc28182dd4398ff Mon Sep 17 00:00:00 2001 From: wirekang Date: Sun, 29 Sep 2024 22:41:57 +0900 Subject: [PATCH 06/28] Remove preventAwait (#1160) Related #748 --- src/kysely.ts | 11 ---------- .../operation-node-transformer.ts | 7 +++++-- src/operation-node/operation-node-visitor.ts | 4 +++- .../aggregate-function-builder.ts | 6 ------ src/query-builder/cte-builder.ts | 6 ------ src/query-builder/delete-query-builder.ts | 6 ------ src/query-builder/insert-query-builder.ts | 6 ------ src/query-builder/join-builder.ts | 6 ------ src/query-builder/merge-query-builder.ts | 21 ------------------- src/query-builder/on-conflict-builder.ts | 13 ------------ src/query-builder/over-builder.ts | 6 ------ src/query-builder/select-query-builder.ts | 11 ---------- src/query-builder/update-query-builder.ts | 6 ------ src/query-compiler/default-query-compiler.ts | 10 ++++++--- src/raw-builder/raw-builder.ts | 11 ---------- src/schema/alter-column-builder.ts | 5 ----- ...able-add-foreign-key-constraint-builder.ts | 6 ------ src/schema/alter-table-add-index-builder.ts | 6 ------ src/schema/alter-table-builder.ts | 8 ------- .../alter-table-drop-constraint-builder.ts | 6 ------ src/schema/alter-table-executor.ts | 6 ------ src/schema/column-definition-builder.ts | 6 ------ src/schema/create-index-builder.ts | 6 ------ src/schema/create-schema-builder.ts | 6 ------ src/schema/create-table-builder.ts | 6 ------ src/schema/create-type-builder.ts | 6 ------ src/schema/create-view-builder.ts | 6 ------ src/schema/drop-index-builder.ts | 6 ------ src/schema/drop-schema-builder.ts | 6 ------ src/schema/drop-table-builder.ts | 6 ------ src/schema/drop-type-builder.ts | 6 ------ src/schema/drop-view-builder.ts | 6 ------ src/schema/foreign-key-constraint-builder.ts | 6 ------ .../refresh-materialized-view-builder.ts | 20 +++++++----------- src/schema/schema.ts | 4 ++-- src/schema/unique-constraint-builder.ts | 6 ------ src/util/prevent-await.ts | 10 --------- test/node/src/schema.test.ts | 6 +++--- 38 files changed, 28 insertions(+), 257 deletions(-) delete mode 100644 src/util/prevent-await.ts diff --git a/src/kysely.ts b/src/kysely.ts index dcb978f3f..e76505300 100644 --- a/src/kysely.ts +++ b/src/kysely.ts @@ -16,7 +16,6 @@ import { TransactionSettings, TRANSACTION_ISOLATION_LEVELS, } from './driver/driver.js' -import { preventAwait } from './util/prevent-await.js' import { createFunctionModule, FunctionModule, @@ -566,11 +565,6 @@ export class ConnectionBuilder { interface ConnectionBuilderProps extends KyselyProps {} -preventAwait( - ConnectionBuilder, - "don't await ConnectionBuilder instances directly. To execute the query you need to call the `execute` method", -) - export class TransactionBuilder { readonly #props: TransactionBuilderProps @@ -619,11 +613,6 @@ interface TransactionBuilderProps extends KyselyProps { readonly isolationLevel?: IsolationLevel } -preventAwait( - TransactionBuilder, - "don't await TransactionBuilder instances directly. To execute the transaction you need to call the `execute` method", -) - function validateTransactionSettings(settings: TransactionSettings): void { if ( settings.isolationLevel && diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index 4ccabaa88..b5cadff5f 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -196,7 +196,8 @@ export class OperationNodeTransformer { DropConstraintNode: this.transformDropConstraint.bind(this), ForeignKeyConstraintNode: this.transformForeignKeyConstraint.bind(this), CreateViewNode: this.transformCreateView.bind(this), - RefreshMaterializedViewNode: this.transformRefreshMaterializedView.bind(this), + RefreshMaterializedViewNode: + this.transformRefreshMaterializedView.bind(this), DropViewNode: this.transformDropView.bind(this), GeneratedNode: this.transformGenerated.bind(this), DefaultValueNode: this.transformDefaultValue.bind(this), @@ -804,7 +805,9 @@ export class OperationNodeTransformer { }) } - protected transformRefreshMaterializedView(node: RefreshMaterializedViewNode): RefreshMaterializedViewNode { + protected transformRefreshMaterializedView( + node: RefreshMaterializedViewNode, + ): RefreshMaterializedViewNode { return requireAllProps({ kind: 'RefreshMaterializedViewNode', name: this.transformNode(node.name), diff --git a/src/operation-node/operation-node-visitor.ts b/src/operation-node/operation-node-visitor.ts index bbe02da53..533f58098 100644 --- a/src/operation-node/operation-node-visitor.ts +++ b/src/operation-node/operation-node-visitor.ts @@ -279,7 +279,9 @@ export abstract class OperationNodeVisitor { protected abstract visitPrimitiveValueList(node: PrimitiveValueListNode): void protected abstract visitOperator(node: OperatorNode): void protected abstract visitCreateView(node: CreateViewNode): void - protected abstract visitRefreshMaterializedView(node: RefreshMaterializedViewNode): void + protected abstract visitRefreshMaterializedView( + node: RefreshMaterializedViewNode, + ): void protected abstract visitDropView(node: DropViewNode): void protected abstract visitGenerated(node: GeneratedNode): void protected abstract visitDefaultValue(node: DefaultValueNode): void diff --git a/src/query-builder/aggregate-function-builder.ts b/src/query-builder/aggregate-function-builder.ts index 9aff159f8..64b6ff512 100644 --- a/src/query-builder/aggregate-function-builder.ts +++ b/src/query-builder/aggregate-function-builder.ts @@ -2,7 +2,6 @@ import { freeze } from '../util/object-utils.js' import { AggregateFunctionNode } from '../operation-node/aggregate-function-node.js' import { AliasNode } from '../operation-node/alias-node.js' import { IdentifierNode } from '../operation-node/identifier-node.js' -import { preventAwait } from '../util/prevent-await.js' import { OverBuilder } from './over-builder.js' import { createOverBuilder } from '../parser/parse-utils.js' import { @@ -344,11 +343,6 @@ export class AggregateFunctionBuilder } } -preventAwait( - AggregateFunctionBuilder, - "don't await AggregateFunctionBuilder instances. They are never executed directly and are always just a part of a query.", -) - /** * {@link AggregateFunctionBuilder} with an alias. The result of calling {@link AggregateFunctionBuilder.as}. */ diff --git a/src/query-builder/cte-builder.ts b/src/query-builder/cte-builder.ts index b1d6013c3..7e0591640 100644 --- a/src/query-builder/cte-builder.ts +++ b/src/query-builder/cte-builder.ts @@ -1,6 +1,5 @@ import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CommonTableExpressionNode } from '../operation-node/common-table-expression-node.js' -import { preventAwait } from '../util/prevent-await.js' import { freeze } from '../util/object-utils.js' export class CTEBuilder implements OperationNodeSource { @@ -39,11 +38,6 @@ export class CTEBuilder implements OperationNodeSource { } } -preventAwait( - CTEBuilder, - "don't await CTEBuilder instances. They are never executed directly and are always just a part of a query.", -) - interface CTEBuilderProps { readonly node: CommonTableExpressionNode } diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index c8023e17c..a0ece541a 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -35,7 +35,6 @@ import { SimplifySingleResult, SqlBool, } from '../util/type-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { Compilable } from '../util/compilable.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' @@ -1178,11 +1177,6 @@ export class DeleteQueryBuilder } } -preventAwait( - DeleteQueryBuilder, - "don't await DeleteQueryBuilder instances directly. To execute the query you need to call `execute` or `executeTakeFirst`.", -) - export interface DeleteQueryBuilderProps { readonly queryId: QueryId readonly queryNode: DeleteQueryNode diff --git a/src/query-builder/insert-query-builder.ts b/src/query-builder/insert-query-builder.ts index 2dc2b1483..2d6b481c1 100644 --- a/src/query-builder/insert-query-builder.ts +++ b/src/query-builder/insert-query-builder.ts @@ -22,7 +22,6 @@ import { UpdateObjectExpression, parseUpdateObjectExpression, } from '../parser/update-set-parser.js' -import { preventAwait } from '../util/prevent-await.js' import { Compilable } from '../util/compilable.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' @@ -1187,11 +1186,6 @@ export class InsertQueryBuilder } } -preventAwait( - InsertQueryBuilder, - "don't await InsertQueryBuilder instances directly. To execute the query you need to call `execute` or `executeTakeFirst`.", -) - export interface InsertQueryBuilderProps { readonly queryId: QueryId readonly queryNode: InsertQueryNode diff --git a/src/query-builder/join-builder.ts b/src/query-builder/join-builder.ts index 9866faa67..da2163877 100644 --- a/src/query-builder/join-builder.ts +++ b/src/query-builder/join-builder.ts @@ -10,7 +10,6 @@ import { import { ExpressionOrFactory } from '../parser/expression-parser.js' import { ReferenceExpression } from '../parser/reference-parser.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { SqlBool } from '../util/type-utils.js' export class JoinBuilder @@ -92,11 +91,6 @@ export class JoinBuilder } } -preventAwait( - JoinBuilder, - "don't await JoinBuilder instances. They are never executed directly and are always just a part of a query.", -) - export interface JoinBuilderProps { readonly joinNode: JoinNode } diff --git a/src/query-builder/merge-query-builder.ts b/src/query-builder/merge-query-builder.ts index d7c8fe20e..e4ea17841 100644 --- a/src/query-builder/merge-query-builder.ts +++ b/src/query-builder/merge-query-builder.ts @@ -37,7 +37,6 @@ import { NOOP_QUERY_EXECUTOR } from '../query-executor/noop-query-executor.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { Compilable } from '../util/compilable.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryId } from '../util/query-id.js' import { ShallowRecord, @@ -263,11 +262,6 @@ export class MergeQueryBuilder } } -preventAwait( - MergeQueryBuilder, - "don't await MergeQueryBuilder instances directly. To execute the query you need to call `execute` when available.", -) - export interface MergeQueryBuilderProps { readonly queryId: QueryId readonly queryNode: MergeQueryNode @@ -840,11 +834,6 @@ export class WheneableMergeQueryBuilder< } } -preventAwait( - WheneableMergeQueryBuilder, - "don't await WheneableMergeQueryBuilder instances directly. To execute the query you need to call `execute`.", -) - export class MatchedThenableMergeQueryBuilder< DB, TT extends keyof DB, @@ -1041,11 +1030,6 @@ export class MatchedThenableMergeQueryBuilder< } } -preventAwait( - MatchedThenableMergeQueryBuilder, - "don't await MatchedThenableMergeQueryBuilder instances directly. To execute the query you need to call `execute` when available.", -) - export class NotMatchedThenableMergeQueryBuilder< DB, TT extends keyof DB, @@ -1152,11 +1136,6 @@ export class NotMatchedThenableMergeQueryBuilder< } } -preventAwait( - NotMatchedThenableMergeQueryBuilder, - "don't await NotMatchedThenableMergeQueryBuilder instances directly. To execute the query you need to call `execute` when available.", -) - export type ExtractWheneableMergeQueryBuilder< DB, TT extends keyof DB, diff --git a/src/query-builder/on-conflict-builder.ts b/src/query-builder/on-conflict-builder.ts index 6fce43e1c..fce275dc5 100644 --- a/src/query-builder/on-conflict-builder.ts +++ b/src/query-builder/on-conflict-builder.ts @@ -17,7 +17,6 @@ import { } from '../parser/update-set-parser.js' import { Updateable } from '../util/column-type.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { AnyColumn, SqlBool } from '../util/type-utils.js' import { WhereInterface } from './where-interface.js' @@ -276,8 +275,6 @@ export interface OnConflictBuilderProps { readonly onConflictNode: OnConflictNode } -preventAwait(OnConflictBuilder, "don't await OnConflictBuilder instances.") - export type OnConflictDatabase = { [K in keyof DB | 'excluded']: Updateable } @@ -298,11 +295,6 @@ export class OnConflictDoNothingBuilder } } -preventAwait( - OnConflictDoNothingBuilder, - "don't await OnConflictDoNothingBuilder instances.", -) - export class OnConflictUpdateBuilder implements WhereInterface, OperationNodeSource { @@ -383,8 +375,3 @@ export class OnConflictUpdateBuilder return this.#props.onConflictNode } } - -preventAwait( - OnConflictUpdateBuilder, - "don't await OnConflictUpdateBuilder instances.", -) diff --git a/src/query-builder/over-builder.ts b/src/query-builder/over-builder.ts index 330be843a..675f4950c 100644 --- a/src/query-builder/over-builder.ts +++ b/src/query-builder/over-builder.ts @@ -12,7 +12,6 @@ import { } from '../parser/partition-by-parser.js' import { StringReference } from '../parser/reference-parser.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' export class OverBuilder implements OperationNodeSource @@ -107,11 +106,6 @@ export class OverBuilder } } -preventAwait( - OverBuilder, - "don't await OverBuilder instances. They are never executed directly and are always just a part of a query.", -) - export interface OverBuilderProps { readonly overNode: OverNode } diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index b61c2650d..3fa5c9a6d 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -40,7 +40,6 @@ import { UndirectedOrderByExpression, parseOrderBy, } from '../parser/order-by-parser.js' -import { preventAwait } from '../util/prevent-await.js' import { LimitNode } from '../operation-node/limit-node.js' import { OffsetNode } from '../operation-node/offset-node.js' import { Compilable } from '../util/compilable.js' @@ -2707,11 +2706,6 @@ class SelectQueryBuilderImpl } } -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 { @@ -2769,11 +2763,6 @@ class AliasedSelectQueryBuilderImpl< } } -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, TB extends keyof DB, diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index 80e3f0df5..a474a2eb9 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -41,7 +41,6 @@ import { ExtractUpdateTypeFromReferenceExpression, parseUpdate, } from '../parser/update-set-parser.js' -import { preventAwait } from '../util/prevent-await.js' import { Compilable } from '../util/compilable.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' @@ -1191,11 +1190,6 @@ export class UpdateQueryBuilder } } -preventAwait( - UpdateQueryBuilder, - "don't await UpdateQueryBuilder instances directly. To execute the query you need to call `execute` or `executeTakeFirst`.", -) - export interface UpdateQueryBuilderProps { readonly queryId: QueryId readonly queryNode: UpdateQueryNode diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index f2c4e2379..5a04313d6 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -810,7 +810,9 @@ export class DefaultQueryCompiler if (node.joins) { if (!node.from) { - throw new Error("Joins in an update query are only supported as a part of a PostgreSQL 'update set from join' query. If you want to create a MySQL 'update join set' query, see https://kysely.dev/docs/examples/update/my-sql-joins") + throw new Error( + "Joins in an update query are only supported as a part of a PostgreSQL 'update set from join' query. If you want to create a MySQL 'update join set' query, see https://kysely.dev/docs/examples/update/my-sql-joins", + ) } this.append(' ') @@ -1254,8 +1256,10 @@ export class DefaultQueryCompiler this.visitNode(node.as) } } - - protected override visitRefreshMaterializedView(node: RefreshMaterializedViewNode): void { + + protected override visitRefreshMaterializedView( + node: RefreshMaterializedViewNode, + ): void { this.append('refresh materialized view ') if (node.concurrently) { diff --git a/src/raw-builder/raw-builder.ts b/src/raw-builder/raw-builder.ts index 976fee89f..a683f09bc 100644 --- a/src/raw-builder/raw-builder.ts +++ b/src/raw-builder/raw-builder.ts @@ -2,7 +2,6 @@ import { QueryResult } from '../driver/database-connection.js' import { AliasNode } from '../operation-node/alias-node.js' 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 } from '../util/object-utils.js' import { KyselyPlugin } from '../plugin/kysely-plugin.js' @@ -230,11 +229,6 @@ export function createRawBuilder(props: RawBuilderProps): RawBuilder { return new RawBuilderImpl(props) } -preventAwait( - 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}. */ @@ -275,8 +269,3 @@ class AliasedRawBuilderImpl ) } } - -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/schema/alter-column-builder.ts b/src/schema/alter-column-builder.ts index b54398f4f..2b9347a8c 100644 --- a/src/schema/alter-column-builder.ts +++ b/src/schema/alter-column-builder.ts @@ -8,7 +8,6 @@ import { DefaultValueExpression, parseDefaultValueExpression, } from '../parser/default-value-parser.js' -import { preventAwait } from '../util/prevent-await.js' export class AlterColumnBuilder { readonly #column: string @@ -64,8 +63,6 @@ export class AlterColumnBuilder { } } -preventAwait(AlterColumnBuilder, "don't await AlterColumnBuilder instances") - /** * Allows us to force consumers to do exactly one alteration to a column. * @@ -110,5 +107,3 @@ export class AlteredColumnBuilder implements OperationNodeSource { export type AlterColumnBuilderCallback = ( builder: AlterColumnBuilder, ) => AlteredColumnBuilder - -preventAwait(AlteredColumnBuilder, "don't await AlteredColumnBuilder instances") diff --git a/src/schema/alter-table-add-foreign-key-constraint-builder.ts b/src/schema/alter-table-add-foreign-key-constraint-builder.ts index 4bdc3165d..046efa3af 100644 --- a/src/schema/alter-table-add-foreign-key-constraint-builder.ts +++ b/src/schema/alter-table-add-foreign-key-constraint-builder.ts @@ -6,7 +6,6 @@ import { CompiledQuery } from '../query-compiler/compiled-query.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { Compilable } from '../util/compilable.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryId } from '../util/query-id.js' import { ForeignKeyConstraintBuilder, @@ -80,8 +79,3 @@ export interface AlterTableAddForeignKeyConstraintBuilderProps { readonly node: AlterTableNode readonly constraintBuilder: ForeignKeyConstraintBuilder } - -preventAwait( - AlterTableAddForeignKeyConstraintBuilder, - "don't await AlterTableAddForeignKeyConstraintBuilder instances directly. To execute the query you need to call `execute`", -) diff --git a/src/schema/alter-table-add-index-builder.ts b/src/schema/alter-table-add-index-builder.ts index f7d3b4b7a..45b619572 100644 --- a/src/schema/alter-table-add-index-builder.ts +++ b/src/schema/alter-table-add-index-builder.ts @@ -12,7 +12,6 @@ import { CompiledQuery } from '../query-compiler/compiled-query.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { Compilable } from '../util/compilable.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryId } from '../util/query-id.js' export class AlterTableAddIndexBuilder @@ -224,8 +223,3 @@ export interface AlterTableAddIndexBuilderProps { readonly executor: QueryExecutor readonly node: AlterTableNode } - -preventAwait( - AlterTableAddIndexBuilder, - "don't await AlterTableAddIndexBuilder instances directly. To execute the query you need to call `execute`", -) diff --git a/src/schema/alter-table-builder.ts b/src/schema/alter-table-builder.ts index b7bee0308..034d144be 100644 --- a/src/schema/alter-table-builder.ts +++ b/src/schema/alter-table-builder.ts @@ -8,7 +8,6 @@ import { RenameColumnNode } from '../operation-node/rename-column-node.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' import { freeze, noop } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { ColumnDefinitionBuilder, ColumnDefinitionBuilderCallback, @@ -323,8 +322,6 @@ export interface AlterTableBuilderProps { readonly node: AlterTableNode } -preventAwait(AlterTableBuilder, "don't await AlterTableBuilder instances") - export interface ColumnAlteringInterface { alterColumn( column: string, @@ -470,8 +467,3 @@ export class AlterTableColumnAlteringBuilder export interface AlterTableColumnAlteringBuilderProps extends AlterTableBuilderProps {} - -preventAwait( - AlterTableColumnAlteringBuilder, - "don't await AlterTableColumnAlteringBuilder instances directly. To execute the query you need to call `execute`", -) diff --git a/src/schema/alter-table-drop-constraint-builder.ts b/src/schema/alter-table-drop-constraint-builder.ts index e6b4199be..4e3ec4117 100644 --- a/src/schema/alter-table-drop-constraint-builder.ts +++ b/src/schema/alter-table-drop-constraint-builder.ts @@ -5,7 +5,6 @@ import { CompiledQuery } from '../query-compiler/compiled-query.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { Compilable } from '../util/compilable.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryId } from '../util/query-id.js' export class AlterTableDropConstraintBuilder @@ -91,8 +90,3 @@ export interface AlterTableDropConstraintBuilderProps { readonly executor: QueryExecutor readonly node: AlterTableNode } - -preventAwait( - AlterTableDropConstraintBuilder, - "don't await AlterTableDropConstraintBuilder instances directly. To execute the query you need to call `execute`", -) diff --git a/src/schema/alter-table-executor.ts b/src/schema/alter-table-executor.ts index 08a172eb3..692d447c4 100644 --- a/src/schema/alter-table-executor.ts +++ b/src/schema/alter-table-executor.ts @@ -4,7 +4,6 @@ import { CompiledQuery } from '../query-compiler/compiled-query.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { Compilable } from '../util/compilable.js' import { freeze } from '../util/object-utils.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryId } from '../util/query-id.js' export class AlterTableExecutor implements OperationNodeSource, Compilable { @@ -38,8 +37,3 @@ export interface AlterTableExecutorProps { readonly executor: QueryExecutor readonly node: AlterTableNode } - -preventAwait( - AlterTableExecutor, - "don't await AlterTableExecutor instances directly. To execute the query you need to call `execute`", -) diff --git a/src/schema/column-definition-builder.ts b/src/schema/column-definition-builder.ts index 1935f00f4..4328d99fd 100644 --- a/src/schema/column-definition-builder.ts +++ b/src/schema/column-definition-builder.ts @@ -6,7 +6,6 @@ import { } from '../operation-node/references-node.js' import { SelectAllNode } from '../operation-node/select-all-node.js' import { parseStringReference } from '../parser/reference-parser.js' -import { preventAwait } from '../util/prevent-await.js' import { ColumnDefinitionNode } from '../operation-node/column-definition-node.js' import { DefaultValueExpression, @@ -686,11 +685,6 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { } } -preventAwait( - ColumnDefinitionBuilder, - "don't await ColumnDefinitionBuilder instances directly.", -) - export type ColumnDefinitionBuilderCallback = ( builder: ColumnDefinitionBuilder, ) => ColumnDefinitionBuilder diff --git a/src/schema/create-index-builder.ts b/src/schema/create-index-builder.ts index c2f6eca30..28b215c6f 100644 --- a/src/schema/create-index-builder.ts +++ b/src/schema/create-index-builder.ts @@ -12,7 +12,6 @@ import { import { parseTable } from '../parser/table-parser.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -303,11 +302,6 @@ export class CreateIndexBuilder } } -preventAwait( - CreateIndexBuilder, - "don't await CreateIndexBuilder instances directly. To execute the query you need to call `execute`", -) - export interface CreateIndexBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/create-schema-builder.ts b/src/schema/create-schema-builder.ts index dd5d6998e..071cdcc35 100644 --- a/src/schema/create-schema-builder.ts +++ b/src/schema/create-schema-builder.ts @@ -2,7 +2,6 @@ import { CreateSchemaNode } from '../operation-node/create-schema-node.js' import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -48,11 +47,6 @@ export class CreateSchemaBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - CreateSchemaBuilder, - "don't await CreateSchemaBuilder instances directly. To execute the query you need to call `execute`", -) - export interface CreateSchemaBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/create-table-builder.ts b/src/schema/create-table-builder.ts index d83068c35..808b49aa2 100644 --- a/src/schema/create-table-builder.ts +++ b/src/schema/create-table-builder.ts @@ -6,7 +6,6 @@ import { import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { ColumnDefinitionBuilder } from './column-definition-builder.js' import { QueryId } from '../util/query-id.js' @@ -510,11 +509,6 @@ export class CreateTableBuilder } } -preventAwait( - CreateTableBuilder, - "don't await CreateTableBuilder instances directly. To execute the query you need to call `execute`", -) - export interface CreateTableBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/create-type-builder.ts b/src/schema/create-type-builder.ts index 78b9a3e6e..b9f079b4a 100644 --- a/src/schema/create-type-builder.ts +++ b/src/schema/create-type-builder.ts @@ -1,7 +1,6 @@ import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -57,11 +56,6 @@ export class CreateTypeBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - CreateTypeBuilder, - "don't await CreateTypeBuilder instances directly. To execute the query you need to call `execute`", -) - export interface CreateTypeBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/create-view-builder.ts b/src/schema/create-view-builder.ts index 50ff2e428..2542c09f6 100644 --- a/src/schema/create-view-builder.ts +++ b/src/schema/create-view-builder.ts @@ -1,7 +1,6 @@ import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -122,11 +121,6 @@ export class CreateViewBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - CreateViewBuilder, - "don't await CreateViewBuilder instances directly. To execute the query you need to call `execute`", -) - export interface CreateViewBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/drop-index-builder.ts b/src/schema/drop-index-builder.ts index 396b09ec9..4fabba208 100644 --- a/src/schema/drop-index-builder.ts +++ b/src/schema/drop-index-builder.ts @@ -2,7 +2,6 @@ import { DropIndexNode } from '../operation-node/drop-index-node.js' import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { parseTable } from '../parser/table-parser.js' @@ -73,11 +72,6 @@ export class DropIndexBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - DropIndexBuilder, - "don't await DropIndexBuilder instances directly. To execute the query you need to call `execute`", -) - export interface DropIndexBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/drop-schema-builder.ts b/src/schema/drop-schema-builder.ts index fc248e219..98001f0c6 100644 --- a/src/schema/drop-schema-builder.ts +++ b/src/schema/drop-schema-builder.ts @@ -2,7 +2,6 @@ import { DropSchemaNode } from '../operation-node/drop-schema-node.js' import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -59,11 +58,6 @@ export class DropSchemaBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - DropSchemaBuilder, - "don't await DropSchemaBuilder instances directly. To execute the query you need to call `execute`", -) - export interface DropSchemaBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/drop-table-builder.ts b/src/schema/drop-table-builder.ts index 120dece01..f5cf168e9 100644 --- a/src/schema/drop-table-builder.ts +++ b/src/schema/drop-table-builder.ts @@ -2,7 +2,6 @@ import { DropTableNode } from '../operation-node/drop-table-node.js' import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -59,11 +58,6 @@ export class DropTableBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - DropTableBuilder, - "don't await DropTableBuilder instances directly. To execute the query you need to call `execute`", -) - export interface DropTableBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/drop-type-builder.ts b/src/schema/drop-type-builder.ts index e783c37f6..c94662069 100644 --- a/src/schema/drop-type-builder.ts +++ b/src/schema/drop-type-builder.ts @@ -2,7 +2,6 @@ import { DropTypeNode } from '../operation-node/drop-type-node.js' import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -50,11 +49,6 @@ export class DropTypeBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - DropTypeBuilder, - "don't await DropTypeBuilder instances directly. To execute the query you need to call `execute`", -) - export interface DropTypeBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/drop-view-builder.ts b/src/schema/drop-view-builder.ts index 33634cad9..a7bd6e122 100644 --- a/src/schema/drop-view-builder.ts +++ b/src/schema/drop-view-builder.ts @@ -1,7 +1,6 @@ import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' @@ -68,11 +67,6 @@ export class DropViewBuilder implements OperationNodeSource, Compilable { } } -preventAwait( - DropViewBuilder, - "don't await DropViewBuilder instances directly. To execute the query you need to call `execute`", -) - export interface DropViewBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/foreign-key-constraint-builder.ts b/src/schema/foreign-key-constraint-builder.ts index 5a7e45fa5..56990cacf 100644 --- a/src/schema/foreign-key-constraint-builder.ts +++ b/src/schema/foreign-key-constraint-builder.ts @@ -2,7 +2,6 @@ import { ForeignKeyConstraintNode } from '../operation-node/foreign-key-constrai import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { OnModifyForeignAction } from '../operation-node/references-node.js' import { parseOnModifyForeignAction } from '../parser/on-modify-action-parser.js' -import { preventAwait } from '../util/prevent-await.js' export interface ForeignKeyConstraintBuilderInterface { onDelete(onDelete: OnModifyForeignAction): R @@ -48,8 +47,3 @@ export class ForeignKeyConstraintBuilder return this.#node } } - -preventAwait( - ForeignKeyConstraintBuilder, - "don't await ForeignKeyConstraintBuilder instances directly.", -) diff --git a/src/schema/refresh-materialized-view-builder.ts b/src/schema/refresh-materialized-view-builder.ts index 6691dd120..75787f1a7 100644 --- a/src/schema/refresh-materialized-view-builder.ts +++ b/src/schema/refresh-materialized-view-builder.ts @@ -1,13 +1,14 @@ import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { CompiledQuery } from '../query-compiler/compiled-query.js' import { Compilable } from '../util/compilable.js' -import { preventAwait } from '../util/prevent-await.js' import { QueryExecutor } from '../query-executor/query-executor.js' import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' -export class RefreshMaterializedViewBuilder implements OperationNodeSource, Compilable { +export class RefreshMaterializedViewBuilder + implements OperationNodeSource, Compilable +{ readonly #props: RefreshMaterializedViewBuilderProps constructor(props: RefreshMaterializedViewBuilderProps) { @@ -16,9 +17,9 @@ export class RefreshMaterializedViewBuilder implements OperationNodeSource, Comp /** * Adds the "concurrently" modifier. - * + * * Use this to refresh the view without locking out concurrent selects on the materialized view. - * + * * WARNING! * This cannot be used with the "with no data" modifier. */ @@ -34,7 +35,7 @@ export class RefreshMaterializedViewBuilder implements OperationNodeSource, Comp /** * Adds the "with data" modifier. - * + * * If specified (or defaults) the backing query is executed to provide the new data, and the materialized view is left in a scannable state */ withData(): RefreshMaterializedViewBuilder { @@ -48,9 +49,9 @@ export class RefreshMaterializedViewBuilder implements OperationNodeSource, Comp /** * Adds the "with no data" modifier. - * + * * If specified, no new data is generated and the materialized view is left in an unscannable state. - * + * * WARNING! * This cannot be used with the "concurrently" modifier. */ @@ -91,11 +92,6 @@ export class RefreshMaterializedViewBuilder implements OperationNodeSource, Comp } } -preventAwait( - RefreshMaterializedViewBuilder, - "don't await RefreshMaterializedViewBuilder instances directly. To execute the query you need to call `execute`", -) - export interface RefreshMaterializedViewBuilderProps { readonly queryId: QueryId readonly executor: QueryExecutor diff --git a/src/schema/schema.ts b/src/schema/schema.ts index 1f8fe414d..07cbdeb4b 100644 --- a/src/schema/schema.ts +++ b/src/schema/schema.ts @@ -238,9 +238,9 @@ export class SchemaModule { /** * Refresh a materialized view. - * + * * ### Examples - * + * * ```ts * await db.schema * .refreshMaterializedView('my_view') diff --git a/src/schema/unique-constraint-builder.ts b/src/schema/unique-constraint-builder.ts index 324263098..4e1e0bcb1 100644 --- a/src/schema/unique-constraint-builder.ts +++ b/src/schema/unique-constraint-builder.ts @@ -1,6 +1,5 @@ import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { UniqueConstraintNode } from '../operation-node/unique-constraint-node.js' -import { preventAwait } from '../util/prevent-await.js' export class UniqueConstraintNodeBuilder implements OperationNodeSource { readonly #node: UniqueConstraintNode @@ -25,11 +24,6 @@ export class UniqueConstraintNodeBuilder implements OperationNodeSource { } } -preventAwait( - UniqueConstraintNodeBuilder, - "don't await UniqueConstraintNodeBuilder instances directly.", -) - export type UniqueConstraintNodeBuilderCallback = ( builder: UniqueConstraintNodeBuilder, ) => UniqueConstraintNodeBuilder diff --git a/src/util/prevent-await.ts b/src/util/prevent-await.ts deleted file mode 100644 index 7a6089dda..000000000 --- a/src/util/prevent-await.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function preventAwait(clazz: Function, message: string): void { - Object.defineProperties(clazz.prototype, { - then: { - enumerable: false, - value: () => { - throw new Error(message) - }, - }, - }) -} diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index 3816e4084..850f199de 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -1968,9 +1968,9 @@ for (const dialect of DIALECTS) { if (dialect === 'postgres') { it('should refresh a materialized view', async () => { - const builder = ctx.db.schema - .refreshMaterializedView('materialized_dogs') - + const builder = + ctx.db.schema.refreshMaterializedView('materialized_dogs') + testSql(builder, dialect, { postgres: { sql: `refresh materialized view "materialized_dogs" with data`, From f7f6064e9150defa0000127e92d99de11bbf9d94 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sat, 5 Oct 2024 18:55:29 +0300 Subject: [PATCH 07/28] ci: run 22.x --- .github/workflows/test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 405a26f89..4a0a17600 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x, 22.4.1] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v4 @@ -44,7 +44,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x, 22.4.1] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v4 @@ -79,7 +79,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.4.1 + node-version: 22.x cache: 'npm' - name: Use Deno ${{ matrix.deno-version }} @@ -114,7 +114,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.4.1 + node-version: 22.x cache: 'npm' - name: Install dependencies @@ -141,7 +141,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.4.1 + node-version: 22.x cache: 'npm' - name: Install dependencies @@ -163,7 +163,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22.4.1 + node-version: 22.x cache: 'npm' - name: Install dependencies From 35312020feadaf94b2ded5d84d6254e7a005f100 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sat, 5 Oct 2024 20:12:23 +0300 Subject: [PATCH 08/28] add `ControlledTransaction`. (#962) Co-authored-by: Igal Klebanov --- scripts/copy-interface-documentation.js | 6 +- src/dialect/mssql/mssql-dialect-config.ts | 26 +- src/dialect/mssql/mssql-driver.ts | 27 +- src/dialect/mysql/mysql-driver.ts | 32 + src/dialect/postgres/postgres-driver.ts | 37 ++ src/dialect/sqlite/sqlite-driver.ts | 32 + src/driver/driver.ts | 28 + src/driver/dummy-driver.ts | 12 + src/driver/runtime-driver.ts | 49 ++ src/kysely.ts | 446 +++++++++++++ src/parser/savepoint-parser.ts | 30 + src/query-executor/query-executor-base.ts | 15 +- src/util/provide-controlled-connection.ts | 27 + test/node/src/controlled-transaction.test.ts | 629 +++++++++++++++++++ 14 files changed, 1369 insertions(+), 27 deletions(-) create mode 100644 src/parser/savepoint-parser.ts create mode 100644 src/util/provide-controlled-connection.ts create mode 100644 test/node/src/controlled-transaction.test.ts diff --git a/scripts/copy-interface-documentation.js b/scripts/copy-interface-documentation.js index 56a8a0df4..735886870 100644 --- a/scripts/copy-interface-documentation.js +++ b/scripts/copy-interface-documentation.js @@ -22,7 +22,7 @@ const OBJECT_REGEXES = [ /^(?:export )?declare (?:abstract )?class (\w+)/, /^(?:export )?interface (\w+)/, ] -const GENERIC_ARGUMENTS_REGEX = /<[\w"'`,{}=| ]+>/g +const GENERIC_ARGUMENTS_REGEX = /<[\w"'`,{}=|\[\] ]+>/g const JSDOC_START_REGEX = /^\s+\/\*\*/ const JSDOC_END_REGEX = /^\s+\*\// @@ -123,7 +123,7 @@ function parseObjects(file) { function parseImplements(line) { if (!line.endsWith('{')) { console.warn( - `skipping object declaration "${line}". Expected it to end with "{"'` + `skipping object declaration "${line}". Expected it to end with "{"'`, ) return [] } @@ -225,7 +225,7 @@ function findDocProperty(files, object, propertyName) { } const interfaceProperty = interfaceObject.properties.find( - (it) => it.name === propertyName + (it) => it.name === propertyName, ) if (interfaceProperty?.doc) { diff --git a/src/dialect/mssql/mssql-dialect-config.ts b/src/dialect/mssql/mssql-dialect-config.ts index 8e8e2b588..7526aed3f 100644 --- a/src/dialect/mssql/mssql-dialect-config.ts +++ b/src/dialect/mssql/mssql-dialect-config.ts @@ -73,17 +73,20 @@ export interface Tedious { export interface TediousConnection { beginTransaction( - callback: (error?: Error | null, transactionDescriptor?: any) => void, - name?: string, - isolationLevel?: number, + callback: ( + err: Error | null | undefined, + transactionDescriptor?: any, + ) => void, + name?: string | undefined, + isolationLevel?: number | undefined, ): void cancel(): boolean close(): void commitTransaction( - callback: (error?: Error | null) => void, - name?: string, + callback: (err: Error | null | undefined) => void, + name?: string | undefined, ): void - connect(callback?: (error?: Error) => void): void + connect(connectListener: (err?: Error) => void): void execSql(request: TediousRequest): void off(event: 'error', listener: (error: unknown) => void): this off(event: string, listener: (...args: any[]) => void): this @@ -91,12 +94,15 @@ export interface TediousConnection { on(event: string, listener: (...args: any[]) => void): this once(event: 'end', listener: () => void): this once(event: string, listener: (...args: any[]) => void): this - reset(callback: (error?: Error | null) => void): void + reset(callback: (err: Error | null | undefined) => void): void rollbackTransaction( - callback: (error?: Error | null) => void, - name?: string, + callback: (err: Error | null | undefined) => void, + name?: string | undefined, + ): void + saveTransaction( + callback: (err: Error | null | undefined) => void, + name: string, ): void - saveTransaction(callback: (error?: Error | null) => void, name: string): void } export type TediousIsolationLevel = Record diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index b9d44e203..8ba60841e 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -86,6 +86,20 @@ export class MssqlDriver implements Driver { await connection.rollbackTransaction() } + async savepoint( + connection: MssqlConnection, + savepointName: string, + ): Promise { + await connection.savepoint(savepointName) + } + + async rollbackToSavepoint( + connection: MssqlConnection, + savepointName: string, + ): Promise { + await connection.rollbackTransaction(savepointName) + } + async releaseConnection(connection: MssqlConnection): Promise { await connection[PRIVATE_RELEASE_METHOD]() this.#pool.release(connection) @@ -174,12 +188,21 @@ class MssqlConnection implements DatabaseConnection { } } - async rollbackTransaction(): Promise { + async rollbackTransaction(savepointName?: string): Promise { await new Promise((resolve, reject) => this.#connection.rollbackTransaction((error) => { if (error) reject(error) else resolve(undefined) - }), + }, savepointName), + ) + } + + async savepoint(savepointName: string): Promise { + await new Promise((resolve, reject) => + this.#connection.saveTransaction((error) => { + if (error) reject(error) + else resolve(undefined) + }, savepointName), ) } diff --git a/src/dialect/mysql/mysql-driver.ts b/src/dialect/mysql/mysql-driver.ts index 063d84ae0..084b6cb6f 100644 --- a/src/dialect/mysql/mysql-driver.ts +++ b/src/dialect/mysql/mysql-driver.ts @@ -3,7 +3,9 @@ import { QueryResult, } from '../../driver/database-connection.js' import { Driver, TransactionSettings } from '../../driver/driver.js' +import { parseSavepointCommand } from '../../parser/savepoint-parser.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' +import { QueryCompiler } from '../../query-compiler/query-compiler.js' import { isFunction, isObject, freeze } from '../../util/object-utils.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' import { @@ -90,6 +92,36 @@ export class MysqlDriver implements Driver { await connection.executeQuery(CompiledQuery.raw('rollback')) } + async savepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('savepoint', savepointName)), + ) + } + + async rollbackToSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('rollback to', savepointName)), + ) + } + + async releaseSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('release savepoint', savepointName)), + ) + } + async releaseConnection(connection: MysqlConnection): Promise { connection[PRIVATE_RELEASE_METHOD]() } diff --git a/src/dialect/postgres/postgres-driver.ts b/src/dialect/postgres/postgres-driver.ts index 7b6639fe2..0321e548b 100644 --- a/src/dialect/postgres/postgres-driver.ts +++ b/src/dialect/postgres/postgres-driver.ts @@ -3,7 +3,14 @@ import { QueryResult, } from '../../driver/database-connection.js' import { Driver, TransactionSettings } from '../../driver/driver.js' +import { IdentifierNode } from '../../operation-node/identifier-node.js' +import { RawNode } from '../../operation-node/raw-node.js' +import { parseSavepointCommand } from '../../parser/savepoint-parser.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' +import { + QueryCompiler, + RootOperationNode, +} from '../../query-compiler/query-compiler.js' import { isFunction, freeze } from '../../util/object-utils.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' import { @@ -78,6 +85,36 @@ export class PostgresDriver implements Driver { await connection.executeQuery(CompiledQuery.raw('rollback')) } + async savepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('savepoint', savepointName)), + ) + } + + async rollbackToSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('rollback to', savepointName)), + ) + } + + async releaseSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('release', savepointName)), + ) + } + async releaseConnection(connection: PostgresConnection): Promise { connection[PRIVATE_RELEASE_METHOD]() } diff --git a/src/dialect/sqlite/sqlite-driver.ts b/src/dialect/sqlite/sqlite-driver.ts index dcfbca2e6..5aefb32ef 100644 --- a/src/dialect/sqlite/sqlite-driver.ts +++ b/src/dialect/sqlite/sqlite-driver.ts @@ -4,7 +4,9 @@ import { } from '../../driver/database-connection.js' import { Driver } from '../../driver/driver.js' import { SelectQueryNode } from '../../operation-node/select-query-node.js' +import { parseSavepointCommand } from '../../parser/savepoint-parser.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' +import { QueryCompiler } from '../../query-compiler/query-compiler.js' import { freeze, isFunction } from '../../util/object-utils.js' import { SqliteDatabase, SqliteDialectConfig } from './sqlite-dialect-config.js' @@ -50,6 +52,36 @@ export class SqliteDriver implements Driver { await connection.executeQuery(CompiledQuery.raw('rollback')) } + async savepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('savepoint', savepointName)), + ) + } + + async rollbackToSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('rollback to', savepointName)), + ) + } + + async releaseSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + await connection.executeQuery( + compileQuery(parseSavepointCommand('release', savepointName)), + ) + } + async releaseConnection(): Promise { this.#connectionMutex.unlock() } diff --git a/src/driver/driver.ts b/src/driver/driver.ts index ef9214d17..849cd0129 100644 --- a/src/driver/driver.ts +++ b/src/driver/driver.ts @@ -1,3 +1,4 @@ +import { QueryCompiler } from '../query-compiler/query-compiler.js' import { ArrayItemType } from '../util/type-utils.js' import { DatabaseConnection } from './database-connection.js' @@ -37,6 +38,33 @@ export interface Driver { */ rollbackTransaction(connection: DatabaseConnection): Promise + /** + * Establishses a new savepoint within a transaction. + */ + savepoint?( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise + + /** + * Rolls back to a savepoint within a transaction. + */ + rollbackToSavepoint?( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise + + /** + * Releases a savepoint within a transaction. + */ + releaseSavepoint?( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise + /** * Releases a connection back to the pool. */ diff --git a/src/driver/dummy-driver.ts b/src/driver/dummy-driver.ts index e86cd29d4..1e846825f 100644 --- a/src/driver/dummy-driver.ts +++ b/src/driver/dummy-driver.ts @@ -66,6 +66,18 @@ export class DummyDriver implements Driver { async destroy(): Promise { // Nothing to do here. } + + async releaseSavepoint(): Promise { + // Nothing to do here. + } + + async rollbackToSavepoint(): Promise { + // Nothing to do here. + } + + async savepoint(): Promise { + // Nothing to do here. + } } class DummyConnection implements DatabaseConnection { diff --git a/src/driver/runtime-driver.ts b/src/driver/runtime-driver.ts index a7ba8d771..d05467ccf 100644 --- a/src/driver/runtime-driver.ts +++ b/src/driver/runtime-driver.ts @@ -1,4 +1,5 @@ import { CompiledQuery } from '../query-compiler/compiled-query.js' +import { QueryCompiler } from '../query-compiler/query-compiler.js' import { Log } from '../util/log.js' import { performanceNow } from '../util/performance-now.js' import { DatabaseConnection, QueryResult } from './database-connection.js' @@ -85,6 +86,54 @@ export class RuntimeDriver implements Driver { return this.#driver.rollbackTransaction(connection) } + savepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + if (this.#driver.savepoint) { + return this.#driver.savepoint(connection, savepointName, compileQuery) + } + + throw new Error('The `savepoint` method is not supported by this driver') + } + + rollbackToSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + if (this.#driver.rollbackToSavepoint) { + return this.#driver.rollbackToSavepoint( + connection, + savepointName, + compileQuery, + ) + } + + throw new Error( + 'The `rollbackToSavepoint` method is not supported by this driver', + ) + } + + releaseSavepoint( + connection: DatabaseConnection, + savepointName: string, + compileQuery: QueryCompiler['compileQuery'], + ): Promise { + if (this.#driver.releaseSavepoint) { + return this.#driver.releaseSavepoint( + connection, + savepointName, + compileQuery, + ) + } + + throw new Error( + 'The `releaseSavepoint` method is not supported by this driver', + ) + } + async destroy(): Promise { if (!this.#initPromise) { return diff --git a/src/kysely.ts b/src/kysely.ts index e76505300..6bd0f755f 100644 --- a/src/kysely.ts +++ b/src/kysely.ts @@ -32,6 +32,15 @@ import { parseExpression } from './parser/expression-parser.js' import { Expression } from './expression/expression.js' import { WithSchemaPlugin } from './plugin/with-schema/with-schema-plugin.js' import { DrainOuterGeneric } from './util/type-utils.js' +import { QueryCompiler } from './query-compiler/query-compiler.js' +import { + ReleaseSavepoint, + RollbackToSavepoint, +} from './parser/savepoint-parser.js' +import { + ControlledConnection, + provideControlledConnection, +} from './util/provide-controlled-connection.js' /** * The main Kysely class. @@ -222,6 +231,9 @@ export class Kysely * of type {@link Transaction} which inherits {@link Kysely}. Any query * started through the transaction object is executed inside the transaction. * + * To run a controlled transaction, allowing you to commit and rollback manually, + * use {@link startTransaction} instead. + * * ### Examples * * @@ -277,6 +289,122 @@ export class Kysely return new TransactionBuilder({ ...this.#props }) } + /** + * Creates a {@link ControlledTransactionBuilder} that can be used to run queries inside a controlled transaction. + * + * The returned {@link ControlledTransactionBuilder} can be used to configure the transaction. + * The {@link ControlledTransactionBuilder.execute} method can then be called + * to start the transaction and return a {@link ControlledTransaction}. + * + * A {@link ControlledTransaction} allows you to commit and rollback manually, + * execute savepoint commands. It extends {@link Transaction} which extends {@link Kysely}, + * so you can run queries inside the transaction. Once the transaction is committed, + * or rolled back, it can't be used anymore - all queries will throw an error. + * This is to prevent accidentally running queries outside the transaction - where + * atomicity is not guaranteed anymore. + * + * ### Examples + * + * + * + * A controlled transaction allows you to commit and rollback manually, execute + * savepoint commands, and queries in general. + * + * In this example we start a transaction, use it to insert two rows and then commit + * the transaction. If an error is thrown, we catch it and rollback the transaction. + * + * ```ts + * const trx = await db.startTransaction().execute() + * + * try { + * const jennifer = await trx.insertInto('person') + * .values({ + * first_name: 'Jennifer', + * last_name: 'Aniston', + * age: 40, + * }) + * .returning('id') + * .executeTakeFirstOrThrow() + * + * const catto = await trx.insertInto('pet') + * .values({ + * owner_id: jennifer.id, + * name: 'Catto', + * species: 'cat', + * is_favorite: false, + * }) + * .returningAll() + * .executeTakeFirstOrThrow() + * + * await trx.commit().execute() + * + * return catto + * } catch (error) { + * await trx.rollback().execute() + * } + * ``` + * + * + * + * A controlled transaction allows you to commit and rollback manually, execute + * savepoint commands, and queries in general. + * + * In this example we start a transaction, insert a person, create a savepoint, + * try inserting a toy and a pet, and if an error is thrown, we rollback to the + * savepoint. Eventually we release the savepoint, insert an audit record and + * commit the transaction. If an error is thrown, we catch it and rollback the + * transaction. + * + * ```ts + * const trx = await db.startTransaction().execute() + * + * try { + * const jennifer = await trx + * .insertInto('person') + * .values({ + * first_name: 'Jennifer', + * last_name: 'Aniston', + * age: 40, + * }) + * .returning('id') + * .executeTakeFirstOrThrow() + * + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * const bone = await trxAfterJennifer + * .insertInto('toy') + * .values({ name: 'Bone', price: 1.99 }) + * .returning('id') + * .executeTakeFirstOrThrow() + * + * await trxAfterJennifer + * .insertInto('pet') + * .values({ + * owner_id: jennifer.id, + * name: 'Catto', + * species: 'cat', + * favorite_toy_id: bone.id, + * }) + * .execute() + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * + * await trxAfterJennifer.releaseSavepoint('after_jennifer').execute() + * + * await trx.insertInto('audit').values({ action: 'added Jennifer' }).execute() + * + * await trx.commit().execute() + * } catch (error) { + * await trx.rollback().execute() + * } + * ``` + */ + startTransaction(): ControlledTransactionBuilder { + return new ControlledTransactionBuilder({ ...this.#props }) + } + /** * Provides a kysely instance bound to a single database connection. * @@ -623,3 +751,321 @@ function validateTransactionSettings(settings: TransactionSettings): void { ) } } + +export class ControlledTransactionBuilder { + readonly #props: ControlledTransactionBuilderProps + + constructor(props: ControlledTransactionBuilderProps) { + this.#props = freeze(props) + } + + setIsolationLevel( + isolationLevel: IsolationLevel, + ): ControlledTransactionBuilder { + return new ControlledTransactionBuilder({ + ...this.#props, + isolationLevel, + }) + } + + async execute(): Promise> { + const { isolationLevel, ...props } = this.#props + const settings = { isolationLevel } + + validateTransactionSettings(settings) + + const connection = await provideControlledConnection(this.#props.executor) + + await this.#props.driver.beginTransaction(connection, settings) + + return new ControlledTransaction({ + ...props, + connection, + executor: this.#props.executor.withConnectionProvider( + new SingleConnectionProvider(connection), + ), + }) + } +} + +interface ControlledTransactionBuilderProps extends TransactionBuilderProps { + readonly releaseConnection?: boolean +} + +export class ControlledTransaction< + DB, + S extends string[] = [], +> extends Transaction { + readonly #props: ControlledTransactionProps + readonly #compileQuery: QueryCompiler['compileQuery'] + #isCommitted: boolean + #isRolledBack: boolean + + constructor(props: ControlledTransactionProps) { + const { connection, ...transactionProps } = props + super(transactionProps) + this.#props = freeze(props) + + const queryId = createQueryId() + this.#compileQuery = (node) => props.executor.compileQuery(node, queryId) + + this.#isCommitted = false + this.#isRolledBack = false + + this.#assertNotCommittedOrRolledBackBeforeAllExecutions() + } + + get isCommitted(): boolean { + return this.#isCommitted + } + + get isRolledBack(): boolean { + return this.#isRolledBack + } + + /** + * Commits the transaction. + * + * See {@link rollback}. + * + * ### Examples + * + * ```ts + * try { + * await doSomething(trx) + * + * await trx.commit().execute() + * } catch (error) { + * await trx.rollback().execute() + * } + * ``` + */ + commit(): Command { + this.#assertNotCommittedOrRolledBack() + + return new Command(async () => { + await this.#props.driver.commitTransaction(this.#props.connection) + this.#isCommitted = true + this.#props.connection.release() + }) + } + + /** + * Rolls back the transaction. + * + * See {@link commit} and {@link rollbackToSavepoint}. + * + * ### Examples + * + * ```ts + * try { + * await doSomething(trx) + * + * await trx.commit().execute() + * } catch (error) { + * await trx.rollback().execute() + * } + * ``` + */ + rollback(): Command { + this.#assertNotCommittedOrRolledBack() + + return new Command(async () => { + await this.#props.driver.rollbackTransaction(this.#props.connection) + this.#isRolledBack = true + this.#props.connection.release() + }) + } + + /** + * Creates a savepoint with a given name. + * + * See {@link rollbackToSavepoint} and {@link releaseSavepoint}. + * + * For a type-safe experience, you should use the returned instance from now on. + * + * ### Examples + * + * ```ts + * await insertJennifer(trx) + * + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * await doSomething(trxAfterJennifer) + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * ``` + */ + savepoint( + savepointName: SN extends S ? never : SN, + ): Command> { + this.#assertNotCommittedOrRolledBack() + + return new Command(async () => { + await this.#props.driver.savepoint?.( + this.#props.connection, + savepointName, + this.#compileQuery, + ) + + return new ControlledTransaction({ ...this.#props }) + }) + } + + /** + * Rolls back to a savepoint with a given name. + * + * See {@link savepoint} and {@link releaseSavepoint}. + * + * You must use the same instance returned by {@link savepoint}, or + * escape the type-check by using `as any`. + * + * ### Examples + * + * ```ts + * await insertJennifer(trx) + * + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * await doSomething(trxAfterJennifer) + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * ``` + */ + rollbackToSavepoint( + savepointName: SN, + ): Command>> { + this.#assertNotCommittedOrRolledBack() + + return new Command(async () => { + await this.#props.driver.rollbackToSavepoint?.( + this.#props.connection, + savepointName, + this.#compileQuery, + ) + + return new ControlledTransaction({ ...this.#props }) + }) + } + + /** + * Releases a savepoint with a given name. + * + * See {@link savepoint} and {@link rollbackToSavepoint}. + * + * You must use the same instance returned by {@link savepoint}, or + * escape the type-check by using `as any`. + * + * ### Examples + * + * ```ts + * await insertJennifer(trx) + * + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * await doSomething(trxAfterJennifer) + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * + * await trxAfterJennifer.releaseSavepoint('after_jennifer').execute() + * + * await doSomethingElse(trx) + * ``` + */ + releaseSavepoint( + savepointName: SN, + ): Command>> { + this.#assertNotCommittedOrRolledBack() + + return new Command(async () => { + await this.#props.driver.releaseSavepoint?.( + this.#props.connection, + savepointName, + this.#compileQuery, + ) + + return new ControlledTransaction({ ...this.#props }) + }) + } + + override withPlugin(plugin: KyselyPlugin): ControlledTransaction { + return new ControlledTransaction({ + ...this.#props, + executor: this.#props.executor.withPlugin(plugin), + }) + } + + override withoutPlugins(): ControlledTransaction { + return new ControlledTransaction({ + ...this.#props, + executor: this.#props.executor.withoutPlugins(), + }) + } + + override withSchema(schema: string): ControlledTransaction { + return new ControlledTransaction({ + ...this.#props, + executor: this.#props.executor.withPluginAtFront( + new WithSchemaPlugin(schema), + ), + }) + } + + override withTables< + T extends Record>, + >(): ControlledTransaction, S> { + return new ControlledTransaction({ ...this.#props }) + } + + #assertNotCommittedOrRolledBackBeforeAllExecutions() { + const { executor } = this.#props + + const originalExecuteQuery = executor.executeQuery.bind(executor) + executor.executeQuery = async (query, queryId) => { + this.#assertNotCommittedOrRolledBack() + return await originalExecuteQuery(query, queryId) + } + + const that = this + const originalStream = executor.stream.bind(executor) + executor.stream = (query, chunkSize, queryId) => { + that.#assertNotCommittedOrRolledBack() + return originalStream(query, chunkSize, queryId) + } + } + + #assertNotCommittedOrRolledBack(): void { + if (this.isCommitted) { + throw new Error('Transaction is already committed') + } + + if (this.isRolledBack) { + throw new Error('Transaction is already rolled back') + } + } +} + +interface ControlledTransactionProps extends KyselyProps { + readonly connection: ControlledConnection +} + +export class Command { + readonly #cb: () => Promise + + constructor(cb: () => Promise) { + this.#cb = cb + } + + /** + * Executes the command. + */ + async execute(): Promise { + return await this.#cb() + } +} diff --git a/src/parser/savepoint-parser.ts b/src/parser/savepoint-parser.ts new file mode 100644 index 000000000..8163bb935 --- /dev/null +++ b/src/parser/savepoint-parser.ts @@ -0,0 +1,30 @@ +import { IdentifierNode } from '../operation-node/identifier-node.js' +import { RawNode } from '../operation-node/raw-node.js' + +export type RollbackToSavepoint< + S extends string[], + SN extends S[number], +> = S extends [...infer L extends string[], infer R] + ? R extends SN + ? S + : RollbackToSavepoint + : never + +export type ReleaseSavepoint< + S extends string[], + SN extends S[number], +> = S extends [...infer L extends string[], infer R] + ? R extends SN + ? L + : ReleaseSavepoint + : never + +export function parseSavepointCommand( + command: string, + savepointName: string, +): RawNode { + return RawNode.createWithChildren([ + RawNode.createWithSql(`${command} `), + IdentifierNode.create(savepointName), // ensures savepointName gets sanitized + ]) +} diff --git a/src/query-executor/query-executor-base.ts b/src/query-executor/query-executor-base.ts index 8bc6d3db4..5aec6a5f4 100644 --- a/src/query-executor/query-executor-base.ts +++ b/src/query-executor/query-executor-base.ts @@ -12,6 +12,7 @@ import { DialectAdapter } from '../dialect/dialect-adapter.js' import { QueryExecutor } from './query-executor.js' import { Deferred } from '../util/deferred.js' import { logOnce } from '../util/log-once.js' +import { provideControlledConnection } from '../util/provide-controlled-connection.js' const NO_PLUGINS: ReadonlyArray = freeze([]) @@ -80,17 +81,7 @@ export abstract class QueryExecutorBase implements QueryExecutor { chunkSize: number, queryId: QueryId, ): AsyncIterableIterator> { - const connectionDefer = new Deferred() - const connectionReleaseDefer = new Deferred() - - this.provideConnection(async (connection) => { - connectionDefer.resolve(connection) - - // Lets wait until we don't need connection before returning here (returning releases connection) - return await connectionReleaseDefer.promise - }).catch((ex) => connectionDefer.reject(ex)) - - const connection = await connectionDefer.promise + const connection = await provideControlledConnection(this) try { for await (const result of connection.streamQuery( @@ -100,7 +91,7 @@ export abstract class QueryExecutorBase implements QueryExecutor { yield await this.#transformResult(result, queryId) } } finally { - connectionReleaseDefer.resolve() + connection.release() } } diff --git a/src/util/provide-controlled-connection.ts b/src/util/provide-controlled-connection.ts new file mode 100644 index 000000000..127712d19 --- /dev/null +++ b/src/util/provide-controlled-connection.ts @@ -0,0 +1,27 @@ +import { ConnectionProvider } from '../driver/connection-provider.js' +import { DatabaseConnection } from '../driver/database-connection.js' +import { Deferred } from './deferred.js' + +export async function provideControlledConnection( + connectionProvider: ConnectionProvider, +): Promise { + const connectionDefer = new Deferred() + const connectionReleaseDefer = new Deferred() + + connectionProvider + .provideConnection(async (connection) => { + connectionDefer.resolve(connection) + + return await connectionReleaseDefer.promise + }) + .catch((ex) => connectionDefer.reject(ex)) + + const connection = (await connectionDefer.promise) as ControlledConnection + connection.release = connectionReleaseDefer.resolve + + return connection +} + +export interface ControlledConnection extends DatabaseConnection { + release(): void +} diff --git a/test/node/src/controlled-transaction.test.ts b/test/node/src/controlled-transaction.test.ts new file mode 100644 index 000000000..5821ef11d --- /dev/null +++ b/test/node/src/controlled-transaction.test.ts @@ -0,0 +1,629 @@ +import * as sinon from 'sinon' +import { Connection } from 'tedious' +import { + CompiledQuery, + ControlledTransaction, + Driver, + DummyDriver, + IsolationLevel, + Kysely, + SqliteDialect, +} from '../../../' +import { + DIALECTS, + Database, + TestContext, + clearDatabase, + destroyTest, + expect, + initTest, + insertDefaultDataSet, + limit, +} from './test-setup.js' + +for (const dialect of DIALECTS) { + describe(`${dialect}: controlled transaction`, () => { + let ctx: TestContext + const executedQueries: CompiledQuery[] = [] + const sandbox = sinon.createSandbox() + let tediousBeginTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > + let tediousCommitTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > + let tediousRollbackTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > + let tediousSaveTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > + + before(async function () { + ctx = await initTest(this, dialect, (event) => { + if (event.level === 'query') { + executedQueries.push(event.query) + } + }) + }) + + beforeEach(async () => { + await insertDefaultDataSet(ctx) + executedQueries.length = 0 + tediousBeginTransactionSpy = sandbox.spy( + Connection.prototype, + 'beginTransaction', + ) + tediousCommitTransactionSpy = sandbox.spy( + Connection.prototype, + 'commitTransaction', + ) + tediousRollbackTransactionSpy = sandbox.spy( + Connection.prototype, + 'rollbackTransaction', + ) + tediousSaveTransactionSpy = sandbox.spy( + Connection.prototype, + 'saveTransaction', + ) + }) + + afterEach(async () => { + await clearDatabase(ctx) + sandbox.restore() + }) + + after(async () => { + await destroyTest(ctx) + }) + + it('should be able to start and commit a transaction', async () => { + const trx = await ctx.db.startTransaction().execute() + + await insertSomething(trx) + + await trx.commit().execute() + + if (dialect == 'postgres') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mysql') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mssql') { + expect(tediousBeginTransactionSpy.calledOnce).to.be.true + expect(tediousBeginTransactionSpy.getCall(0).args[1]).to.be.undefined + expect(tediousBeginTransactionSpy.getCall(0).args[2]).to.be.undefined + + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Foo', 'Barson', 'male'], + }, + ]) + + expect(tediousCommitTransactionSpy.calledOnce).to.be.true + } else { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'commit', parameters: [] }, + ]) + } + }) + + it('should be able to start and rollback a transaction', async () => { + const trx = await ctx.db.startTransaction().execute() + + await insertSomething(trx) + + await trx.rollback().execute() + + if (dialect == 'postgres') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'rollback', parameters: [] }, + ]) + } else if (dialect === 'mysql') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'rollback', parameters: [] }, + ]) + } else if (dialect === 'mssql') { + expect(tediousBeginTransactionSpy.calledOnce).to.be.true + expect(tediousBeginTransactionSpy.getCall(0).args[1]).to.be.undefined + expect(tediousBeginTransactionSpy.getCall(0).args[2]).to.be.undefined + + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Foo', 'Barson', 'male'], + }, + ]) + + expect(tediousRollbackTransactionSpy.calledOnce).to.be.true + } else { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'rollback', parameters: [] }, + ]) + } + + const person = await ctx.db + .selectFrom('person') + .where('first_name', '=', 'Foo') + .select('first_name') + .executeTakeFirst() + + expect(person).to.be.undefined + }) + + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { + for (const isolationLevel of [ + 'read uncommitted', + 'read committed', + 'repeatable read', + 'serializable', + ...(dialect === 'mssql' ? (['snapshot'] as const) : []), + ] satisfies IsolationLevel[]) { + it(`should set the transaction isolation level as "${isolationLevel}"`, async () => { + const trx = await ctx.db + .startTransaction() + .setIsolationLevel(isolationLevel) + .execute() + + await trx + .insertInto('person') + .values({ + first_name: 'Foo', + last_name: 'Barson', + gender: 'male', + }) + .execute() + + await trx.commit().execute() + }) + } + } + + it('should be able to start a transaction with a single connection', async () => { + await ctx.db.connection().execute(async (conn) => { + const trx = await conn.startTransaction().execute() + + await insertSomething(trx) + + await trx.commit().execute() + + await insertSomethingElse(conn) + + const trx2 = await conn.startTransaction().execute() + + await insertSomething(trx2) + + await trx2.rollback().execute() + + await insertSomethingElse(conn) + }) + + const results = await ctx.db + .selectFrom('person') + .select('first_name') + .orderBy('id', 'desc') + .$call(limit(3, dialect)) + .execute() + expect(results).to.eql([ + { first_name: 'Fizz' }, + { first_name: 'Fizz' }, + { first_name: 'Foo' }, + ]) + }) + + it('should be able to savepoint and rollback to savepoint', async () => { + const trx = await ctx.db.startTransaction().execute() + + await insertSomething(trx) + + const trxAfterFoo = await trx.savepoint('foo').execute() + + await insertSomethingElse(trxAfterFoo) + + await trxAfterFoo.rollbackToSavepoint('foo').execute() + + await trxAfterFoo.commit().execute() + + if (dialect == 'postgres') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { sql: 'begin', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'savepoint "foo"', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + { sql: 'rollback to "foo"', parameters: [] }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mysql') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { sql: 'begin', parameters: [] }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'savepoint `foo`', parameters: [] }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + { sql: 'rollback to `foo`', parameters: [] }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mssql') { + expect(tediousBeginTransactionSpy.calledOnce).to.be.true + expect(tediousBeginTransactionSpy.getCall(0).args[1]).to.be.undefined + expect(tediousBeginTransactionSpy.getCall(0).args[2]).to.be.undefined + + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Foo', 'Barson', 'male'], + }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + ]) + + expect(tediousSaveTransactionSpy.calledOnce).to.be.true + expect(tediousSaveTransactionSpy.getCall(0).args[1]).to.equal('foo') + + expect(tediousRollbackTransactionSpy.calledOnce).to.be.true + expect(tediousRollbackTransactionSpy.getCall(0).args[1]).to.equal('foo') + + expect(tediousCommitTransactionSpy.calledOnce).to.be.true + } else { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { sql: 'begin', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'savepoint "foo"', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + { sql: 'rollback to "foo"', parameters: [] }, + { sql: 'commit', parameters: [] }, + ]) + } + + const results = await ctx.db + .selectFrom('person') + .where('first_name', 'in', ['Foo', 'Fizz']) + .select('first_name') + .execute() + + expect(results).to.have.length(1) + expect(results[0].first_name).to.equal('Foo') + }) + + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'sqlite') { + it('should be able to savepoint and release savepoint', async () => { + const trx = await ctx.db.startTransaction().execute() + + await insertSomething(trx) + + const trxAfterFoo = await trx.savepoint('foo').execute() + + await insertSomethingElse(trxAfterFoo) + + await trxAfterFoo.releaseSavepoint('foo').execute() + + await trxAfterFoo.commit().execute() + + if (dialect == 'postgres') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { sql: 'begin', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'savepoint "foo"', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + { sql: 'release "foo"', parameters: [] }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mysql') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { sql: 'begin', parameters: [] }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'savepoint `foo`', parameters: [] }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + { sql: 'release savepoint `foo`', parameters: [] }, + { sql: 'commit', parameters: [] }, + ]) + } else { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })), + ).to.eql([ + { sql: 'begin', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'savepoint "foo"', parameters: [] }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', + parameters: ['Fizz', 'Buzzson', 'female'], + }, + { sql: 'release "foo"', parameters: [] }, + { sql: 'commit', parameters: [] }, + ]) + } + + const results = await ctx.db + .selectFrom('person') + .where('first_name', 'in', ['Foo', 'Fizz']) + .select('first_name') + .orderBy('first_name') + .execute() + + expect(results).to.have.length(2) + expect(results[0].first_name).to.equal('Fizz') + expect(results[1].first_name).to.equal('Foo') + }) + } + + if (dialect === 'mssql') { + it('should throw an error when trying to release a savepoint as it is not supported', async () => { + const trx = await ctx.db.startTransaction().execute() + + await expect( + trx.releaseSavepoint('foo' as never).execute(), + ).to.be.rejectedWith( + 'The `releaseSavepoint` method is not supported by this driver', + ) + + await trx.rollback().execute() + }) + } + + it('should throw an error when trying to execute a query after the transaction has been committed', async () => { + const trx = await ctx.db.startTransaction().execute() + + await insertSomething(trx) + + await trx.commit().execute() + + await expect(insertSomethingElse(trx)).to.be.rejectedWith( + 'Transaction is already committed', + ) + }) + + it('should throw an error when trying to execute a query after the transaction has been rolled back', async () => { + const trx = await ctx.db.startTransaction().execute() + + await insertSomething(trx) + + await trx.rollback().execute() + + await expect(insertSomethingElse(trx)).to.be.rejectedWith( + 'Transaction is already rolled back', + ) + }) + }) +} + +describe('custom dialect: controlled transaction', () => { + const db = new Kysely({ + dialect: new (class extends SqliteDialect { + createDriver(): Driver { + const driver = class extends DummyDriver {} + + // @ts-ignore + driver.prototype.releaseSavepoint = undefined + // @ts-ignore + driver.prototype.rollbackToSavepoint = undefined + // @ts-ignore + driver.prototype.savepoint = undefined + + return new driver() + } + // @ts-ignore + })({}), + }) + let trx: ControlledTransaction + + before(async () => { + trx = await db.startTransaction().execute() + }) + + after(async () => { + await trx.rollback().execute() + }) + + it('should throw an error when trying to savepoint on a dialect that does not support it', async () => { + const trx = await db.startTransaction().execute() + + await expect(trx.savepoint('foo').execute()).to.be.rejectedWith( + 'The `savepoint` method is not supported by this driver', + ) + }) + + it('should throw an error when trying to rollback to a savepoint on a dialect that does not support it', async () => { + const trx = await db.startTransaction().execute() + + await expect( + trx.rollbackToSavepoint('foo' as never).execute(), + ).to.be.rejectedWith( + 'The `rollbackToSavepoint` method is not supported by this driver', + ) + }) + + it('should throw an error when trying to release a savepoint on a dialect that does not support it', async () => { + const trx = await db.startTransaction().execute() + + await expect( + trx.releaseSavepoint('foo' as never).execute(), + ).to.be.rejectedWith( + 'The `releaseSavepoint` method is not supported by this driver', + ) + }) +}) + +async function insertSomething(db: Kysely) { + return await db + .insertInto('person') + .values({ + first_name: 'Foo', + last_name: 'Barson', + gender: 'male', + }) + .executeTakeFirstOrThrow() +} + +async function insertSomethingElse(db: Kysely) { + return await db + .insertInto('person') + .values({ + first_name: 'Fizz', + last_name: 'Buzzson', + gender: 'female', + }) + .executeTakeFirstOrThrow() +} From afb74e27d8cfd3acf8d1dc5c108191605b0ec90a Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sat, 19 Oct 2024 16:32:22 +0300 Subject: [PATCH 09/28] `await using kysely = new Kysely()` support. (#1167) --- src/kysely.ts | 9 +++- test/node/src/async-dispose.test.ts | 65 +++++++++++++++++++++++++++++ test/node/tsconfig.json | 2 + 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/node/src/async-dispose.test.ts diff --git a/src/kysely.ts b/src/kysely.ts index 6bd0f755f..31d815e70 100644 --- a/src/kysely.ts +++ b/src/kysely.ts @@ -42,6 +42,9 @@ import { provideControlledConnection, } from './util/provide-controlled-connection.js' +// @ts-ignore +Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose') + /** * The main Kysely class. * @@ -77,7 +80,7 @@ import { */ export class Kysely extends QueryCreator - implements QueryExecutorProvider + implements QueryExecutorProvider, AsyncDisposable { readonly #props: KyselyProps @@ -536,6 +539,10 @@ export class Kysely return this.getExecutor().executeQuery(compiledQuery, queryId) } + + async [Symbol.asyncDispose]() { + await this.destroy() + } } export class Transaction extends Kysely { diff --git a/test/node/src/async-dispose.test.ts b/test/node/src/async-dispose.test.ts new file mode 100644 index 000000000..72440ba46 --- /dev/null +++ b/test/node/src/async-dispose.test.ts @@ -0,0 +1,65 @@ +import { + CompiledQuery, + DatabaseConnection, + DummyDriver, + Kysely, + PostgresAdapter, + PostgresIntrospector, + PostgresQueryCompiler, + QueryResult, + RootOperationNode, + sql, +} from '../../..' +import { expect } from './test-setup' + +describe('async dispose', function () { + it('should call destroy ', async () => { + const steps: string[] = [] + + { + await using db = new Kysely({ + dialect: { + createAdapter: () => new PostgresAdapter(), + createDriver: () => + new (class extends DummyDriver { + async acquireConnection() { + return new (class implements DatabaseConnection { + async executeQuery(): Promise> { + steps.push('executed') + return { rows: [] } + } + streamQuery(): AsyncIterableIterator> { + throw new Error('Method not implemented.') + } + })() + } + async destroy(): Promise { + steps.push('destroyed') + } + })(), + createIntrospector: (db) => new PostgresIntrospector(db), + createQueryCompiler: () => + new (class extends PostgresQueryCompiler { + compileQuery(node: RootOperationNode): CompiledQuery { + const compiled = super.compileQuery(node) + steps.push('compiled') + return compiled + } + })(), + }, + }) + + await sql`select 1`.execute(db) + } + + steps.push('after runScope') + + expect(steps).to.length.to.be.greaterThan(1) + expect(steps).to.deep.equal([ + 'compiled', + 'executed', + 'destroyed', + 'after runScope', + ]) + }) +}) diff --git a/test/node/tsconfig.json b/test/node/tsconfig.json index 83e6196bb..1ed9e5376 100644 --- a/test/node/tsconfig.json +++ b/test/node/tsconfig.json @@ -2,6 +2,8 @@ "extends": "../../tsconfig-base.json", "include": ["src/**/*"], "compilerOptions": { + "target": "ES2022", + "lib": ["ESNext"], "module": "CommonJS", "outDir": "dist", "skipLibCheck": true From 223c309e66694689b503a61c61108006cf56eac6 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Thu, 24 Oct 2024 17:28:12 +0300 Subject: [PATCH 10/28] fix TS backwards compat following `ControlledTransaction`. (#1193) --- .github/workflows/test.yml | 3 + package-lock.json | 111 ++++++++++-------- package.json | 4 + ...xclude-test-files-for-backwards-compat.mts | 22 ++++ src/kysely.ts | 12 +- src/parser/savepoint-parser.ts | 8 +- test/node/src/async-dispose.test.ts | 1 + test/node/src/controlled-transaction.test.ts | 2 +- test/node/tsconfig.json | 12 +- 9 files changed, 103 insertions(+), 72 deletions(-) create mode 100644 scripts/exclude-test-files-for-backwards-compat.mts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a0a17600..0bc02bbf5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -211,6 +211,9 @@ jobs: - name: Install older TypeScript run: npm i -D typescript@${{ matrix.typescript-version }} tsd@${{ fromJson('{ "^4.6":"0.20.0", "^4.7":"0.22.0", "^4.8":"0.24.1", "^4.9":"0.27.0", "^5.0":"0.28.1", "^5.2":"0.29.0", "^5.3":"0.30.7", "^5.4":"0.31.2" }')[matrix.typescript-version] }} + - name: Exclude non-backward compatible tests + run: npx tsx ./scripts/exclude-test-files-for-backwards-compat.mts + - name: Run tests with older TypeScript run: npm run test:typings && npm run test:node:build diff --git a/package-lock.json b/package-lock.json index dc3a67a66..6267dc398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@types/node": "^22.5.0", "@types/pg": "^8.11.6", "@types/pg-cursor": "^2.7.2", + "@types/semver": "^7.5.8", "@types/sinon": "^17.0.2", "@types/tedious": "^4.0.9", "better-sqlite3": "^11.2.1", @@ -28,15 +29,18 @@ "lodash": "^4.17.21", "mocha": "^10.7.3", "mysql2": "^3.11.0", + "pathe": "^1.1.2", "pg": "^8.12.0", "pg-cursor": "^2.11.0", "pkg-pr-new": "^0.0.30", "playwright": "^1.46.1", "prettier": "^3.3.3", + "semver": "^7.6.3", "sinon": "^19.0.2", "tarn": "^3.0.2", "tedious": "^19.0.0", "tsd": "^0.31.1", + "tsx": "^4.19.1", "typescript": "^5.6.3" }, "engines": { @@ -1531,6 +1535,12 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/sinon": { "version": "17.0.3", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", @@ -2529,20 +2539,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2579,6 +2575,18 @@ "node": "*" } }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -4048,20 +4056,6 @@ "node": ">=18" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/plur": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", @@ -4320,9 +4314,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/read-pkg": { @@ -4519,6 +4513,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4579,13 +4582,10 @@ "dev": true }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4593,18 +4593,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/seq-queue": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", @@ -5146,9 +5134,9 @@ } }, "node_modules/tsd": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.31.1.tgz", - "integrity": "sha512-sSL84A0SFwx2xGMWrxlGaarKFSQszWjJS2vgNDDLwatytzg2aq6ShlwHsBYxRNmjzXISODwMva5ZOdAg/4AoOA==", + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.31.2.tgz", + "integrity": "sha512-VplBAQwvYrHzVihtzXiUVXu5bGcr7uH1juQZ1lmKgkuGNGT+FechUCqmx9/zk7wibcqR2xaNEwCkDyKh+VVZnQ==", "dev": true, "dependencies": { "@tsd/typescript": "~5.4.3", @@ -5172,6 +5160,25 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/tsx": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", + "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index 8c24b339c..e1a083dbb 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "@types/node": "^22.5.0", "@types/pg": "^8.11.6", "@types/pg-cursor": "^2.7.2", + "@types/semver": "^7.5.8", "@types/sinon": "^17.0.2", "@types/tedious": "^4.0.9", "better-sqlite3": "^11.2.1", @@ -92,15 +93,18 @@ "lodash": "^4.17.21", "mocha": "^10.7.3", "mysql2": "^3.11.0", + "pathe": "^1.1.2", "pg": "^8.12.0", "pg-cursor": "^2.11.0", "pkg-pr-new": "^0.0.30", "playwright": "^1.46.1", "prettier": "^3.3.3", + "semver": "^7.6.3", "sinon": "^19.0.2", "tarn": "^3.0.2", "tedious": "^19.0.0", "tsd": "^0.31.1", + "tsx": "^4.19.1", "typescript": "^5.6.3" } } diff --git a/scripts/exclude-test-files-for-backwards-compat.mts b/scripts/exclude-test-files-for-backwards-compat.mts new file mode 100644 index 000000000..9378dc76d --- /dev/null +++ b/scripts/exclude-test-files-for-backwards-compat.mts @@ -0,0 +1,22 @@ +import { writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'pathe' +import { lt } from 'semver' +import { devDependencies } from '../package.json' + +const typescriptVersion = devDependencies.typescript.replace('^', '') +const testTsConfigRelativePath = '../test/node/tsconfig.json' + +if (lt(typescriptVersion, '5.0.0')) { + const tsconfig = await import('../test/node/tsconfig.json') + + await writeFile( + resolve( + dirname(new URL(import.meta.url).pathname), + testTsConfigRelativePath, + ), + JSON.stringify({ + ...tsconfig, + exclude: [...(tsconfig.exclude || []), 'src/async-dispose.test.ts'], + }), + ) +} diff --git a/src/kysely.ts b/src/kysely.ts index 31d815e70..e099e33b5 100644 --- a/src/kysely.ts +++ b/src/kysely.ts @@ -945,7 +945,9 @@ export class ControlledTransaction< */ rollbackToSavepoint( savepointName: SN, - ): Command>> { + ): RollbackToSavepoint extends string[] + ? Command>> + : never { this.#assertNotCommittedOrRolledBack() return new Command(async () => { @@ -956,7 +958,7 @@ export class ControlledTransaction< ) return new ControlledTransaction({ ...this.#props }) - }) + }) as any } /** @@ -987,7 +989,9 @@ export class ControlledTransaction< */ releaseSavepoint( savepointName: SN, - ): Command>> { + ): ReleaseSavepoint extends string[] + ? Command>> + : never { this.#assertNotCommittedOrRolledBack() return new Command(async () => { @@ -998,7 +1002,7 @@ export class ControlledTransaction< ) return new ControlledTransaction({ ...this.#props }) - }) + }) as any } override withPlugin(plugin: KyselyPlugin): ControlledTransaction { diff --git a/src/parser/savepoint-parser.ts b/src/parser/savepoint-parser.ts index 8163bb935..73297aa1b 100644 --- a/src/parser/savepoint-parser.ts +++ b/src/parser/savepoint-parser.ts @@ -4,19 +4,19 @@ import { RawNode } from '../operation-node/raw-node.js' export type RollbackToSavepoint< S extends string[], SN extends S[number], -> = S extends [...infer L extends string[], infer R] +> = S extends [...infer L, infer R] ? R extends SN ? S - : RollbackToSavepoint + : RollbackToSavepoint : never export type ReleaseSavepoint< S extends string[], SN extends S[number], -> = S extends [...infer L extends string[], infer R] +> = S extends [...infer L, infer R] ? R extends SN ? L - : ReleaseSavepoint + : ReleaseSavepoint : never export function parseSavepointCommand( diff --git a/test/node/src/async-dispose.test.ts b/test/node/src/async-dispose.test.ts index 72440ba46..5fd922df9 100644 --- a/test/node/src/async-dispose.test.ts +++ b/test/node/src/async-dispose.test.ts @@ -17,6 +17,7 @@ describe('async dispose', function () { const steps: string[] = [] { + // @ts-ignore - `using` was only introduced in TS 5.2 await using db = new Kysely({ dialect: { createAdapter: () => new PostgresAdapter(), diff --git a/test/node/src/controlled-transaction.test.ts b/test/node/src/controlled-transaction.test.ts index 5821ef11d..8ac61ea15 100644 --- a/test/node/src/controlled-transaction.test.ts +++ b/test/node/src/controlled-transaction.test.ts @@ -254,7 +254,7 @@ for (const dialect of DIALECTS) { 'repeatable read', 'serializable', ...(dialect === 'mssql' ? (['snapshot'] as const) : []), - ] satisfies IsolationLevel[]) { + ] as const) { it(`should set the transaction isolation level as "${isolationLevel}"`, async () => { const trx = await ctx.db .startTransaction() diff --git a/test/node/tsconfig.json b/test/node/tsconfig.json index 1ed9e5376..314b0282c 100644 --- a/test/node/tsconfig.json +++ b/test/node/tsconfig.json @@ -1,11 +1 @@ -{ - "extends": "../../tsconfig-base.json", - "include": ["src/**/*"], - "compilerOptions": { - "target": "ES2022", - "lib": ["ESNext"], - "module": "CommonJS", - "outDir": "dist", - "skipLibCheck": true - } -} +{"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true},"default":{"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true},"default":{"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true},"default":{"extends":"../../tsconfig-base.json","include":["src/**/*"],"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true}},"extends":"../../tsconfig-base.json","include":["src/**/*"],"exclude":["src/async-dispose.test.ts"]},"exclude":["src/async-dispose.test.ts"],"extends":"../../tsconfig-base.json","include":["src/**/*"]},"exclude":["src/async-dispose.test.ts","src/async-dispose.test.ts"],"extends":"../../tsconfig-base.json","include":["src/**/*"]} \ No newline at end of file From b8fc2b6a32d878c2f2ebd3dc616ccaa6ff906426 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Tue, 5 Nov 2024 00:27:58 +0200 Subject: [PATCH 11/28] chore: enforce min TS version (#1194) --- .github/workflows/test.yml | 10 +- .npmignore | 2 +- outdated-typescript.d.ts | 5 + package-lock.json | 2 +- package.json | 10 +- test/outdated-ts/outdated-ts.test.ts | 10 + test/outdated-ts/package-lock.json | 4048 ++++++++++++++++++++++++++ test/outdated-ts/package.json | 19 + test/outdated-ts/tsconfig.json | 7 + 9 files changed, 4107 insertions(+), 6 deletions(-) create mode 100644 outdated-typescript.d.ts create mode 100644 test/outdated-ts/outdated-ts.test.ts create mode 100644 test/outdated-ts/package-lock.json create mode 100644 test/outdated-ts/package.json create mode 100644 test/outdated-ts/tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0bc02bbf5..3afe0e966 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -208,13 +208,17 @@ jobs: - name: Run build with newer TypeScript run: npm run build - - name: Install older TypeScript - run: npm i -D typescript@${{ matrix.typescript-version }} tsd@${{ fromJson('{ "^4.6":"0.20.0", "^4.7":"0.22.0", "^4.8":"0.24.1", "^4.9":"0.27.0", "^5.0":"0.28.1", "^5.2":"0.29.0", "^5.3":"0.30.7", "^5.4":"0.31.2" }')[matrix.typescript-version] }} + - run: | + echo "TS_VERSION=${{ matrix.typescript-version }}" >> $GITHUB_ENV + echo "TSD_VERSION=${{ fromJson('{ "^4.6":"0.20.0", "^4.7":"0.22.0", "^4.8":"0.24.1", "^4.9":"0.27.0", "^5.0":"0.28.1", "^5.2":"0.29.0", "^5.3":"0.30.7", "^5.4":"0.31.2" }')[env.TS_VERSION] }} >> $GITHUB_ENV" + + - name: Install Typescript (${{ env.TS_VERSION }}) and TSD (${{ env.TSD_VERSION }}) + run: npm i -D typescript@${{ env.TS_VERSION }} tsd@${{ env.TSD_VERSION }} - name: Exclude non-backward compatible tests run: npx tsx ./scripts/exclude-test-files-for-backwards-compat.mts - - name: Run tests with older TypeScript + - name: Run tests with older TypeScript version run: npm run test:typings && npm run test:node:build jsdocs: diff --git a/.npmignore b/.npmignore index c92c384f7..d9e1ad890 100644 --- a/.npmignore +++ b/.npmignore @@ -16,4 +16,4 @@ package-lock.json tsconfig-base.json tsconfig-cjs.json tsconfig.json - +CONTRIBUTING.md diff --git a/outdated-typescript.d.ts b/outdated-typescript.d.ts new file mode 100644 index 000000000..ec280583e --- /dev/null +++ b/outdated-typescript.d.ts @@ -0,0 +1,5 @@ +import type { KyselyTypeError } from './dist/cjs/util/type-error' + +declare const Kysely: KyselyTypeError<'The installed TypeScript version is outdated and cannot guarantee type-safety with Kysely. Please upgrade to version 4.6 or newer.'> +declare const RawBuilder: KyselyTypeError<'The installed TypeScript version is outdated and cannot guarantee type-safety with Kysely. Please upgrade to version 4.6 or newer.'> +declare const sql: KyselyTypeError<'The installed TypeScript version is outdated and cannot guarantee type-safety with Kysely. Please upgrade to version 4.6 or newer.'> diff --git a/package-lock.json b/package-lock.json index 6267dc398..7c368c9fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "typescript": "^5.6.3" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@andrewbranch/untar.js": { diff --git a/package.json b/package.json index e1a083dbb..03712fef5 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,17 @@ "url": "git://github.com/kysely-org/kysely.git" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" }, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", + "typesVersions": { + "<4.6": { + "*": [ + "outdated-typescript.d.ts" + ] + } + }, "exports": { ".": { "import": "./dist/esm/index.js", @@ -54,6 +61,7 @@ "test:esbuild": "esbuild --bundle --platform=node --external:pg-native dist/esm/index.js --outfile=/dev/null", "test:exports": "attw --pack . && node scripts/check-exports.js", "test:jsdocs": "deno check --doc-only --no-lock --unstable-sloppy-imports --config=\"deno.check.json\" ./src", + "test:outdatedts": "npm run build && cd test/outdated-ts && npm ci && npm test", "prettier": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", "build": "npm run clean && (npm run build:esm & npm run build:cjs) && npm run script:module-fixup && npm run script:copy-interface-doc", "build:esm": "tsc -p tsconfig.json && npm run script:add-deno-type-references", diff --git a/test/outdated-ts/outdated-ts.test.ts b/test/outdated-ts/outdated-ts.test.ts new file mode 100644 index 000000000..9d6072cd3 --- /dev/null +++ b/test/outdated-ts/outdated-ts.test.ts @@ -0,0 +1,10 @@ +import { Kysely, RawBuilder, sql } from 'kysely' +import { KyselyTypeError } from '../../src/util/type-error' + +function expectOutdatedTSError( + _: KyselyTypeError<'The installed TypeScript version is outdated and cannot guarantee type-safety with Kysely. Please upgrade to version 4.6 or newer.'>, +): void {} + +expectOutdatedTSError(Kysely) +expectOutdatedTSError(RawBuilder) +expectOutdatedTSError(sql) diff --git a/test/outdated-ts/package-lock.json b/test/outdated-ts/package-lock.json new file mode 100644 index 000000000..02dcaf22f --- /dev/null +++ b/test/outdated-ts/package-lock.json @@ -0,0 +1,4048 @@ +{ + "name": "outdated-ts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "outdated-ts", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "kysely": "file:../../" + }, + "devDependencies": { + "typescript": "4.5.x" + } + }, + "../..": { + "version": "0.27.4", + "license": "MIT", + "devDependencies": { + "@arethetypeswrong/cli": "^0.16.4", + "@types/better-sqlite3": "^7.6.11", + "@types/chai": "^4.3.17", + "@types/chai-as-promised": "^7.1.8", + "@types/chai-subset": "^1.3.5", + "@types/mocha": "^10.0.7", + "@types/node": "^22.5.0", + "@types/pg": "^8.11.6", + "@types/pg-cursor": "^2.7.2", + "@types/semver": "^7.5.8", + "@types/sinon": "^17.0.2", + "@types/tedious": "^4.0.9", + "better-sqlite3": "^11.2.1", + "chai": "^4.5.0", + "chai-as-promised": "^7.1.2", + "chai-subset": "^1.6.0", + "esbuild": "^0.23.1", + "lodash": "^4.17.21", + "mocha": "^10.7.3", + "mysql2": "^3.11.0", + "pathe": "^1.1.2", + "pg": "^8.12.0", + "pg-cursor": "^2.11.0", + "playwright": "^1.46.1", + "prettier": "^3.3.3", + "semver": "^7.6.3", + "sinon": "^18.0.0", + "tarn": "^3.0.2", + "tedious": "^19.0.0", + "tsd": "^0.31.1", + "tsx": "^4.19.1", + "typescript": "^5.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@andrewbranch/untar.js": { + "version": "1.0.3", + "dev": true + }, + "../../node_modules/@arethetypeswrong/cli": { + "version": "0.16.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@arethetypeswrong/core": "0.16.4", + "chalk": "^4.1.2", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "marked": "^9.1.2", + "marked-terminal": "^7.1.0", + "semver": "^7.5.4" + }, + "bin": { + "attw": "dist/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "../../node_modules/@arethetypeswrong/core": { + "version": "0.16.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@andrewbranch/untar.js": "^1.0.3", + "cjs-module-lexer": "^1.2.3", + "fflate": "^0.8.2", + "lru-cache": "^10.4.3", + "semver": "^7.5.4", + "typescript": "5.6.1-rc", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "../../node_modules/@arethetypeswrong/core/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "../../node_modules/@arethetypeswrong/core/node_modules/typescript": { + "version": "5.6.1-rc", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "../../node_modules/@azure/abort-controller": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "../../node_modules/@azure/core-auth": { + "version": "1.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-client": { + "version": "1.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-client/node_modules/@azure/abort-controller": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-http-compat": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.4", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "../../node_modules/@azure/core-lro": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-lro/node_modules/@azure/abort-controller": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-paging": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "../../node_modules/@azure/core-rest-pipeline": { + "version": "1.14.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-tracing": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "../../node_modules/@azure/core-util": { + "version": "1.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/core-util/node_modules/@azure/abort-controller": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/identity": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.14.0", + "@azure/msal-node": "^2.9.2", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/keyvault-keys": { + "version": "4.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^2.0.1", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "../../node_modules/@azure/logger": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "../../node_modules/@azure/msal-browser": { + "version": "3.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.14.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "../../node_modules/@azure/msal-common": { + "version": "14.14.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "../../node_modules/@azure/msal-node": { + "version": "2.13.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.14.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "../../node_modules/@babel/code-frame": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "../../node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "../../node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "../../node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "../../node_modules/@babel/highlight": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "../../node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "../../node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "../../node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "../../node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "../../node_modules/@jest/schemas": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "../../node_modules/@js-joda/core": { + "version": "5.6.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "../../node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "../../node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "../../node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "../../node_modules/@sinclair/typebox": { + "version": "0.27.8", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@sindresorhus/is": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "../../node_modules/@sinonjs/commons": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "../../node_modules/@sinonjs/fake-timers": { + "version": "11.3.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "../../node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "../../node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "../../node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "../../node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "../../node_modules/@tsd/typescript": { + "version": "5.4.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "../../node_modules/@types/better-sqlite3": { + "version": "7.6.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "../../node_modules/@types/chai": { + "version": "4.3.17", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "*" + } + }, + "../../node_modules/@types/chai-subset": { + "version": "1.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "*" + } + }, + "../../node_modules/@types/eslint": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "../../node_modules/@types/estree": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/minimist": { + "version": "1.2.5", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/mocha": { + "version": "10.0.7", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/node": { + "version": "22.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "../../node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/pg": { + "version": "8.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "../../node_modules/@types/pg-cursor": { + "version": "2.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/pg": "*" + } + }, + "../../node_modules/@types/readable-stream": { + "version": "4.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "../../node_modules/@types/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/semver": { + "version": "7.5.8", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/sinon": { + "version": "17.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "../../node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "dev": true, + "license": "MIT" + }, + "../../node_modules/@types/tedious": { + "version": "4.0.14", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "../../node_modules/abort-controller": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "../../node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "../../node_modules/ansi-colors": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "../../node_modules/any-promise": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "../../node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "../../node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/assertion-error": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "../../node_modules/aws-ssl-profiles": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "../../node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "../../node_modules/better-sqlite3": { + "version": "11.2.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "../../node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/bindings": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "../../node_modules/bl": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "../../node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "../../node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "../../node_modules/buffer": { + "version": "5.7.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "../../node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "../../node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/camelcase-keys": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/chai": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/chai-as-promised": { + "version": "7.1.2", + "dev": true, + "license": "WTFPL", + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "../../node_modules/chai-subset": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/chai/node_modules/type-detect": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "../../node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/char-regex": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "../../node_modules/check-error": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "../../node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "../../node_modules/chownr": { + "version": "1.1.4", + "dev": true, + "license": "ISC" + }, + "../../node_modules/cjs-module-lexer": { + "version": "1.4.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/cli-highlight": { + "version": "2.1.11", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "../../node_modules/cli-table3": { + "version": "0.6.5", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "../../node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "../../node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "../../node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "../../node_modules/commander": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "../../node_modules/debug": { + "version": "4.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "../../node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/decamelize-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/decompress-response": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/deep-eql": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "../../node_modules/deep-extend": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "../../node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/denque": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "../../node_modules/detect-libc": { + "version": "2.0.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/diff": { + "version": "5.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "../../node_modules/diff-sequences": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "../../node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "../../node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/emojilib": { + "version": "2.4.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/end-of-stream": { + "version": "1.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "../../node_modules/environment": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "../../node_modules/esbuild": { + "version": "0.23.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "../../node_modules/escalade": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/eslint-formatter-pretty": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "^7.2.13", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "eslint-rule-docs": "^1.1.5", + "log-symbols": "^4.0.0", + "plur": "^4.0.0", + "string-width": "^4.2.0", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/eslint-rule-docs": { + "version": "1.1.235", + "dev": true, + "license": "MIT" + }, + "../../node_modules/event-target-shim": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "../../node_modules/expand-template": { + "version": "2.0.3", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/fast-glob": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "../../node_modules/fastq": { + "version": "1.17.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "../../node_modules/fflate": { + "version": "0.8.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/file-uri-to-path": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "../../node_modules/fs-constants": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "../../node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "../../node_modules/generate-function": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "../../node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "../../node_modules/get-func-name": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "../../node_modules/get-tsconfig": { + "version": "4.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "../../node_modules/github-from-package": { + "version": "0.0.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "../../node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "../../node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/hard-rejection": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/hasown": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "../../node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "../../node_modules/highlight.js": { + "version": "10.7.3", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "../../node_modules/hosted-git-info": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "../../node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "../../node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/ieee754": { + "version": "1.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "../../node_modules/ignore": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "../../node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "../../node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "../../node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "../../node_modules/irregular-plurals": { + "version": "3.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/is-core-module": { + "version": "2.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "../../node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "../../node_modules/is-plain-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/is-property": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/jest-diff": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "../../node_modules/jest-get-type": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "../../node_modules/js-md4": { + "version": "0.3.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "../../node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/jsonwebtoken": { + "version": "9.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "../../node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "../../node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "../../node_modules/just-extend": { + "version": "6.2.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/jwa": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "../../node_modules/jws": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "../../node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "../../node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.get": { + "version": "4.4.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.includes": { + "version": "4.3.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.isboolean": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.isinteger": { + "version": "4.0.4", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.isnumber": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.isplainobject": { + "version": "4.0.6", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.isstring": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/lodash.once": { + "version": "4.1.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/long": { + "version": "5.2.3", + "dev": true, + "license": "Apache-2.0" + }, + "../../node_modules/loupe": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "../../node_modules/lru-cache": { + "version": "8.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16.14" + } + }, + "../../node_modules/map-obj": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/marked": { + "version": "9.1.6", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "../../node_modules/marked-terminal": { + "version": "7.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.3.0", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <15" + } + }, + "../../node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/marked-terminal/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "../../node_modules/marked-terminal/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "../../node_modules/marked-terminal/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/marked-terminal/node_modules/supports-hyperlinks": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/meow": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "../../node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "../../node_modules/mimic-response": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "../../node_modules/minimist-options": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "../../node_modules/mkdirp-classic": { + "version": "0.5.3", + "dev": true, + "license": "MIT" + }, + "../../node_modules/mocha": { + "version": "10.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "../../node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "../../node_modules/mysql2": { + "version": "3.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "../../node_modules/mz": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "../../node_modules/named-placeholders": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "../../node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "../../node_modules/napi-build-utils": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/native-duplexpair": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "../../node_modules/nise": { + "version": "6.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "../../node_modules/node-abi": { + "version": "3.56.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/node-emoji": { + "version": "2.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "../../node_modules/normalize-package-data": { + "version": "3.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/obuf": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "../../node_modules/open": { + "version": "8.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "../../node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/parse5": { + "version": "5.1.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "../../node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "../../node_modules/path-to-regexp": { + "version": "6.2.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/pathe": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/pathval": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "../../node_modules/pg": { + "version": "8.12.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "../../node_modules/pg-cloudflare": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "../../node_modules/pg-connection-string": { + "version": "2.6.4", + "dev": true, + "license": "MIT" + }, + "../../node_modules/pg-cursor": { + "version": "2.11.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "pg": "^8" + } + }, + "../../node_modules/pg-int8": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "../../node_modules/pg-numeric": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/pg-pool": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "../../node_modules/pg-protocol": { + "version": "1.6.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/pg-types": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/pg/node_modules/pg-types": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "../../node_modules/pg/node_modules/postgres-array": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/pg/node_modules/postgres-date": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/pg/node_modules/postgres-interval": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/pgpass": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "../../node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "../../node_modules/playwright": { + "version": "1.46.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.46.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "../../node_modules/playwright-core": { + "version": "1.46.1", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "../../node_modules/plur": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^3.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/postgres-array": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "../../node_modules/postgres-bytea": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "../../node_modules/postgres-date": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "../../node_modules/postgres-interval": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "../../node_modules/postgres-range": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "../../node_modules/prebuild-install": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/prettier": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "../../node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "../../node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "../../node_modules/process": { + "version": "0.11.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "../../node_modules/pump": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "../../node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "../../node_modules/quick-lru": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "../../node_modules/rc": { + "version": "1.2.8", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "../../node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "../../node_modules/read-pkg": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/read-pkg-up": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "dev": true, + "license": "ISC" + }, + "../../node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "../../node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "../../node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/readable-stream": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "../../node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "../../node_modules/redent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "../../node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "../../node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "../../node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "../../node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "../../node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/semver": { + "version": "7.6.3", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/seq-queue": { + "version": "0.0.5", + "dev": true + }, + "../../node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "../../node_modules/simple-concat": { + "version": "1.0.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "../../node_modules/simple-get": { + "version": "4.0.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "../../node_modules/sinon": { + "version": "18.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.2.0", + "nise": "^6.0.0", + "supports-color": "^7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "../../node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/skin-tone": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "../../node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "../../node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "../../node_modules/spdx-license-ids": { + "version": "3.0.17", + "dev": true, + "license": "CC0-1.0" + }, + "../../node_modules/split2": { + "version": "4.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "../../node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "../../node_modules/sqlstring": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "../../node_modules/stoppable": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, + "../../node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "../../node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "../../node_modules/supports-hyperlinks": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "../../node_modules/tar-fs": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "../../node_modules/tar-stream": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "../../node_modules/tarn": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "../../node_modules/tedious": { + "version": "19.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.7.2", + "@azure/identity": "^4.2.1", + "@azure/keyvault-keys": "^4.4.0", + "@js-joda/core": "^5.6.1", + "@types/node": ">=18", + "bl": "^6.0.11", + "iconv-lite": "^0.6.3", + "js-md4": "^0.3.2", + "native-duplexpair": "^1.0.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">=18.17" + } + }, + "../../node_modules/tedious/node_modules/bl": { + "version": "6.0.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "../../node_modules/tedious/node_modules/buffer": { + "version": "6.0.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "../../node_modules/tedious/node_modules/readable-stream": { + "version": "4.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "../../node_modules/thenify": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "../../node_modules/thenify-all": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "../../node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "../../node_modules/trim-newlines": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/tsd": { + "version": "0.31.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@tsd/typescript": "~5.4.3", + "eslint-formatter-pretty": "^4.1.0", + "globby": "^11.0.1", + "jest-diff": "^29.0.3", + "meow": "^9.0.0", + "path-exists": "^4.0.0", + "read-pkg-up": "^7.0.0" + }, + "bin": { + "tsd": "dist/cli.js" + }, + "engines": { + "node": ">=14.16" + } + }, + "../../node_modules/tslib": { + "version": "2.6.2", + "dev": true, + "license": "0BSD" + }, + "../../node_modules/tsx": { + "version": "4.19.1", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "../../node_modules/tunnel-agent": { + "version": "0.6.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "../../node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/typescript": { + "version": "5.6.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "../../node_modules/undici-types": { + "version": "6.19.8", + "dev": true, + "license": "MIT" + }, + "../../node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "../../node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "../../node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "../../node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "../../node_modules/validate-npm-package-name": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "../../node_modules/workerpool": { + "version": "6.5.1", + "dev": true, + "license": "Apache-2.0" + }, + "../../node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "../../node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "../../node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "../../node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "../../node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "../../node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "../../node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "../../node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "../../node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/kysely": { + "resolved": "../..", + "link": true + }, + "node_modules/typescript": { + "version": "4.5.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/test/outdated-ts/package.json b/test/outdated-ts/package.json new file mode 100644 index 000000000..3ce68bf4a --- /dev/null +++ b/test/outdated-ts/package.json @@ -0,0 +1,19 @@ +{ + "name": "outdated-ts", + "private": true, + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "tsc --noEmit" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "kysely": "file:../../" + }, + "devDependencies": { + "typescript": "4.5.x" + } +} diff --git a/test/outdated-ts/tsconfig.json b/test/outdated-ts/tsconfig.json new file mode 100644 index 000000000..4e0948262 --- /dev/null +++ b/test/outdated-ts/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig-base.json", + "include": ["./**/*"], + "compilerOptions": { + "skipLibCheck": true + } +} From f16bf764a0a096b590009dd456941f864d738a48 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 24 Nov 2024 02:41:15 +0200 Subject: [PATCH 12/28] fix package-lock. --- package-lock.json | 478 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7c368c9fc..2e0f9838f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2539,6 +2539,21 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -4056,6 +4071,21 @@ "node": ">=18" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/plur": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", @@ -5179,6 +5209,454 @@ "fsevents": "~2.3.3" } }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", From 070cf458aa006d2140f8144f137ee6aad1fa6e7d Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 1 Dec 2024 09:57:21 +0200 Subject: [PATCH 13/28] fix jsdocs. --- deno.check.d.ts | 6 ++ src/kysely.ts | 102 +++++++++++++++------- src/query-builder/select-query-builder.ts | 8 +- src/query-builder/update-query-builder.ts | 3 +- 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/deno.check.d.ts b/deno.check.d.ts index c7629f1e2..b534ec3f6 100644 --- a/deno.check.d.ts +++ b/deno.check.d.ts @@ -10,6 +10,7 @@ import type { } from './dist/esm' export interface Database { + audit: AuditTable person: PersonTable pet: PetTable toy: ToyTable @@ -17,6 +18,10 @@ export interface Database { wine_stock_change: WineStockChangeTable } +interface AuditTable { + action: string +} + interface PersonTable { id: Generated address: { city: string } | null @@ -51,6 +56,7 @@ interface PetTable { interface ToyTable { id: Generated + name: string pet_id: number price: number } diff --git a/src/kysely.ts b/src/kysely.ts index e099e33b5..6739cc00b 100644 --- a/src/kysely.ts +++ b/src/kysely.ts @@ -341,7 +341,7 @@ export class Kysely * * await trx.commit().execute() * - * return catto + * // ... * } catch (error) { * await trx.rollback().execute() * } @@ -375,20 +375,19 @@ export class Kysely * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() * * try { - * const bone = await trxAfterJennifer - * .insertInto('toy') - * .values({ name: 'Bone', price: 1.99 }) - * .returning('id') - * .executeTakeFirstOrThrow() - * - * await trxAfterJennifer + * const catto = await trxAfterJennifer * .insertInto('pet') * .values({ * owner_id: jennifer.id, * name: 'Catto', * species: 'cat', - * favorite_toy_id: bone.id, * }) + * .returning('id') + * .executeTakeFirstOrThrow() + * + * await trxAfterJennifer + * .insertInto('toy') + * .values({ name: 'Bone', price: 1.99, pet_id: catto.id }) * .execute() * } catch (error) { * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() @@ -838,6 +837,11 @@ export class ControlledTransaction< * ### Examples * * ```ts + * import type { Kysely } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module + * + * const trx = await db.startTransaction().execute() + * * try { * await doSomething(trx) * @@ -845,6 +849,8 @@ export class ControlledTransaction< * } catch (error) { * await trx.rollback().execute() * } + * + * async function doSomething(kysely: Kysely) {} * ``` */ commit(): Command { @@ -865,6 +871,11 @@ export class ControlledTransaction< * ### Examples * * ```ts + * import type { Kysely } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module + * + * const trx = await db.startTransaction().execute() + * * try { * await doSomething(trx) * @@ -872,6 +883,8 @@ export class ControlledTransaction< * } catch (error) { * await trx.rollback().execute() * } + * + * async function doSomething(kysely: Kysely) {} * ``` */ rollback(): Command { @@ -894,15 +907,23 @@ export class ControlledTransaction< * ### Examples * * ```ts - * await insertJennifer(trx) + * import type { Kysely } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module * - * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * const trx = await db.startTransaction().execute() * - * try { - * await doSomething(trxAfterJennifer) - * } catch (error) { - * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() - * } + * await insertJennifer(trx) + * + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * await doSomething(trxAfterJennifer) + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * + * async function insertJennifer(kysely: Kysely) {} + * async function doSomething(kysely: Kysely) {} * ``` */ savepoint( @@ -932,15 +953,23 @@ export class ControlledTransaction< * ### Examples * * ```ts - * await insertJennifer(trx) + * import type { Kysely } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module * - * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * const trx = await db.startTransaction().execute() * - * try { - * await doSomething(trxAfterJennifer) - * } catch (error) { - * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() - * } + * await insertJennifer(trx) + * + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * await doSomething(trxAfterJennifer) + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * + * async function insertJennifer(kysely: Kysely) {} + * async function doSomething(kysely: Kysely) {} * ``` */ rollbackToSavepoint( @@ -972,19 +1001,28 @@ export class ControlledTransaction< * ### Examples * * ```ts - * await insertJennifer(trx) + * import type { Kysely } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module * - * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * const trx = await db.startTransaction().execute() * - * try { - * await doSomething(trxAfterJennifer) - * } catch (error) { - * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() - * } + * await insertJennifer(trx) * - * await trxAfterJennifer.releaseSavepoint('after_jennifer').execute() + * const trxAfterJennifer = await trx.savepoint('after_jennifer').execute() + * + * try { + * await doSomething(trxAfterJennifer) + * } catch (error) { + * await trxAfterJennifer.rollbackToSavepoint('after_jennifer').execute() + * } + * + * await trxAfterJennifer.releaseSavepoint('after_jennifer').execute() + * + * await doSomethingElse(trx) * - * await doSomethingElse(trx) + * async function insertJennifer(kysely: Kysely) {} + * async function doSomething(kysely: Kysely) {} + * async function doSomethingElse(kysely: Kysely) {} * ``` */ releaseSavepoint( diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index 3fa5c9a6d..cb6126cca 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -259,7 +259,7 @@ export interface SelectQueryBuilder * ]).as('is_jennifer_or_arnold'), * * // Select a raw sql expression - * sql`concat(first_name, ' ', last_name)`.as('full_name'). + * sql`concat(first_name, ' ', last_name)`.as('full_name'), * * // Select a static string value * val('Some value').as('string_value'), @@ -1961,13 +1961,15 @@ export interface SelectQueryBuilder * instances as inputs: * * ```ts + * import type { Expression } from 'kysely' + * * function doStuff(expr: Expression) { - * ... + * // ... * } * * // Error! This is not ok because the expression type is * // `{ first_name: string }` instead of `string`. - * doStuff(db.selectFrom('person').select('first_name')) + * // doStuff(db.selectFrom('person').select('first_name')) * * // Ok! This is ok since we've plucked the `string` type of the * // only column in the output type. diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index a474a2eb9..dba84a5a3 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -54,7 +54,6 @@ import { NoResultError, NoResultErrorConstructor, } from './no-result-error.js' -import { Selectable } from '../util/column-type.js' import { Explainable, ExplainFormat } from '../util/explainable.js' import { AliasedExpression, Expression } from '../expression/expression.js' import { @@ -649,7 +648,7 @@ export class UpdateQueryBuilder * .set('person.first_name', 'Updated person') * .set('pet.name', 'Updated doggo') * .whereRef('person.id', '=', 'pet.owner_id') - * .where('person.id', '=', '1') + * .where('person.id', '=', 1) * .executeTakeFirst() * ``` * From 498bcb3719e50f8d755c1eecac80b03bf1b0ecc5 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sun, 1 Dec 2024 15:26:53 +0100 Subject: [PATCH 14/28] add `returning` support in `MERGE` queries. (#1171) --- deno.check.d.ts | 1 + src/helpers/postgres.ts | 52 +++++ src/operation-node/merge-query-node.ts | 2 + .../operation-node-transformer.ts | 1 + src/query-builder/delete-query-builder.ts | 4 +- src/query-builder/merge-query-builder.ts | 117 +++++++++- src/query-builder/returning-interface.ts | 24 ++- src/query-builder/update-query-builder.ts | 4 +- src/query-compiler/default-query-compiler.ts | 5 + test/node/src/merge.test.ts | 201 ++++++++++++++++++ test/typings/test-d/merge.test-d.ts | 181 +++++++++------- 11 files changed, 503 insertions(+), 89 deletions(-) diff --git a/deno.check.d.ts b/deno.check.d.ts index b534ec3f6..403306475 100644 --- a/deno.check.d.ts +++ b/deno.check.d.ts @@ -12,6 +12,7 @@ import type { export interface Database { audit: AuditTable person: PersonTable + person_backup: PersonTable pet: PetTable toy: ToyTable wine: WineTable diff --git a/src/helpers/postgres.ts b/src/helpers/postgres.ts index b1452e77c..3e010c2d6 100644 --- a/src/helpers/postgres.ts +++ b/src/helpers/postgres.ts @@ -169,3 +169,55 @@ export function jsonBuildObject>>( Object.keys(obj).flatMap((k) => [sql.lit(k), obj[k]]), )})` } + +export type MergeAction = 'INSERT' | 'UPDATE' | 'DELETE' + +/** + * The PostgreSQL `merge_action` function. + * + * This function can be used in a `returning` clause to get the action that was + * performed in a `mergeInto` query. The function returns one of the following + * strings: `'INSERT'`, `'UPDATE'`, or `'DELETE'`. + * + * ### Examples + * + * ```ts + * import { mergeAction } from 'kysely/helpers/postgres' + * + * const result = await db + * .mergeInto('person as p') + * .using('person_backup as pb', 'p.id', 'pb.id') + * .whenMatched() + * .thenUpdateSet(({ ref }) => ({ + * first_name: ref('pb.first_name'), + * updated_at: ref('pb.updated_at').$castTo(), + * })) + * .whenNotMatched() + * .thenInsertValues(({ ref}) => ({ + * id: ref('pb.id'), + * first_name: ref('pb.first_name'), + * created_at: ref('pb.updated_at'), + * updated_at: ref('pb.updated_at').$castTo(), + * })) + * .returning([mergeAction().as('action'), 'p.id', 'p.updated_at']) + * .execute() + * + * result[0].action + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * merge into "person" as "p" + * using "person_backup" as "pb" on "p"."id" = "pb"."id" + * when matched then update set + * "first_name" = "pb"."first_name", + * "updated_at" = "pb"."updated_at"::text + * when not matched then insert values ("id", "first_name", "created_at", "updated_at") + * values ("pb"."id", "pb"."first_name", "pb"."updated_at", "pb"."updated_at") + * returning merge_action() as "action", "p"."id", "p"."updated_at" + * ``` + */ +export function mergeAction(): RawBuilder { + return sql`merge_action()` +} diff --git a/src/operation-node/merge-query-node.ts b/src/operation-node/merge-query-node.ts index 1e659d418..f99839b7c 100644 --- a/src/operation-node/merge-query-node.ts +++ b/src/operation-node/merge-query-node.ts @@ -3,6 +3,7 @@ import { AliasNode } from './alias-node.js' import { JoinNode } from './join-node.js' import { OperationNode } from './operation-node.js' import { OutputNode } from './output-node.js' +import { ReturningNode } from './returning-node.js' import { TableNode } from './table-node.js' import { TopNode } from './top-node.js' import { WhenNode } from './when-node.js' @@ -15,6 +16,7 @@ export interface MergeQueryNode extends OperationNode { readonly whens?: ReadonlyArray readonly with?: WithNode readonly top?: TopNode + readonly returning?: ReturningNode readonly output?: OutputNode readonly endModifiers?: ReadonlyArray } diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index b5cadff5f..1fcf5ea83 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -1039,6 +1039,7 @@ export class OperationNodeTransformer { top: this.transformNode(node.top), endModifiers: this.transformNodeList(node.endModifiers), output: this.transformNode(node.output), + returning: this.transformNode(node.returning), }) } diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index a0ece541a..069fbb7e4 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -41,7 +41,7 @@ import { QueryId } from '../util/query-id.js' import { freeze } from '../util/object-utils.js' import { KyselyPlugin } from '../plugin/kysely-plugin.js' import { WhereInterface } from './where-interface.js' -import { ReturningInterface } from './returning-interface.js' +import { MultiTableReturningInterface } from './returning-interface.js' import { isNoResultErrorConstructor, NoResultError, @@ -82,7 +82,7 @@ import { export class DeleteQueryBuilder implements WhereInterface, - ReturningInterface, + MultiTableReturningInterface, OutputInterface, OperationNodeSource, Compilable, diff --git a/src/query-builder/merge-query-builder.ts b/src/query-builder/merge-query-builder.ts index e4ea17841..b1b397bf4 100644 --- a/src/query-builder/merge-query-builder.ts +++ b/src/query-builder/merge-query-builder.ts @@ -22,8 +22,17 @@ import { } from '../parser/join-parser.js' import { parseMergeThen, parseMergeWhen } from '../parser/merge-parser.js' import { ReferenceExpression } from '../parser/reference-parser.js' -import { ReturningAllRow, ReturningRow } from '../parser/returning-parser.js' -import { parseSelectAll, parseSelectArg } from '../parser/select-parser.js' +import { + ReturningAllRow, + ReturningCallbackRow, + ReturningRow, +} from '../parser/returning-parser.js' +import { + parseSelectAll, + parseSelectArg, + SelectCallback, + SelectExpression, +} from '../parser/select-parser.js' import { TableExpression } from '../parser/table-parser.js' import { parseTop } from '../parser/top-parser.js' import { @@ -58,10 +67,13 @@ import { SelectExpressionFromOutputCallback, SelectExpressionFromOutputExpression, } from './output-interface.js' +import { MultiTableReturningInterface } from './returning-interface.js' import { UpdateQueryBuilder } from './update-query-builder.js' export class MergeQueryBuilder - implements OutputInterface + implements + MultiTableReturningInterface, + OutputInterface { readonly #props: MergeQueryBuilderProps @@ -215,6 +227,44 @@ export class MergeQueryBuilder }) } + returning>( + selections: ReadonlyArray, + ): MergeQueryBuilder> + + returning>( + callback: CB, + ): MergeQueryBuilder> + + returning>( + selection: SE, + ): MergeQueryBuilder> + + returning(args: any): any { + return new MergeQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithReturning( + this.#props.queryNode, + parseSelectArg(args), + ), + }) + } + + returningAll( + table: T, + ): MergeQueryBuilder> + + returningAll(): MergeQueryBuilder> + + returningAll(table?: any): any { + return new MergeQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithReturning( + this.#props.queryNode, + parseSelectAll(table), + ), + }) + } + output>( selections: readonly OE[], ): MergeQueryBuilder< @@ -274,7 +324,11 @@ export class WheneableMergeQueryBuilder< ST extends keyof DB, O, > - implements Compilable, OutputInterface, OperationNodeSource + implements + Compilable, + MultiTableReturningInterface, + OutputInterface, + OperationNodeSource { readonly #props: MergeQueryBuilderProps @@ -608,6 +662,54 @@ export class WheneableMergeQueryBuilder< return this.#whenNotMatched([lhs, op, rhs], true, true) } + returning>( + selections: ReadonlyArray, + ): WheneableMergeQueryBuilder> + + returning>( + callback: CB, + ): WheneableMergeQueryBuilder< + DB, + TT, + ST, + ReturningCallbackRow + > + + returning>( + selection: SE, + ): WheneableMergeQueryBuilder> + + returning(args: any): any { + return new WheneableMergeQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithReturning( + this.#props.queryNode, + parseSelectArg(args), + ), + }) + } + + returningAll( + table: T, + ): WheneableMergeQueryBuilder> + + returningAll(): WheneableMergeQueryBuilder< + DB, + TT, + ST, + ReturningAllRow + > + + returningAll(table?: any): any { + return new WheneableMergeQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithReturning( + this.#props.queryNode, + parseSelectAll(table), + ), + }) + } + output>( selections: readonly OE[], ): WheneableMergeQueryBuilder< @@ -788,9 +890,12 @@ export class WheneableMergeQueryBuilder< this.#props.queryId, ) + const { adapter } = this.#props.executor + const query = compiledQuery.query as MergeQueryNode + if ( - (compiledQuery.query as MergeQueryNode).output && - this.#props.executor.adapter.supportsOutput + (query.returning && adapter.supportsReturning) || + (query.output && adapter.supportsOutput) ) { return result.rows as any } diff --git a/src/query-builder/returning-interface.ts b/src/query-builder/returning-interface.ts index 8803677f7..1a7a7f0c3 100644 --- a/src/query-builder/returning-interface.ts +++ b/src/query-builder/returning-interface.ts @@ -1,4 +1,5 @@ import { + ReturningAllRow, ReturningCallbackRow, ReturningRow, } from '../parser/returning-parser.js' @@ -10,7 +11,7 @@ export interface ReturningInterface { * Allows you to return data from modified rows. * * On supported databases like PostgreSQL, this method can be chained to - * `insert`, `update` and `delete` queries to return data. + * `insert`, `update`, `delete` and `merge` queries to return data. * * Note that on SQLite you need to give aliases for the expressions to avoid * [this bug](https://sqlite.org/forum/forumpost/033daf0b32) in SQLite. @@ -78,10 +79,29 @@ export interface ReturningInterface { ): ReturningInterface> /** - * Adds a `returning *` to an insert/update/delete query on databases + * Adds a `returning *` to an insert/update/delete/merge query on databases * that support `returning` such as PostgreSQL. * * Also see the {@link returning} method. */ returningAll(): ReturningInterface> } + +export interface MultiTableReturningInterface + extends ReturningInterface { + /** + * Adds a `returning *` or `returning table.*` to an insert/update/delete/merge + * query on databases that support `returning` such as PostgreSQL. + * + * Also see the {@link returning} method. + */ + returningAll( + tables: ReadonlyArray, + ): MultiTableReturningInterface> + + returningAll( + table: T, + ): MultiTableReturningInterface> + + returningAll(): ReturningInterface> +} diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index dba84a5a3..d35615cad 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -48,7 +48,7 @@ import { freeze } from '../util/object-utils.js' import { UpdateResult } from './update-result.js' import { KyselyPlugin } from '../plugin/kysely-plugin.js' import { WhereInterface } from './where-interface.js' -import { ReturningInterface } from './returning-interface.js' +import { MultiTableReturningInterface } from './returning-interface.js' import { isNoResultErrorConstructor, NoResultError, @@ -83,7 +83,7 @@ import { export class UpdateQueryBuilder implements WhereInterface, - ReturningInterface, + MultiTableReturningInterface, OutputInterface, OperationNodeSource, Compilable, diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 5a04313d6..c0267e1e2 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -1585,6 +1585,11 @@ export class DefaultQueryCompiler this.compileList(node.whens, ' ') } + if (node.returning) { + this.append(' ') + this.visitNode(node.returning) + } + if (node.output) { this.append(' ') this.visitNode(node.output) diff --git a/test/node/src/merge.test.ts b/test/node/src/merge.test.ts index cc4767e58..136385079 100644 --- a/test/node/src/merge.test.ts +++ b/test/node/src/merge.test.ts @@ -1,4 +1,5 @@ import { MergeResult, sql } from '../../..' +import { mergeAction } from '../../../helpers/postgres' import { DIALECTS, NOT_SUPPORTED, @@ -1013,6 +1014,206 @@ for (const dialect of DIALECTS.filter( }) }) + if (dialect === 'postgres') { + it('should perform a merge...using table simple on...when matched then delete returning id query', async () => { + const expected = await ctx.db.selectFrom('pet').select('id').execute() + + const query = ctx.db + .mergeInto('pet') + .using('person', 'pet.owner_id', 'person.id') + .whenMatched() + .thenDelete() + .returning('pet.id') + + testSql(query, dialect, { + postgres: { + sql: 'merge into "pet" using "person" on "pet"."owner_id" = "person"."id" when matched then delete returning "pet"."id"', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + + expect(result).to.eql(expected) + }) + + it('should perform a merge...using table simple on...when matched then update set name returning {target}.name, {source}.first_name query', async () => { + const query = ctx.db + .mergeInto('pet') + .using('person', 'pet.owner_id', 'person.id') + .whenMatched() + .thenUpdateSet((eb) => ({ + name: sql`${eb.ref('person.first_name')} || '''s pet'`, + })) + .returning([ + 'pet.name as pet_name', + 'person.first_name as owner_name', + ]) + + testSql(query, dialect, { + postgres: { + sql: 'merge into "pet" using "person" on "pet"."owner_id" = "person"."id" when matched then update set "name" = "person"."first_name" || \'\'\'s pet\' returning "pet"."name" as "pet_name", "person"."first_name" as "owner_name"', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + + expect(result).to.eql([ + { owner_name: 'Jennifer', pet_name: "Jennifer's pet" }, + { owner_name: 'Arnold', pet_name: "Arnold's pet" }, + { owner_name: 'Sylvester', pet_name: "Sylvester's pet" }, + ]) + }) + + it('should perform a merge...using table simple on...when matched then delete returning * query', async () => { + const expected = await ctx.db + .selectFrom('pet') + .innerJoin('person', 'pet.owner_id', 'person.id') + .selectAll() + .execute() + + const query = ctx.db + .mergeInto('pet') + .using('person', 'pet.owner_id', 'person.id') + .whenMatched() + .thenDelete() + .returningAll() + + testSql(query, dialect, { + postgres: { + sql: 'merge into "pet" using "person" on "pet"."owner_id" = "person"."id" when matched then delete returning *', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + + expect(result).to.eql(expected) + }) + + it('should perform a merge...using table simple on...when matched then delete returning {target}.* query', async () => { + const expected = await ctx.db.selectFrom('pet').selectAll().execute() + + const query = ctx.db + .mergeInto('pet') + .using('person', 'pet.owner_id', 'person.id') + .whenMatched() + .thenDelete() + .returningAll('pet') + + testSql(query, dialect, { + postgres: { + sql: 'merge into "pet" using "person" on "pet"."owner_id" = "person"."id" when matched then delete returning "pet".*', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + + expect(result).to.eql(expected) + }) + + it('should perform a merge...using table simple on...when matched then delete returning {source}.* query', async () => { + const expected = await ctx.db + .selectFrom('pet') + .innerJoin('person', 'pet.owner_id', 'person.id') + .selectAll('person') + .execute() + + const query = ctx.db + .mergeInto('pet') + .using('person', 'pet.owner_id', 'person.id') + .whenMatched() + .thenDelete() + .returningAll('person') + + testSql(query, dialect, { + postgres: { + sql: 'merge into "pet" using "person" on "pet"."owner_id" = "person"."id" when matched then delete returning "person".*', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + + expect(result).to.eql(expected) + }) + + it('should perform a merge...using table simple on...when matched then delete returning merge_action(), {target}.name', async () => { + await ctx.db.connection().execute(async (db) => { + await ctx.db + .insertInto('person') + .values({ first_name: 'Moshe', gender: 'other' }) + .execute() + + await sql`SET session_replication_role = 'replica'`.execute(db) + await db + .insertInto('pet') + .values({ + name: 'Ralph', + owner_id: 9999, + species: 'hamster', + }) + .execute() + await sql`SET session_replication_role = 'origin'`.execute(db) + }) + + const query = ctx.db + .mergeInto('pet') + .using('person', 'pet.owner_id', 'person.id') + .whenMatched() + .thenUpdateSet( + 'name', + (eb) => sql`${eb.ref('person.first_name')} || '''s pet'`, + ) + .whenNotMatched() + .thenInsertValues((eb) => ({ + name: sql`${eb.ref('person.first_name')} || '''s pet'`, + owner_id: eb.ref('person.id'), + species: 'hamster', + })) + .whenNotMatchedBySource() + .thenDelete() + .returning([mergeAction().as('action'), 'pet.name']) + + testSql(query, dialect, { + postgres: { + sql: 'merge into "pet" using "person" on "pet"."owner_id" = "person"."id" when matched then update set "name" = "person"."first_name" || \'\'\'s pet\' when not matched then insert ("name", "owner_id", "species") values ("person"."first_name" || \'\'\'s pet\', "person"."id", $1) when not matched by source then delete returning merge_action() as "action", "pet"."name"', + parameters: ['hamster'], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + + expect(result).to.eql([ + { action: 'UPDATE', name: "Jennifer's pet" }, + { action: 'UPDATE', name: "Arnold's pet" }, + { action: 'UPDATE', name: "Sylvester's pet" }, + { action: 'DELETE', name: 'Ralph' }, + { action: 'INSERT', name: "Moshe's pet" }, + ]) + }) + } + if (dialect === 'mssql') { it('should perform a merge top...using table simple on...when matched then delete query', async () => { const query = ctx.db diff --git a/test/typings/test-d/merge.test-d.ts b/test/typings/test-d/merge.test-d.ts index bff938a03..3a86030e3 100644 --- a/test/typings/test-d/merge.test-d.ts +++ b/test/typings/test-d/merge.test-d.ts @@ -7,12 +7,14 @@ import { MergeQueryBuilder, MergeResult, NotMatchedThenableMergeQueryBuilder, + SelectType, Selectable, UpdateQueryBuilder, WheneableMergeQueryBuilder, + mergeAction, sql, } from '..' -import { Database, Person } from '../shared' +import { Database, Person, Pet } from '../shared' async function testMergeInto(db: Kysely) { db.mergeInto('person') @@ -422,35 +424,105 @@ async function testThenInsert( ) } -async function testOutput(db: Kysely) { - // One returning expression - const r1 = await db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .output('deleted.id') - .executeTakeFirst() +async function testReturning( + baseQuery: WheneableMergeQueryBuilder, +) { + // One returning expression, target table + const r1 = await baseQuery.returning('person.id').execute() + + expectType<{ id: number }[]>(r1) - expectType<{ id: number } | undefined>(r1) + // One returning expression, source table + const r2 = await baseQuery.returning('pet.name').execute() + + expectType<{ name: string }[]>(r2) // Multiple returning expressions - const r2 = await db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .output(['deleted.id', 'deleted.first_name as fn']) + const r3 = await baseQuery + .returning(['person.id', 'pet.name as pet_name']) .execute() - expectType<{ id: number; fn: string }[]>(r2) + expectType<{ id: number; pet_name: string }[]>(r3) // Non-column reference returning expressions - const r3 = await db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenUpdateSet('age', (eb) => eb(eb.ref('age'), '+', 20)) + const r4 = await baseQuery + .returning([ + 'person.age', + sql`concat(person.first_name, ' ', person.last_name)`.as( + 'full_name', + ), + ]) + .execute() + + expectType<{ age: number; full_name: string }[]>(r4) + + // Return all columns + const r5 = await baseQuery.returningAll().executeTakeFirstOrThrow() + + expectType<{ + [K in keyof Person | keyof Pet]: + | (K extends keyof Person ? SelectType : never) + | (K extends keyof Pet ? SelectType : never) + }>(r5) + + // Return all target columns + const r6 = await baseQuery.returningAll('person').executeTakeFirstOrThrow() + + expectType>(r6) + + // Return all source columns + const r7 = await baseQuery.returningAll('pet').executeTakeFirstOrThrow() + + expectType>(r7) + + // Return single merge_action + const r8 = await baseQuery.returning(mergeAction().as('action')).execute() + + expectType<{ action: 'INSERT' | 'UPDATE' | 'DELETE' }[]>(r8) + + // Return multi merge_action + const r9 = await baseQuery + .returning([mergeAction().as('action'), 'person.id']) + .execute() + + expectType<{ action: 'INSERT' | 'UPDATE' | 'DELETE'; id: number }[]>(r9) + + // Non-existent column + expectError(baseQuery.returning('not_column')) + expectError(baseQuery.returning('person.not_column')) + expectError(baseQuery.returning('pet.not_column')) + + // Non-existent prefix + expectError(baseQuery.returning('foo.age')) + expectError(baseQuery.returningAll('foo')) + + // unaliased merge_action + expectError(baseQuery.returning(mergeAction()).execute()) + expectError(baseQuery.returning([mergeAction(), 'person.id']).execute()) +} + +async function testOutput( + baseQuery: WheneableMergeQueryBuilder, +) { + // One returning expression, deleted values + const r1 = await baseQuery.output('deleted.id').execute() + + expectType<{ id: number }[]>(r1) + + // One returning expression, inserted values + const r2 = await baseQuery.output('inserted.id').execute() + + expectType<{ id: number }[]>(r2) + + // Multiple returning expressions + const r3 = await baseQuery + .output(['deleted.id', 'inserted.first_name as fn']) + .execute() + + expectType<{ id: number; fn: string }[]>(r3) + + // Non-column reference returning expressions + const r4 = await baseQuery .output([ 'inserted.age', sql`concat(deleted.first_name, ' ', deleted.last_name)`.as( @@ -459,66 +531,21 @@ async function testOutput(db: Kysely) { ]) .execute() - expectType<{ age: number; full_name: string }[]>(r3) + expectType<{ age: number; full_name: string }[]>(r4) // Return all columns - const r4 = await db - .mergeInto('person') - .using('pet', 'person.id', 'pet.owner_id') - .whenNotMatched() - .thenInsertValues({ - gender: 'female', - age: 15, - first_name: 'Jane', - }) - .outputAll('inserted') - .executeTakeFirstOrThrow() - - expectType>(r4) + const r5 = await baseQuery.outputAll('inserted').executeTakeFirstOrThrow() + + expectType>(r5) // Non-existent column - expectError( - db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .output('inserted.not_column'), - ) + expectError(baseQuery.output('inserted.not_column')) // Without prefix - expectError( - db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .output('age'), - ) - expectError( - db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .outputAll(), - ) + expectError(baseQuery.output('age')) + expectError(baseQuery.outputAll()) // Non-existent prefix - expectError( - db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .output('foo.age'), - ) - expectError( - db - .mergeInto('person') - .using('pet', 'pet.owner_id', 'person.id') - .whenMatched() - .thenDelete() - .outputAll('foo'), - ) + expectError(baseQuery.output('foo.age')) + expectError(baseQuery.outputAll('foo')) } From 8524c3cedb9b34398f6314a4287dfa8d67e9fe56 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 5 Jan 2025 13:58:29 +0200 Subject: [PATCH 15/28] feat: add HandleEmtpyInListsPlugin. (#925) * feat: empty where in plugin * test: add new tests * chore: remove unneccesary typeguards * fix: change to binary operator node * test: update tests to do both in and not in * test: for having * chore: rm test * test: nullable tests * chore: nit * chore: condense suite * chore: db config override * chore: extra console log * chore: empty arr plugin docs * HandleEmptyInListsPlugin initial commit. Co-authored-by: Austin Woon Quan <43132101+austinwoon@users.noreply.github.com> --------- Co-authored-by: Austin Woon Co-authored-by: igalklebanov remove only. --- site/docs/plugins.md | 6 +- src/index.ts | 2 + .../handle-empty-in-lists-plugin.ts | 171 ++++++++ .../handle-empty-in-lists-transformer.ts | 40 ++ .../handle-empty-in-lists.ts | 102 +++++ test/node/src/controlled-transaction.test.ts | 10 +- .../src/handle-empty-in-lists-plugin.test.ts | 406 ++++++++++++++++++ test/node/src/test-setup.ts | 7 +- test/node/src/transaction.test.ts | 10 +- 9 files changed, 740 insertions(+), 14 deletions(-) create mode 100644 src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts create mode 100644 src/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.ts create mode 100644 src/plugin/handle-empty-in-lists/handle-empty-in-lists.ts create mode 100644 test/node/src/handle-empty-in-lists-plugin.test.ts diff --git a/site/docs/plugins.md b/site/docs/plugins.md index f9f23230b..7b2b3269e 100644 --- a/site/docs/plugins.md +++ b/site/docs/plugins.md @@ -20,4 +20,8 @@ A plugin that converts snake_case identifiers in the database into camelCase in ### Deduplicate joins plugin -Plugin that removes duplicate joins from queries. You can read more about it in the [examples](/docs/recipes/deduplicate-joins) section or check the [API docs](https://kysely-org.github.io/kysely-apidoc/classes/DeduplicateJoinsPlugin.html). +A plugin that removes duplicate joins from queries. You can read more about it in the [examples](/docs/recipes/deduplicate-joins) section or check the [API docs](https://kysely-org.github.io/kysely-apidoc/classes/DeduplicateJoinsPlugin.html). + +### Handle `in ()` and `not in ()` plugin + +A plugin that allows handling `in ()` and `not in ()` with a chosen strategy. [Learn more](https://kysely-org.github.io/kysely-apidoc/classes/HandleEmptyWhereInListsPlugin.html). \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index b467d871c..6ff0e3a80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -110,6 +110,8 @@ export * from './plugin/camel-case/camel-case-plugin.js' export * from './plugin/deduplicate-joins/deduplicate-joins-plugin.js' export * from './plugin/with-schema/with-schema-plugin.js' export * from './plugin/parse-json-results/parse-json-results-plugin.js' +export * from './plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.js' +export * from './plugin/handle-empty-in-lists/handle-empty-in-lists.js' export * from './operation-node/add-column-node.js' export * from './operation-node/add-constraint-node.js' diff --git a/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts b/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts new file mode 100644 index 000000000..615b087e7 --- /dev/null +++ b/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts @@ -0,0 +1,171 @@ +import { QueryResult } from '../../driver/database-connection.js' +import { RootOperationNode } from '../../query-compiler/query-compiler.js' +import { + KyselyPlugin, + PluginTransformQueryArgs, + PluginTransformResultArgs, +} from '../kysely-plugin.js' +import { UnknownRow } from '../../util/type-utils.js' +import { HandleEmptyInListsTransformer } from './handle-empty-in-lists-transformer.js' +import { HandleEmptyInListsOptions } from './handle-empty-in-lists.js' + +/** + * A plugin that allows handling `in ()` and `not in ()` expressions. + * + * These expressions are invalid SQL syntax for many databases, and result in runtime + * database errors. + * + * The workarounds used by other libraries always involve modifying the query under + * the hood, which is not aligned with Kysely's philosophy of WYSIWYG. We recommend manually checking + * for empty arrays before passing them as arguments to `in` and `not in` expressions + * instead, but understand that this can be cumbersome. Hence we're going with an + * opt-in approach where you can choose if and how to handle these cases. We do + * not want to make this the default behavior, as it can lead to unexpected behavior. + * Use it at your own risk. Test it. Make sure it works as expected for you. + * + * Using this plugin also allows you to throw an error (thus avoiding unnecessary + * requests to the database) or print a warning in these cases. + * + * ### Examples + * + * The following strategy replaces the `in`/`not in` expression with a noncontingent + * expression. A contradiction (falsy) `1 = 0` for `in`, and a tautology (truthy) `1 = 1` for `not in`), + * similarily to how {@link https://github.com/knex/knex/blob/176151d8048b2a7feeb89a3d649a5580786d4f4e/docs/src/guide/query-builder.md#L1763 | Knex.js}, + * {@link https://github.com/prisma/prisma-engines/blob/99168c54187178484dae45d9478aa40cfd1866d2/quaint/src/visitor.rs#L804-L823 | PrismaORM}, + * {@link https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/Query/Grammars/Grammar.php#L284-L291 | Laravel}, + * {@link https://docs.sqlalchemy.org/en/13/core/engines.html#sqlalchemy.create_engine.params.empty_in_strategy | SQLAlchemy} + * handle this. + * + * ```ts + * import Sqlite from 'better-sqlite3' + * import { + * HandleEmptyInListsPlugin, + * Kysely, + * replaceWithNoncontingentExpression, + * SqliteDialect, + * } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module + * + * const db = new Kysely({ + * dialect: new SqliteDialect({ + * database: new Sqlite(':memory:'), + * }), + * plugins: [ + * new HandleEmptyInListsPlugin({ + * strategy: replaceWithNoncontingentExpression + * }) + * ], + * }) + * + * const results = await db + * .selectFrom('person') + * .where('id', 'in', []) + * .where('first_name', 'not in', []) + * .selectAll() + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * select * from "person" where 1 = 0 and 1 = 1 + * ``` + * + * The following strategy does the following: + * + * When `in`, pushes a `null` value into the empty list resulting in `in (null)`, + * similiarly to how {@link https://github.com/typeorm/typeorm/blob/0280cdc451c35ef73c830eb1191c95d34f6ce06e/src/query-builder/QueryBuilder.ts#L919-L922 | TypeORM} + * and {@link https://github.com/sequelize/sequelize/blob/0f2891c6897e12bf9bf56df344aae5b698f58c7d/packages/core/src/abstract-dialect/where-sql-builder.ts#L368-L379 | Sequelize} + * handle `in ()`. `in (null)` is logically the equivalent of `= null`, which returns + * `null`, which is a falsy expression in most SQL databases. We recommend NOT + * using this strategy if you plan to use `in` in `select`, `returning`, or `output` + * clauses, as the return type differs from the `SqlBool` default type for comparisons. + * + * When `not in`, casts the left operand as `char` and pushes a unique value into + * the empty list resulting in `cast({{lhs}} as char) not in ({{VALUE}})`. Casting + * is required to avoid database errors with non-string values. + * + * ```ts + * import Sqlite from 'better-sqlite3' + * import { + * HandleEmptyInListsPlugin, + * Kysely, + * pushValueIntoList, + * SqliteDialect + * } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module + * + * const db = new Kysely({ + * dialect: new SqliteDialect({ + * database: new Sqlite(':memory:'), + * }), + * plugins: [ + * new HandleEmptyInListsPlugin({ + * strategy: pushValueIntoList('__kysely_no_values_were_provided__') // choose a unique value for not in. has to be something with zero chance being in the data. + * }) + * ], + * }) + * + * const results = await db + * .selectFrom('person') + * .where('id', 'in', []) + * .where('first_name', 'not in', []) + * .selectAll() + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * select * from "person" where "id" in (null) and cast("first_name" as char) not in ('__kysely_no_values_were_provided__') + * ``` + * + * The following custom strategy throws an error when an empty list is encountered + * to avoid unnecessary requests to the database: + * + * ```ts + * import Sqlite from 'better-sqlite3' + * import { + * HandleEmptyInListsPlugin, + * Kysely, + * SqliteDialect + * } from 'kysely' + * import type { Database } from 'type-editor' // imaginary module + * + * const db = new Kysely({ + * dialect: new SqliteDialect({ + * database: new Sqlite(':memory:'), + * }), + * plugins: [ + * new HandleEmptyInListsPlugin({ + * strategy: () => { + * throw new Error('Empty in/not-in is not allowed') + * } + * }) + * ], + * }) + * + * const results = await db + * .selectFrom('person') + * .where('id', 'in', []) + * .selectAll() + * .execute() // throws an error with 'Empty in/not-in is not allowed' message! + * ``` + */ +export class HandleEmptyInListsPlugin implements KyselyPlugin { + readonly #transformer: HandleEmptyInListsTransformer + + constructor(readonly opt: HandleEmptyInListsOptions) { + this.#transformer = new HandleEmptyInListsTransformer(opt.strategy) + } + + transformQuery(args: PluginTransformQueryArgs): RootOperationNode { + return this.#transformer.transformNode(args.node) + } + + async transformResult( + args: PluginTransformResultArgs, + ): Promise> { + return args.result + } +} diff --git a/src/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.ts b/src/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.ts new file mode 100644 index 000000000..b69445738 --- /dev/null +++ b/src/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.ts @@ -0,0 +1,40 @@ +import { BinaryOperationNode } from '../../operation-node/binary-operation-node.js' +import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js' +import { PrimitiveValueListNode } from '../../operation-node/primitive-value-list-node.js' +import { OperatorNode } from '../../operation-node/operator-node.js' +import { + EmptyInListNode, + EmptyInListsStrategy, +} from './handle-empty-in-lists.js' +import { ValueListNode } from '../../operation-node/value-list-node.js' + +export class HandleEmptyInListsTransformer extends OperationNodeTransformer { + readonly #strategy: EmptyInListsStrategy + + constructor(strategy: EmptyInListsStrategy) { + super() + this.#strategy = strategy + } + + protected transformBinaryOperation( + node: BinaryOperationNode, + ): BinaryOperationNode { + if (this.#isEmptyInListNode(node)) { + return this.#strategy(node) + } + + return node + } + + #isEmptyInListNode(node: BinaryOperationNode): node is EmptyInListNode { + const { operator, rightOperand } = node + + return ( + (PrimitiveValueListNode.is(rightOperand) || + ValueListNode.is(rightOperand)) && + rightOperand.values.length === 0 && + OperatorNode.is(operator) && + (operator.operator === 'in' || operator.operator === 'not in') + ) + } +} diff --git a/src/plugin/handle-empty-in-lists/handle-empty-in-lists.ts b/src/plugin/handle-empty-in-lists/handle-empty-in-lists.ts new file mode 100644 index 000000000..cb8514b6a --- /dev/null +++ b/src/plugin/handle-empty-in-lists/handle-empty-in-lists.ts @@ -0,0 +1,102 @@ +import { BinaryOperationNode } from '../../operation-node/binary-operation-node.js' +import { CastNode } from '../../operation-node/cast-node.js' +import { DataTypeNode } from '../../operation-node/data-type-node.js' +import { OperatorNode } from '../../operation-node/operator-node.js' +import { ParensNode } from '../../operation-node/parens-node.js' +import { PrimitiveValueListNode } from '../../operation-node/primitive-value-list-node.js' +import { ValueListNode } from '../../operation-node/value-list-node.js' +import { ValueNode } from '../../operation-node/value-node.js' +import { freeze } from '../../util/object-utils.js' + +export interface HandleEmptyInListsOptions { + /** + * The strategy to use when handling `in ()` and `not in ()`. + * + * See {@link HandleEmptyInListsPlugin} for examples. + */ + strategy: EmptyInListsStrategy +} + +export type EmptyInListNode = BinaryOperationNode & { + operator: OperatorNode & { + operator: 'in' | 'not in' + } + rightOperand: (ValueListNode | PrimitiveValueListNode) & { + values: Readonly<[]> + } +} + +export type EmptyInListsStrategy = ( + node: EmptyInListNode, +) => BinaryOperationNode + +let contradiction: BinaryOperationNode +let eq: OperatorNode +let one: ValueNode +let tautology: BinaryOperationNode +/** + * Replaces the `in`/`not in` expression with a noncontingent expression (always true or always + * false) depending on the original operator. + * + * This is how Knex.js, PrismaORM, Laravel, and SQLAlchemy handle `in ()` and `not in ()`. + * + * See {@link pushValueIntoList} for an alternative strategy. + */ +export function replaceWithNoncontingentExpression( + node: EmptyInListNode, +): BinaryOperationNode { + const _one = (one ||= ValueNode.createImmediate(1)) + const _eq = (eq ||= OperatorNode.create('=')) + + if (node.operator.operator === 'in') { + return (contradiction ||= BinaryOperationNode.create( + _one, + _eq, + ValueNode.createImmediate(0), + )) + } + + return (tautology ||= BinaryOperationNode.create(_one, _eq, _one)) +} + +let char: DataTypeNode +let listNull: ValueListNode +let listVal: ValueListNode +/** + * When `in`, pushes a `null` value into the list resulting in `in (null)`. This + * is how TypeORM and Sequelize handle `in ()`. `in (null)` is logically the equivalent + * of `= null`, which returns `null`, which is a falsy expression in most SQL databases. + * We recommend NOT using this strategy if you plan to use `in` in `select`, `returning`, + * or `output` clauses, as the return type differs from the `SqlBool` default type. + * + * When `not in`, casts the left operand as `char` and pushes a literal value into + * the list resulting in `cast({{lhs}} as char) not in ({{VALUE}})`. Casting + * is required to avoid database errors with non-string columns. + * + * See {@link replaceWithNoncontingentExpression} for an alternative strategy. + */ +export function pushValueIntoList( + uniqueNotInLiteral: '__kysely_no_values_were_provided__' | (string & {}), +): EmptyInListsStrategy { + return function pushValueIntoList(node) { + if (node.operator.operator === 'in') { + return freeze({ + ...node, + rightOperand: (listNull ||= ValueListNode.create([ + ValueNode.createImmediate(null), + ])), + }) + } + + return freeze({ + ...node, + leftOperand: CastNode.create( + node.leftOperand, + (char ||= DataTypeNode.create('char')), + ), + rightOperand: (listVal ||= ValueListNode.create([ + ValueNode.createImmediate(uniqueNotInLiteral), + ])), + }) + } +} diff --git a/test/node/src/controlled-transaction.test.ts b/test/node/src/controlled-transaction.test.ts index 8ac61ea15..48ed2a75e 100644 --- a/test/node/src/controlled-transaction.test.ts +++ b/test/node/src/controlled-transaction.test.ts @@ -44,10 +44,12 @@ for (const dialect of DIALECTS) { > before(async function () { - ctx = await initTest(this, dialect, (event) => { - if (event.level === 'query') { - executedQueries.push(event.query) - } + ctx = await initTest(this, dialect, { + log(event) { + if (event.level === 'query') { + executedQueries.push(event.query) + } + }, }) }) diff --git a/test/node/src/handle-empty-in-lists-plugin.test.ts b/test/node/src/handle-empty-in-lists-plugin.test.ts new file mode 100644 index 000000000..38d5ee9ce --- /dev/null +++ b/test/node/src/handle-empty-in-lists-plugin.test.ts @@ -0,0 +1,406 @@ +import { + HandleEmptyInListsPlugin, + pushValueIntoList, + replaceWithNoncontingentExpression, +} from '../../../dist/cjs/index.js' +import { + destroyTest, + initTest, + TestContext, + testSql, + expect, + DIALECTS, + insertDefaultDataSet, + BuiltInDialect, + NOT_SUPPORTED, + clearDatabase, +} from './test-setup.js' + +const fixtures = [ + { + strategy: replaceWithNoncontingentExpression, + replaceIn: (_lhs: string) => '1 = 0', + inReturnValue: (dialect: BuiltInDialect) => + ({ + [dialect]: false, + mysql: '0', + sqlite: 0, + })[dialect], + replaceNotIn: (_lhs: string) => '1 = 1', + notInReturnValue: (dialect: BuiltInDialect) => + ({ + [dialect]: true, + mysql: '1', + sqlite: 1, + })[dialect], + }, + { + strategy: pushValueIntoList('__kysely_no_values_were_provided__'), + replaceIn: (lhs: string) => `${lhs} in (null)`, + inReturnValue: () => null, + replaceNotIn: (lhs: string) => + `cast(${lhs} as char) not in ('__kysely_no_values_were_provided__')`, + notInReturnValue: (dialect: BuiltInDialect) => + ({ + [dialect]: true, + mysql: '1', + sqlite: 1, + })[dialect], + }, +] as const + +for (const dialect of DIALECTS) { + describe(`${dialect}: handle empty in lists plugin`, () => { + for (const fixture of fixtures) { + describe(`strategy: ${fixture.strategy.name}`, () => { + let ctx: TestContext + + before(async function () { + ctx = await initTest(this, dialect, { + plugins: [ + new HandleEmptyInListsPlugin({ strategy: fixture.strategy }), + ], + }) + }) + + beforeEach(async () => { + await insertDefaultDataSet(ctx) + }) + + afterEach(async () => { + await clearDatabase(ctx) + }) + + after(async () => { + await destroyTest(ctx) + }) + + it('should handle `select ... where {{string_ref}} in ()`', async () => { + const query = ctx.db + .selectFrom('person') + .where('first_name', 'in', []) + .select('first_name') + + testSql(query, dialect, { + postgres: { + sql: `select "first_name" from "person" where ${fixture.replaceIn('"first_name"')}`, + parameters: [], + }, + mysql: { + sql: `select \`first_name\` from \`person\` where ${fixture.replaceIn('`first_name`')}`, + parameters: [], + }, + mssql: { + sql: `select "first_name" from "person" where ${fixture.replaceIn('"first_name"')}`, + parameters: [], + }, + sqlite: { + sql: `select "first_name" from "person" where ${fixture.replaceIn('"first_name"')}`, + parameters: [], + }, + }) + + const result = await query.execute() + + expect(result).to.have.lengthOf(0) + }) + + it('should handle `select ... where {{string_ref}} not in ()`', async () => { + const query = ctx.db + .selectFrom('person') + .where('first_name', 'not in', []) + .select('first_name') + + testSql(query, dialect, { + postgres: { + sql: `select "first_name" from "person" where ${fixture.replaceNotIn('"first_name"')}`, + parameters: [], + }, + mysql: { + sql: `select \`first_name\` from \`person\` where ${fixture.replaceNotIn('`first_name`')}`, + parameters: [], + }, + mssql: { + sql: `select "first_name" from "person" where ${fixture.replaceNotIn('"first_name"')}`, + parameters: [], + }, + sqlite: { + sql: `select "first_name" from "person" where ${fixture.replaceNotIn('"first_name"')}`, + parameters: [], + }, + }) + + const result = await query.execute() + + expect(result).to.have.lengthOf(3) + }) + + it('should handle `select ... where {{number_ref}} in ()`', async () => { + const result = await ctx.db + .selectFrom('person') + .where('children', 'in', []) + .select('children') + .execute() + + expect(result).to.have.lengthOf(0) + }) + + it('should handle `select ... where {{number_ref}} not in ()`', async () => { + const result = await ctx.db + .selectFrom('person') + .where('children', 'not in', []) + .select('children') + .execute() + + expect(result).to.have.lengthOf(3) + }) + + it('should handle `select ... having ... in ()`', async () => { + const query = ctx.db + .selectFrom('person') + .groupBy('first_name') + .having('first_name', 'in', []) + .select('first_name') + + testSql(query, dialect, { + postgres: { + sql: `select "first_name" from "person" group by "first_name" having ${fixture.replaceIn('"first_name"')}`, + parameters: [], + }, + mysql: { + sql: `select \`first_name\` from \`person\` group by \`first_name\` having ${fixture.replaceIn('`first_name`')}`, + parameters: [], + }, + mssql: { + sql: `select "first_name" from "person" group by "first_name" having ${fixture.replaceIn('"first_name"')}`, + parameters: [], + }, + sqlite: { + sql: `select "first_name" from "person" group by "first_name" having ${fixture.replaceIn('"first_name"')}`, + parameters: [], + }, + }) + + const result = await query.execute() + + expect(result).to.have.lengthOf(0) + }) + + it('should handle `select ... having ... not in ()`', async () => { + const query = ctx.db + .selectFrom('person') + .groupBy('first_name') + .having('first_name', 'not in', []) + .select('first_name') + + testSql(query, dialect, { + postgres: { + sql: `select "first_name" from "person" group by "first_name" having ${fixture.replaceNotIn('"first_name"')}`, + parameters: [], + }, + mysql: { + sql: `select \`first_name\` from \`person\` group by \`first_name\` having ${fixture.replaceNotIn('`first_name`')}`, + parameters: [], + }, + mssql: { + sql: `select "first_name" from "person" group by "first_name" having ${fixture.replaceNotIn('"first_name"')}`, + parameters: [], + }, + sqlite: { + sql: `select "first_name" from "person" group by "first_name" having ${fixture.replaceNotIn('"first_name"')}`, + parameters: [], + }, + }) + + const result = await query.execute() + + expect(result).to.have.lengthOf(3) + }) + + if ( + dialect === 'mysql' || + dialect === 'postgres' || + dialect === 'sqlite' + ) { + it('should handle `select ... in (), ... not in ()`', async () => { + const query = ctx.db + .selectFrom('person') + .select((eb) => [ + eb('first_name', 'in', []).as('in'), + eb('first_name', 'not in', []).as('not_in'), + ]) + + testSql(query, dialect, { + postgres: { + sql: `select ${fixture.replaceIn('"first_name"')} as "in", ${fixture.replaceNotIn('"first_name"')} as "not_in" from "person"`, + parameters: [], + }, + mysql: { + sql: `select ${fixture.replaceIn('`first_name`')} as \`in\`, ${fixture.replaceNotIn('`first_name`')} as \`not_in\` from \`person\``, + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: `select ${fixture.replaceIn('"first_name"')} as "in", ${fixture.replaceNotIn('"first_name"')} as "not_in" from "person"`, + parameters: [], + }, + }) + + const result = await query.execute() + + expect(result).to.deep.equal( + new Array(3).fill({ + in: fixture.inReturnValue(dialect), + not_in: fixture.notInReturnValue(dialect), + }), + ) + }) + } + + it('should handle `update ... where ... in ()`', async () => { + const query = ctx.db + .updateTable('person') + .set('first_name', 'Tesla') + .where('id', 'in', []) + + testSql(query, dialect, { + postgres: { + sql: `update "person" set "first_name" = $1 where ${fixture.replaceIn('"id"')}`, + parameters: ['Tesla'], + }, + mysql: { + sql: `update \`person\` set \`first_name\` = ? where ${fixture.replaceIn('`id`')}`, + parameters: ['Tesla'], + }, + mssql: { + sql: `update "person" set "first_name" = @1 where ${fixture.replaceIn('"id"')}`, + parameters: ['Tesla'], + }, + sqlite: { + sql: `update "person" set "first_name" = ? where ${fixture.replaceIn('"id"')}`, + parameters: ['Tesla'], + }, + }) + + const result = await query.executeTakeFirstOrThrow() + + expect(result.numUpdatedRows).to.equal(0n) + }) + + it('should handle `update ... where ... not in ()`', async () => { + const query = ctx.db + .updateTable('person') + .set('first_name', 'John') + .where('id', 'not in', []) + + testSql(query, dialect, { + postgres: { + sql: `update "person" set "first_name" = $1 where ${fixture.replaceNotIn('"id"')}`, + parameters: ['John'], + }, + mysql: { + sql: `update \`person\` set \`first_name\` = ? where ${fixture.replaceNotIn('`id`')}`, + parameters: ['John'], + }, + mssql: { + sql: `update "person" set "first_name" = @1 where ${fixture.replaceNotIn('"id"')}`, + parameters: ['John'], + }, + sqlite: { + sql: `update "person" set "first_name" = ? where ${fixture.replaceNotIn('"id"')}`, + parameters: ['John'], + }, + }) + + const result = await query.executeTakeFirstOrThrow() + + expect(result.numUpdatedRows).to.equal(3n) + }) + + it('should handle `delete ... where ... in ()`', async () => { + const query = ctx.db.deleteFrom('person').where('id', 'in', []) + + testSql(query, dialect, { + postgres: { + sql: `delete from "person" where ${fixture.replaceIn('"id"')}`, + parameters: [], + }, + mysql: { + sql: `delete from \`person\` where ${fixture.replaceIn('`id`')}`, + parameters: [], + }, + mssql: { + sql: `delete from "person" where ${fixture.replaceIn('"id"')}`, + parameters: [], + }, + sqlite: { + sql: `delete from "person" where ${fixture.replaceIn('"id"')}`, + parameters: [], + }, + }) + + const result = await query.executeTakeFirstOrThrow() + + expect(result.numDeletedRows).to.equal(0n) + }) + + it('should handle `delete ... where ... not in ()`', async () => { + const query = ctx.db.deleteFrom('person').where('id', 'not in', []) + + testSql(query, dialect, { + postgres: { + sql: `delete from "person" where ${fixture.replaceNotIn('"id"')}`, + parameters: [], + }, + mysql: { + sql: `delete from \`person\` where ${fixture.replaceNotIn('`id`')}`, + parameters: [], + }, + mssql: { + sql: `delete from "person" where ${fixture.replaceNotIn('"id"')}`, + parameters: [], + }, + sqlite: { + sql: `delete from "person" where ${fixture.replaceNotIn('"id"')}`, + parameters: [], + }, + }) + + const result = await query.executeTakeFirstOrThrow() + + expect(result.numDeletedRows).to.equal(3n) + }) + + it('should not affect queries with non-empty lists', async () => { + const query = ctx.db + .selectFrom('person') + .where('first_name', 'in', ['Jennifer']) + .select('first_name') + + testSql(query, dialect, { + postgres: { + sql: 'select "first_name" from "person" where "first_name" in ($1)', + parameters: ['Jennifer'], + }, + mysql: { + sql: 'select `first_name` from `person` where `first_name` in (?)', + parameters: ['Jennifer'], + }, + mssql: { + sql: 'select "first_name" from "person" where "first_name" in (@1)', + parameters: ['Jennifer'], + }, + sqlite: { + sql: 'select "first_name" from "person" where "first_name" in (?)', + parameters: ['Jennifer'], + }, + }) + + const result = await query.execute() + + expect(result).to.deep.equal([{ first_name: 'Jennifer' }]) + }) + }) + } + }) +} diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 0c1e866e7..e4d4951cc 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -217,15 +217,12 @@ export const DB_CONFIGS: PerDialect = { export async function initTest( ctx: Mocha.Context, dialect: BuiltInDialect, - log?: Logger, + overrides?: Omit, ): Promise { const config = DB_CONFIGS[dialect] ctx.timeout(TEST_INIT_TIMEOUT) - const db = await connect({ - ...config, - log, - }) + const db = await connect({ ...config, ...overrides }) await createDatabase(db, dialect) return { config, db, dialect } diff --git a/test/node/src/transaction.test.ts b/test/node/src/transaction.test.ts index a0e6e4a30..1c3f09498 100644 --- a/test/node/src/transaction.test.ts +++ b/test/node/src/transaction.test.ts @@ -30,10 +30,12 @@ for (const dialect of DIALECTS) { > before(async function () { - ctx = await initTest(this, dialect, (event) => { - if (event.level === 'query') { - executedQueries.push(event.query) - } + ctx = await initTest(this, dialect, { + log(event) { + if (event.level === 'query') { + executedQueries.push(event.query) + } + }, }) }) From 5b546fff3fa4ed4b3e8fb6463354a8fbae182f71 Mon Sep 17 00:00:00 2001 From: Sam Clearman Date: Sun, 5 Jan 2025 14:55:18 -0800 Subject: [PATCH 16/28] Add `Date` as a valid return type for `max` and `min` (#1062) --- src/query-builder/function-module.ts | 18 ++++++++++++++---- test/node/src/test-setup.ts | 1 - .../test-d/aggregate-function.test-d.ts | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/query-builder/function-module.ts b/src/query-builder/function-module.ts index cfb7cda3c..028e27500 100644 --- a/src/query-builder/function-module.ts +++ b/src/query-builder/function-module.ts @@ -487,7 +487,7 @@ export interface FunctionModule { * ``` */ max< - O extends number | string | bigint | null = never, + O extends number | string | Date | bigint | null = never, RE extends ReferenceExpression = ReferenceExpression, >( expr: RE, @@ -495,7 +495,12 @@ export interface FunctionModule { DB, TB, IsNever extends true - ? ExtractTypeFromReferenceExpression + ? ExtractTypeFromReferenceExpression< + DB, + TB, + RE, + number | string | Date | bigint + > : O > @@ -538,7 +543,7 @@ export interface FunctionModule { * ``` */ min< - O extends number | string | bigint | null = never, + O extends number | string | Date | bigint | null = never, RE extends ReferenceExpression = ReferenceExpression, >( expr: RE, @@ -546,7 +551,12 @@ export interface FunctionModule { DB, TB, IsNever extends true - ? ExtractTypeFromReferenceExpression + ? ExtractTypeFromReferenceExpression< + DB, + TB, + RE, + number | string | Date | bigint + > : O > diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index e4d4951cc..1c6fce5ef 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -29,7 +29,6 @@ import { InsertResult, SqliteDialect, InsertQueryBuilder, - Logger, Generated, sql, ColumnType, diff --git a/test/typings/test-d/aggregate-function.test-d.ts b/test/typings/test-d/aggregate-function.test-d.ts index 04323f6dc..4a35fd24a 100644 --- a/test/typings/test-d/aggregate-function.test-d.ts +++ b/test/typings/test-d/aggregate-function.test-d.ts @@ -339,13 +339,13 @@ async function testSelectWithDynamicReference(db: Kysely) { expectNotAssignable(result.count) expectAssignable(result.another_count) expectNotAssignable(result.another_count) - expectAssignable(result.max) + expectAssignable(result.max) expectNotAssignable(result.max) expectAssignable(result.another_max) expectNotAssignable(result.another_max) expectAssignable(result.nullable_max) expectNotAssignable(result.nullable_max) - expectAssignable(result.min) + expectAssignable(result.min) expectNotAssignable(result.min) expectAssignable(result.another_min) expectNotAssignable(result.another_min) From 7d2698fe435ec59258e0cbde239f21803219064f Mon Sep 17 00:00:00 2001 From: vincentiusvin <54709710+vincentiusvin@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:08:36 +0700 Subject: [PATCH 17/28] SQLite's OR CONFLICT clause for inserts (#976) Co-authored-by: vincentiusvin <54709710+vincentiusvin@users.noreply.github.com> Co-authored-by: Igal Klebanov --- src/dialect/sqlite/sqlite-query-compiler.ts | 6 + src/index.ts | 1 + src/operation-node/insert-query-node.ts | 4 + .../operation-node-transformer.ts | 8 + src/operation-node/operation-node-visitor.ts | 3 + src/operation-node/operation-node.ts | 1 + src/operation-node/or-action-node.ts | 23 ++ src/query-builder/insert-query-builder.ts | 206 +++++++++- src/query-compiler/default-query-compiler.ts | 15 + src/query-creator.ts | 18 +- test/node/src/insert.test.ts | 49 ++- test/node/src/replace.test.ts | 362 +++++++++--------- 12 files changed, 509 insertions(+), 187 deletions(-) create mode 100644 src/operation-node/or-action-node.ts diff --git a/src/dialect/sqlite/sqlite-query-compiler.ts b/src/dialect/sqlite/sqlite-query-compiler.ts index 0fb97399a..5272842af 100644 --- a/src/dialect/sqlite/sqlite-query-compiler.ts +++ b/src/dialect/sqlite/sqlite-query-compiler.ts @@ -1,9 +1,15 @@ import { DefaultInsertValueNode } from '../../operation-node/default-insert-value-node.js' +import { OrActionNode } from '../../operation-node/or-action-node.js' import { DefaultQueryCompiler } from '../../query-compiler/default-query-compiler.js' const ID_WRAP_REGEX = /"/g export class SqliteQueryCompiler extends DefaultQueryCompiler { + protected override visitOrAction(node: OrActionNode): void { + this.append('or ') + this.append(node.action) + } + protected override getCurrentParameterPlaceholder() { return '?' } diff --git a/src/index.ts b/src/index.ts index 6ff0e3a80..d95d35cc7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -178,6 +178,7 @@ export * from './operation-node/operation-node-transformer.js' export * from './operation-node/operation-node-visitor.js' export * from './operation-node/operation-node.js' export * from './operation-node/operator-node.js' +export * from './operation-node/or-action-node.js' export * from './operation-node/or-node.js' export * from './operation-node/order-by-item-node.js' export * from './operation-node/order-by-node.js' diff --git a/src/operation-node/insert-query-node.ts b/src/operation-node/insert-query-node.ts index a079f2e15..e0d455e0b 100644 --- a/src/operation-node/insert-query-node.ts +++ b/src/operation-node/insert-query-node.ts @@ -4,6 +4,7 @@ import { ExplainNode } from './explain-node.js' import { OnConflictNode } from './on-conflict-node.js' import { OnDuplicateKeyNode } from './on-duplicate-key-node.js' import { OperationNode } from './operation-node.js' +import { OrActionNode } from './or-action-node.js' import { OutputNode } from './output-node.js' import { ReturningNode } from './returning-node.js' import { TableNode } from './table-node.js' @@ -21,7 +22,10 @@ export interface InsertQueryNode extends OperationNode { readonly onConflict?: OnConflictNode readonly onDuplicateKey?: OnDuplicateKeyNode readonly with?: WithNode + // TODO: remove in 0.29 + /** @deprecated use {@link orAction} instead. */ readonly ignore?: boolean + readonly orAction?: OrActionNode readonly replace?: boolean readonly explain?: ExplainNode readonly defaultValues?: boolean diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index 1fcf5ea83..a1ce5584f 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -95,6 +95,7 @@ import { FetchNode } from './fetch-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' +import { OrActionNode } from './or-action-node.js' /** * Transforms an operation node tree into another one. @@ -231,6 +232,7 @@ export class OperationNodeTransformer { FetchNode: this.transformFetch.bind(this), TopNode: this.transformTop.bind(this), OutputNode: this.transformOutput.bind(this), + OrActionNode: this.transformOrAction.bind(this), }) transformNode(node: T): T { @@ -392,6 +394,7 @@ export class OperationNodeTransformer { endModifiers: this.transformNodeList(node.endModifiers), with: this.transformNode(node.with), ignore: node.ignore, + orAction: this.transformNode(node.orAction), replace: node.replace, explain: this.transformNode(node.explain), defaultValues: node.defaultValues, @@ -1131,4 +1134,9 @@ export class OperationNodeTransformer { // An Object.freezed leaf node. No need to clone. return node } + + protected transformOrAction(node: OrActionNode): OrActionNode { + // An Object.freezed leaf node. No need to clone. + return node + } } diff --git a/src/operation-node/operation-node-visitor.ts b/src/operation-node/operation-node-visitor.ts index 533f58098..70d308ec0 100644 --- a/src/operation-node/operation-node-visitor.ts +++ b/src/operation-node/operation-node-visitor.ts @@ -97,6 +97,7 @@ import { FetchNode } from './fetch-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' +import { OrActionNode } from './or-action-node.js' export abstract class OperationNodeVisitor { protected readonly nodeStack: OperationNode[] = [] @@ -201,6 +202,7 @@ export abstract class OperationNodeVisitor { FetchNode: this.visitFetch.bind(this), TopNode: this.visitTop.bind(this), OutputNode: this.visitOutput.bind(this), + OrActionNode: this.visitOrAction.bind(this), }) protected readonly visitNode = (node: OperationNode): void => { @@ -315,4 +317,5 @@ export abstract class OperationNodeVisitor { protected abstract visitFetch(node: FetchNode): void protected abstract visitTop(node: TopNode): void protected abstract visitOutput(node: OutputNode): void + protected abstract visitOrAction(node: OrActionNode): void } diff --git a/src/operation-node/operation-node.ts b/src/operation-node/operation-node.ts index 7786b7ec1..a893a3951 100644 --- a/src/operation-node/operation-node.ts +++ b/src/operation-node/operation-node.ts @@ -93,6 +93,7 @@ export type OperationNodeKind = | 'FetchNode' | 'TopNode' | 'OutputNode' + | 'OrActionNode' export interface OperationNode { readonly kind: OperationNodeKind diff --git a/src/operation-node/or-action-node.ts b/src/operation-node/or-action-node.ts new file mode 100644 index 000000000..eaad93705 --- /dev/null +++ b/src/operation-node/or-action-node.ts @@ -0,0 +1,23 @@ +import { freeze } from '../util/object-utils.js' +import { OperationNode } from './operation-node.js' + +export interface OrActionNode extends OperationNode { + readonly kind: 'OrActionNode' + readonly action: string +} + +/** + * @internal + */ +export const OrActionNode = freeze({ + is(node: OperationNode): node is OrActionNode { + return node.kind === 'OrActionNode' + }, + + create(action: string): OrActionNode { + return freeze({ + kind: 'OrActionNode', + action, + }) + }, +}) diff --git a/src/query-builder/insert-query-builder.ts b/src/query-builder/insert-query-builder.ts index 2d6b481c1..c8ebfbc66 100644 --- a/src/query-builder/insert-query-builder.ts +++ b/src/query-builder/insert-query-builder.ts @@ -66,6 +66,7 @@ import { SelectExpressionFromOutputCallback, SelectExpressionFromOutputExpression, } from './output-interface.js' +import { OrActionNode } from '../operation-node/or-action-node.js' export class InsertQueryBuilder implements @@ -412,15 +413,18 @@ export class InsertQueryBuilder /** * Changes an `insert into` query to an `insert ignore into` query. * + * This is only supported by some dialects like MySQL. + * + * To avoid a footgun, when invoked with the SQLite dialect, this method will + * be handled like {@link orIgnore}. See also, {@link orAbort}, {@link orFail}, + * {@link orReplace}, and {@link orRollback}. + * * If you use the ignore modifier, ignorable errors that occur while executing the * insert statement are ignored. For example, without ignore, a row that duplicates * an existing unique index or primary key value in the table causes a duplicate-key * error and the statement is aborted. With ignore, the row is discarded and no error * occurs. * - * This is only supported on some dialects like MySQL. On most dialects you should - * use the {@link onConflict} method. - * * ### Examples * * ```ts @@ -437,14 +441,206 @@ export class InsertQueryBuilder * The generated SQL (MySQL): * * ```sql - * insert ignore into `person` ("first_name", "last_name", "gender") values (?, ?, ?) + * insert ignore into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?) + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * insert or ignore into "person" ("first_name", "last_name", "gender") values (?, ?, ?) * ``` */ ignore(): InsertQueryBuilder { return new InsertQueryBuilder({ ...this.#props, queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, { - ignore: true, + orAction: OrActionNode.create('ignore'), + }), + }) + } + + /** + * Changes an `insert into` query to an `insert or ignore into` query. + * + * This is only supported by some dialects like SQLite. + * + * To avoid a footgun, when invoked with the MySQL dialect, this method will + * be handled like {@link ignore}. + * + * See also, {@link orAbort}, {@link orFail}, {@link orReplace}, and {@link orRollback}. + * + * ### Examples + * + * ```ts + * await db.insertInto('person') + * .orIgnore() + * .values({ + * first_name: 'John', + * last_name: 'Doe', + * gender: 'female', + * }) + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * insert or ignore into "person" ("first_name", "last_name", "gender") values (?, ?, ?) + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * insert ignore into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?) + * ``` + */ + orIgnore(): InsertQueryBuilder { + return new InsertQueryBuilder({ + ...this.#props, + queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, { + orAction: OrActionNode.create('ignore'), + }), + }) + } + + /** + * Changes an `insert into` query to an `insert or abort into` query. + * + * This is only supported by some dialects like SQLite. + * + * See also, {@link orIgnore}, {@link orFail}, {@link orReplace}, and {@link orRollback}. + * + * ### Examples + * + * ```ts + * await db.insertInto('person') + * .orAbort() + * .values({ + * first_name: 'John', + * last_name: 'Doe', + * gender: 'female', + * }) + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * insert or abort into "person" ("first_name", "last_name", "gender") values (?, ?, ?) + * ``` + */ + orAbort(): InsertQueryBuilder { + return new InsertQueryBuilder({ + ...this.#props, + queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, { + orAction: OrActionNode.create('abort'), + }), + }) + } + + /** + * Changes an `insert into` query to an `insert or fail into` query. + * + * This is only supported by some dialects like SQLite. + * + * See also, {@link orIgnore}, {@link orAbort}, {@link orReplace}, and {@link orRollback}. + * + * ### Examples + * + * ```ts + * await db.insertInto('person') + * .orFail() + * .values({ + * first_name: 'John', + * last_name: 'Doe', + * gender: 'female', + * }) + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * insert or fail into "person" ("first_name", "last_name", "gender") values (?, ?, ?) + * ``` + */ + orFail(): InsertQueryBuilder { + return new InsertQueryBuilder({ + ...this.#props, + queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, { + orAction: OrActionNode.create('fail'), + }), + }) + } + + /** + * Changes an `insert into` query to an `insert or replace into` query. + * + * This is only supported by some dialects like SQLite. + * + * You can also use {@link Kysely.replaceInto} to achieve the same result. + * + * See also, {@link orIgnore}, {@link orAbort}, {@link orFail}, and {@link orRollback}. + * + * ### Examples + * + * ```ts + * await db.insertInto('person') + * .orReplace() + * .values({ + * first_name: 'John', + * last_name: 'Doe', + * gender: 'female', + * }) + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * insert or replace into "person" ("first_name", "last_name", "gender") values (?, ?, ?) + * ``` + */ + orReplace(): InsertQueryBuilder { + return new InsertQueryBuilder({ + ...this.#props, + queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, { + orAction: OrActionNode.create('replace'), + }), + }) + } + + /** + * Changes an `insert into` query to an `insert or rollback into` query. + * + * This is only supported by some dialects like SQLite. + * + * See also, {@link orIgnore}, {@link orAbort}, {@link orFail}, and {@link orReplace}. + * + * ### Examples + * + * ```ts + * await db.insertInto('person') + * .orRollback() + * .values({ + * first_name: 'John', + * last_name: 'Doe', + * gender: 'female', + * }) + * .execute() + * ``` + * + * The generated SQL (SQLite): + * + * ```sql + * insert or rollback into "person" ("first_name", "last_name", "gender") values (?, ?, ?) + * ``` + */ + orRollback(): InsertQueryBuilder { + return new InsertQueryBuilder({ + ...this.#props, + queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, { + orAction: OrActionNode.create('rollback'), }), }) } diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index c0267e1e2..559ed112b 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -112,6 +112,8 @@ import { FetchNode } from '../operation-node/fetch-node.js' import { TopNode } from '../operation-node/top-node.js' import { OutputNode } from '../operation-node/output-node.js' import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' +import { OrActionNode } from '../operation-node/or-action-node.js' +import { logOnce } from '../util/log-once.js' export class DefaultQueryCompiler extends OperationNodeVisitor @@ -311,10 +313,19 @@ export class DefaultQueryCompiler this.append(node.replace ? 'replace' : 'insert') + // TODO: remove in 0.29. if (node.ignore) { + logOnce( + '`InsertQueryNode.ignore` is deprecated. Use `InsertQueryNode.orAction` instead.', + ) this.append(' ignore') } + if (node.orAction) { + this.append(' ') + this.visitNode(node.orAction) + } + if (node.top) { this.append(' ') this.visitNode(node.top) @@ -1663,6 +1674,10 @@ export class DefaultQueryCompiler } } + protected override visitOrAction(node: OrActionNode): void { + this.append(node.action) + } + protected append(str: string): void { this.#sql += str } diff --git a/src/query-creator.ts b/src/query-creator.ts index 1b403a715..a2b09c41a 100644 --- a/src/query-creator.ts +++ b/src/query-creator.ts @@ -298,10 +298,14 @@ export class QueryCreator { } /** - * Creates a replace query. + * Creates a "replace into" query. * - * A MySQL-only statement similar to {@link InsertQueryBuilder.onDuplicateKeyUpdate} - * that deletes and inserts values on collision instead of updating existing rows. + * This is only supported by some dialects like MySQL or SQLite. + * + * Similar to MySQL's {@link InsertQueryBuilder.onDuplicateKeyUpdate} that deletes + * and inserts values on collision instead of updating existing rows. + * + * An alias of SQLite's {@link InsertQueryBuilder.orReplace}. * * The return value of this query is an instance of {@link InsertResult}. {@link InsertResult} * has the {@link InsertResult.insertId | insertId} field that holds the auto incremented id of @@ -318,10 +322,16 @@ export class QueryCreator { * first_name: 'Jennifer', * last_name: 'Aniston' * }) - * .executeTakeFirst() + * .executeTakeFirstOrThrow() * * console.log(result.insertId) * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * replace into `person` (`first_name`, `last_name`) values (?, ?) + * ``` */ replaceInto( table: T, diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index 9be13a1d7..418694fda 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -16,7 +16,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS) { - describe(`${dialect}: insert`, () => { + describe(`${dialect}: insert into`, () => { let ctx: TestContext before(async function () { @@ -317,7 +317,36 @@ for (const dialect of DIALECTS) { } }) - if (dialect === 'mysql') { + if (dialect === 'sqlite') { + for (const { method, action } of [ + { method: 'orAbort', action: 'abort' }, + { method: 'orFail', action: 'fail' }, + { method: 'orIgnore', action: 'ignore' }, + { method: 'orReplace', action: 'replace' }, + { method: 'orRollback', action: 'rollback' }, + ] as const) { + it(`should insert or ${action}`, async () => { + const query = ctx.db.insertInto('person')[method]().values({ + first_name: 'foo', + gender: 'other', + }) + + testSql(query, dialect, { + mysql: NOT_SUPPORTED, + postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: { + sql: `insert or ${action} into "person" ("first_name", "gender") values (?, ?)`, + parameters: ['foo', 'other'], + }, + }) + + await query.execute() + }) + } + } + + if (dialect === 'mysql' || dialect == 'sqlite') { it('should insert one row and ignore conflicts using insert ignore', async () => { const [{ id, ...existingPet }] = await ctx.db .selectFrom('pet') @@ -338,14 +367,26 @@ for (const dialect of DIALECTS) { }, postgres: NOT_SUPPORTED, mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + sqlite: { + sql: 'insert or ignore into "pet" ("name", "owner_id", "species") values (?, ?, ?)', + parameters: [ + existingPet.name, + existingPet.owner_id, + existingPet.species, + ], + }, }) const result = await query.executeTakeFirst() expect(result).to.be.instanceOf(InsertResult) - expect(result.insertId).to.be.undefined expect(result.numInsertedOrUpdatedRows).to.equal(0n) + if (dialect === 'sqlite') { + // SQLite seems to return the last inserted id even if nothing got inserted. + expect(result.insertId! > 0n).to.be.equal(true) + } else { + expect(result.insertId).to.be.undefined + } }) } diff --git a/test/node/src/replace.test.ts b/test/node/src/replace.test.ts index 9611e68c5..facfc1385 100644 --- a/test/node/src/replace.test.ts +++ b/test/node/src/replace.test.ts @@ -1,5 +1,4 @@ import { InsertResult, Kysely, sql } from '../../../' - import { clearDatabase, destroyTest, @@ -14,198 +13,213 @@ import { DIALECTS, } from './test-setup.js' -if (DIALECTS.includes('mysql')) { - const dialect = 'mysql' as const - - describe(`mysql: replace`, () => { - let ctx: TestContext - - before(async function () { - ctx = await initTest(this, dialect) - }) - - beforeEach(async () => { - await insertDefaultDataSet(ctx) - }) - - afterEach(async () => { - await clearDatabase(ctx) - }) - - after(async () => { - await destroyTest(ctx) - }) - - it('should insert one row', async () => { - const query = ctx.db.replaceInto('person').values({ - id: 15, - first_name: 'Foo', - last_name: 'Barson', - gender: 'other', - }) +for (const dialect of DIALECTS) { + if (dialect === 'mysql' || dialect === 'sqlite') { + describe(`${dialect}: replace into`, () => { + let ctx: TestContext - testSql(query, dialect, { - postgres: NOT_SUPPORTED, - mysql: { - sql: 'replace into `person` (`id`, `first_name`, `last_name`, `gender`) values (?, ?, ?, ?)', - parameters: [15, 'Foo', 'Barson', 'other'], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + before(async function () { + ctx = await initTest(this, dialect) }) - const result = await query.executeTakeFirst() - expect(result).to.be.instanceOf(InsertResult) - - if (dialect === 'mysql') { - expect(result.insertId).to.be.a('bigint') - } else { - expect(result.insertId).to.equal(undefined) - } - - expect(await getNewestPerson(ctx.db)).to.eql({ - first_name: 'Foo', - last_name: 'Barson', - }) - }) - - it('should insert one row with complex values', async () => { - const query = ctx.db.replaceInto('person').values({ - id: 2500, - first_name: ctx.db - .selectFrom('pet') - .select(sql`max(name)`.as('max_name')), - last_name: sql`concat('Bar', 'son')`, - gender: 'other', + beforeEach(async () => { + await insertDefaultDataSet(ctx) }) - testSql(query, dialect, { - postgres: NOT_SUPPORTED, - mysql: { - sql: "replace into `person` (`id`, `first_name`, `last_name`, `gender`) values (?, (select max(name) as `max_name` from `pet`), concat('Bar', 'son'), ?)", - parameters: [2500, 'other'], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + afterEach(async () => { + await clearDatabase(ctx) }) - const result = await query.executeTakeFirst() - expect(result).to.be.instanceOf(InsertResult) - - expect(await getNewestPerson(ctx.db)).to.eql({ - first_name: 'Hammo', - last_name: 'Barson', + after(async () => { + await destroyTest(ctx) }) - }) - it('should insert the result of a select query', async () => { - const query = ctx.db - .replaceInto('person') - .columns(['first_name', 'gender']) - .expression((eb) => - eb.selectFrom('pet').select(['name', sql`${'other'}`.as('gender')]), - ) + it('should insert one row', async () => { + const query = ctx.db.replaceInto('person').values({ + id: 15, + first_name: 'Foo', + last_name: 'Barson', + gender: 'other', + }) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'replace into `person` (`id`, `first_name`, `last_name`, `gender`) values (?, ?, ?, ?)', + parameters: [15, 'Foo', 'Barson', 'other'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'replace into "person" ("id", "first_name", "last_name", "gender") values (?, ?, ?, ?)', + parameters: [15, 'Foo', 'Barson', 'other'], + }, + }) + + const result = await query.executeTakeFirst() + expect(result).to.be.instanceOf(InsertResult) + expect(result.insertId).to.be.a('bigint') - testSql(query, dialect, { - postgres: NOT_SUPPORTED, - mysql: { - sql: 'replace into `person` (`first_name`, `gender`) select `name`, ? as `gender` from `pet`', - parameters: ['other'], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + expect(await getNewestPerson(ctx.db)).to.eql({ + first_name: 'Foo', + last_name: 'Barson', + }) }) - await query.execute() - - const persons = await ctx.db - .selectFrom('person') - .select('first_name') - .orderBy('first_name') - .execute() - - expect(persons.map((it) => it.first_name)).to.eql([ - 'Arnold', - 'Catto', - 'Doggo', - 'Hammo', - 'Jennifer', - 'Sylvester', - ]) - }) - - it('undefined values should be ignored', async () => { - const query = ctx.db.replaceInto('person').values({ - id: 12, - gender: 'male', - middle_name: undefined, + it('should insert one row with complex values', async () => { + const query = ctx.db.replaceInto('person').values({ + id: 2500, + first_name: ctx.db + .selectFrom('pet') + .select(sql`max(name)`.as('max_name')), + last_name: sql`concat('Bar', 'son')`, + gender: 'other', + }) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: "replace into `person` (`id`, `first_name`, `last_name`, `gender`) values (?, (select max(name) as `max_name` from `pet`), concat('Bar', 'son'), ?)", + parameters: [2500, 'other'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: `replace into "person" ("id", "first_name", "last_name", "gender") values (?, (select max(name) as "max_name" from "pet"), concat('Bar', 'son'), ?)`, + parameters: [2500, 'other'], + }, + }) + + const result = await query.executeTakeFirst() + expect(result).to.be.instanceOf(InsertResult) + + expect(await getNewestPerson(ctx.db)).to.eql({ + first_name: 'Hammo', + last_name: 'Barson', + }) }) - testSql(query, dialect, { - postgres: NOT_SUPPORTED, - mysql: { - sql: 'replace into `person` (`id`, `gender`) values (?, ?)', - parameters: [12, 'male'], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + it('should insert the result of a select query', async () => { + const query = ctx.db + .replaceInto('person') + .columns(['first_name', 'gender']) + .expression((eb) => + eb.selectFrom('pet').select(['name', sql`${'other'}`.as('gender')]), + ) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'replace into `person` (`first_name`, `gender`) select `name`, ? as `gender` from `pet`', + parameters: ['other'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'replace into "person" ("first_name", "gender") select "name", ? as "gender" from "pet"', + parameters: ['other'], + }, + }) + + await query.execute() + + const persons = await ctx.db + .selectFrom('person') + .select('first_name') + .orderBy('first_name') + .execute() + + expect(persons.map((it) => it.first_name)).to.eql([ + 'Arnold', + 'Catto', + 'Doggo', + 'Hammo', + 'Jennifer', + 'Sylvester', + ]) }) - await query.execute() - }) - - it('should replace on conflict', async () => { - const [existingPet] = await ctx.db - .selectFrom('pet') - .selectAll() - .limit(1) - .execute() - - const query = ctx.db - .replaceInto('pet') - .values({ ...existingPet, species: 'hamster' }) - - testSql(query, dialect, { - mysql: { - sql: 'replace into `pet` (`id`, `name`, `owner_id`, `species`) values (?, ?, ?, ?)', - parameters: [ - existingPet.id, - existingPet.name, - existingPet.owner_id, - 'hamster', - ], - }, - postgres: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + it('undefined values should be ignored', async () => { + const query = ctx.db.replaceInto('person').values({ + id: 12, + gender: 'male', + middle_name: undefined, + }) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: 'replace into `person` (`id`, `gender`) values (?, ?)', + parameters: [12, 'male'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'replace into "person" ("id", "gender") values (?, ?)', + parameters: [12, 'male'], + }, + }) + + await query.execute() }) - await query.execute() - - const updatedPet = await ctx.db - .selectFrom('pet') - .selectAll() - .where('id', '=', existingPet.id) - .executeTakeFirstOrThrow() - - expect(updatedPet).to.containSubset({ - name: 'Catto', - species: 'hamster', + it('should replace on conflict', async () => { + const [existingPet] = await ctx.db + .selectFrom('pet') + .selectAll() + .limit(1) + .execute() + + const query = ctx.db + .replaceInto('pet') + .values({ ...existingPet, species: 'hamster' }) + + testSql(query, dialect, { + mysql: { + sql: 'replace into `pet` (`id`, `name`, `owner_id`, `species`) values (?, ?, ?, ?)', + parameters: [ + existingPet.id, + existingPet.name, + existingPet.owner_id, + 'hamster', + ], + }, + postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'replace into "pet" ("id", "name", "owner_id", "species") values (?, ?, ?, ?)', + parameters: [ + existingPet.id, + existingPet.name, + existingPet.owner_id, + 'hamster', + ], + }, + }) + + await query.execute() + + const updatedPet = await ctx.db + .selectFrom('pet') + .selectAll() + .where('id', '=', existingPet.id) + .executeTakeFirstOrThrow() + + expect(updatedPet).to.containSubset({ + name: 'Catto', + species: 'hamster', + }) }) }) - }) - - async function getNewestPerson( - db: Kysely, - ): Promise | undefined> { - return await db - .selectFrom('person') - .select(['first_name', 'last_name']) - .where( - 'id', - '=', - db.selectFrom('person').select(sql`max(id)`.as('max_id')), - ) - .executeTakeFirst() + + async function getNewestPerson( + db: Kysely, + ): Promise | undefined> { + return await db + .selectFrom('person') + .select(['first_name', 'last_name']) + .where( + 'id', + '=', + db.selectFrom('person').select(sql`max(id)`.as('max_id')), + ) + .executeTakeFirst() + } } } From 2f7d7d190861d6ed57fad5a1fda220831c0601f1 Mon Sep 17 00:00:00 2001 From: Ivashkin Olexiy <32811685+ivashog@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:23:43 +0200 Subject: [PATCH 18/28] Add `within group` clause support for aggregate function builder (#1024) Co-authored-by: Ivashkin Olexiy <32811685+ivashog@users.noreply.github.com> Co-authored-by: Dev K0te Co-authored-by: igalklebanov --- src/operation-node/aggregate-function-node.ts | 8 +++- .../operation-node-transformer.ts | 3 +- .../aggregate-function-builder.ts | 47 +++++++++++++++++-- src/query-compiler/default-query-compiler.ts | 6 +++ test/node/src/aggregate-function.test.ts | 42 ++++++++++++++++- 5 files changed, 98 insertions(+), 8 deletions(-) diff --git a/src/operation-node/aggregate-function-node.ts b/src/operation-node/aggregate-function-node.ts index fd1548454..d336642d9 100644 --- a/src/operation-node/aggregate-function-node.ts +++ b/src/operation-node/aggregate-function-node.ts @@ -11,6 +11,7 @@ export interface AggregateFunctionNode extends OperationNode { readonly aggregated: readonly OperationNode[] readonly distinct?: boolean readonly orderBy?: OrderByNode + readonly withinGroup?: OrderByNode readonly filter?: WhereNode readonly over?: OverNode } @@ -46,11 +47,14 @@ export const AggregateFunctionNode = freeze({ cloneWithOrderBy( aggregateFunctionNode: AggregateFunctionNode, orderItems: ReadonlyArray, + withinGroup = false, ): AggregateFunctionNode { + const prop = withinGroup ? 'withinGroup' : 'orderBy' + return freeze({ ...aggregateFunctionNode, - orderBy: aggregateFunctionNode.orderBy - ? OrderByNode.cloneWithItems(aggregateFunctionNode.orderBy, orderItems) + [prop]: aggregateFunctionNode[prop] + ? OrderByNode.cloneWithItems(aggregateFunctionNode[prop], orderItems) : OrderByNode.create(orderItems), }) }, diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index a1ce5584f..af306c39e 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -904,11 +904,12 @@ export class OperationNodeTransformer { ): AggregateFunctionNode { return requireAllProps({ kind: 'AggregateFunctionNode', + func: node.func, aggregated: this.transformNodeList(node.aggregated), distinct: node.distinct, orderBy: this.transformNode(node.orderBy), + withinGroup: this.transformNode(node.withinGroup), filter: this.transformNode(node.filter), - func: node.func, over: this.transformNode(node.over), }) } diff --git a/src/query-builder/aggregate-function-builder.ts b/src/query-builder/aggregate-function-builder.ts index 64b6ff512..ae4cb64a6 100644 --- a/src/query-builder/aggregate-function-builder.ts +++ b/src/query-builder/aggregate-function-builder.ts @@ -11,7 +11,7 @@ import { } from '../expression/expression.js' import { ReferenceExpression, - StringReference, + SimpleReferenceExpression, } from '../parser/reference-parser.js' import { ComparisonOperatorExpression, @@ -21,7 +21,6 @@ import { } from '../parser/binary-operation-parser.js' import { SqlBool } from '../util/type-utils.js' import { ExpressionOrFactory } from '../parser/expression-parser.js' -import { DynamicReferenceBuilder } from '../dynamic/dynamic-reference-builder.js' import { OrderByDirectionExpression, parseOrderBy, @@ -125,7 +124,7 @@ export class AggregateFunctionBuilder * inner join "pet" ON "pet"."owner_id" = "person"."id" * ``` */ - orderBy | DynamicReferenceBuilder>( + orderBy>( orderBy: OE, direction?: OrderByDirectionExpression, ): AggregateFunctionBuilder { @@ -138,6 +137,48 @@ export class AggregateFunctionBuilder }) } + /** + * Adds a `withing group` clause with a nested `order by` clause after the function. + * + * This is only supported by some dialects like PostgreSQL or MS SQL Server. + * + * ### Examples + * + * Most frequent person name: + * + * ```ts + * const result = await db + * .selectFrom('person') + * .select((eb) => [ + * eb.fn + * .agg('mode') + * .withinGroupOrderBy('person.first_name') + * .as('most_frequent_name') + * ]) + * .executeTakeFirstOrThrow() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select mode() within group (order by "person"."first_name") as "most_frequent_name" + * from "person" + * ``` + */ + withinGroupOrderBy>( + orderBy: OE, + direction?: OrderByDirectionExpression, + ): AggregateFunctionBuilder { + return new AggregateFunctionBuilder({ + ...this.#props, + aggregateFunctionNode: AggregateFunctionNode.cloneWithOrderBy( + this.#props.aggregateFunctionNode, + parseOrderBy([orderBy, direction]), + true, + ), + }) + } + /** * Adds a `filter` clause with a nested `where` clause after the function. * diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 559ed112b..a93652262 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -1418,6 +1418,12 @@ export class DefaultQueryCompiler this.append(')') + if (node.withinGroup) { + this.append(' within group (') + this.visitNode(node.withinGroup) + this.append(')') + } + if (node.filter) { this.append(' filter(') this.visitNode(node.filter) diff --git a/test/node/src/aggregate-function.test.ts b/test/node/src/aggregate-function.test.ts index 877cff410..e61935737 100644 --- a/test/node/src/aggregate-function.test.ts +++ b/test/node/src/aggregate-function.test.ts @@ -4,6 +4,7 @@ import { SimpleReferenceExpression, ReferenceExpression, sql, + expressionBuilder, } from '../../../' import { Database, @@ -1108,8 +1109,12 @@ for (const dialect of DIALECTS) { await query.execute() }) - describe(`should execute order-sensitive aggregate functions`, () => { - if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'sqlite') { + describe('should execute order-sensitive aggregate functions', () => { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { const isMySql = dialect === 'mysql' const funcName = isMySql ? 'group_concat' : 'string_agg' const funcArgs: Array> = [ @@ -1157,6 +1162,39 @@ for (const dialect of DIALECTS) { await query.execute() }) } + + if (dialect === 'postgres' || dialect === 'mssql') { + it(`should execute a query with within group (order by column) in select clause`, async () => { + const query = ctx.db.selectFrom('toy').select((eb) => + eb.fn + .agg('percentile_cont', [sql.lit(0.5)]) + .withinGroupOrderBy('toy.price') + .$call((ab) => (dialect === 'mssql' ? ab.over() : ab)) + .as('median_price'), + ) + + testSql(query, dialect, { + postgres: { + sql: [ + `select percentile_cont(0.5) within group (order by "toy"."price") as "median_price"`, + `from "toy"`, + ], + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: { + sql: [ + `select percentile_cont(0.5) within group (order by "toy"."price") over() as "median_price"`, + `from "toy"`, + ], + parameters: [], + }, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + }) + } }) }) } From 1d7f836a7cee1b487dca4a08b3ee0d920c87dd6e Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sat, 11 Jan 2025 14:08:17 +0200 Subject: [PATCH 19/28] add TypeScript benchmarks. (#1314) --- .github/workflows/test.yml | 19 +++ package-lock.json | 196 ++++++++++++++++++++++++- package.json | 2 + src/parser/select-from-parser.ts | 31 ++-- test/ts-benchmarks/index.ts | 1 + test/ts-benchmarks/selectFrom.bench.ts | 98 +++++++++++++ test/ts-benchmarks/tsconfig.json | 4 + 7 files changed, 330 insertions(+), 21 deletions(-) create mode 100644 test/ts-benchmarks/index.ts create mode 100644 test/ts-benchmarks/selectFrom.bench.ts create mode 100644 test/ts-benchmarks/tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3afe0e966..04897e3a6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -247,3 +247,22 @@ jobs: - name: Type-check JSDocs code blocks run: npm run test:jsdocs + + typescript-benchmarks: + name: TypeScript Benchmarks + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run benchmarks + run: npm run bench:ts diff --git a/package-lock.json b/package-lock.json index 2e0f9838f..faf6b6934 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "kysely", - "version": "0.27.4", + "version": "0.27.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kysely", - "version": "0.27.4", + "version": "0.27.5", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.17.0", + "@ark/attest": "^0.36.0", "@types/better-sqlite3": "^7.6.11", "@types/chai": "^4.3.17", "@types/chai-as-promised": "^7.1.8", @@ -113,6 +114,52 @@ "node": ">=14.17" } }, + "node_modules/@ark/attest": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@ark/attest/-/attest-0.36.0.tgz", + "integrity": "sha512-eXDHXRQyCS2VjU6AmrXKfd/XYQs5WngjFiBwvwBB5osI3aW4/fuDO7s4t4jBqh3UyZCLObjirjvlGch7bferyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/fs": "0.32.0", + "@ark/util": "0.32.0", + "@prettier/sync": "0.5.2", + "@typescript/analyze-trace": "0.10.1", + "@typescript/vfs": "1.6.0", + "arktype": "2.0.0-rc.32", + "prettier": "3.4.2" + }, + "bin": { + "attest": "out/cli/cli.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@ark/fs": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@ark/fs/-/fs-0.32.0.tgz", + "integrity": "sha512-BgQ4pVLk7QHxCy9CD1dKFsRbhEX/LqqGuJxDmoguRD8OUe6P3lzjn0d8dIc7z1ci1noJp7Z1eicv7R/Yzk096g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ark/schema": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.32.0.tgz", + "integrity": "sha512-7d/7xvuNEiHXkVAc2kn63xXaTG/rpUZs1zyNZiGGfDAPItPJGD572GK4UbOPBpsF1XpgK1akPxtpxs4tW+hBfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/util": "0.32.0" + } + }, + "node_modules/@ark/util": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.32.0.tgz", + "integrity": "sha512-gh8KwsRyUm2NXLrQ76XnF0H2NYTBWjziJnnazDd3NRsz9ouZ8Xs2k5gJNSbr/PMgg4+dGJbIoXgjESiyeZsCRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@azure/abort-controller": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", @@ -1333,6 +1380,22 @@ "@octokit/openapi-types": "^20.0.0" } }, + "node_modules/@prettier/sync": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.5.2.tgz", + "integrity": "sha512-Yb569su456XNx5BsH/Vyem7xD6g/y9iLmLUzRKM1a/dhU/D7HqqvkAG72znulXlMXztbV0iiu9O5AL8K98TzZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "make-synchronized": "^0.2.8" + }, + "funding": { + "url": "https://github.com/prettier/prettier-synchronized?sponsor=1" + }, + "peerDependencies": { + "prettier": "*" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1565,6 +1628,51 @@ "@types/node": "*" } }, + "node_modules/@typescript/analyze-trace": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@typescript/analyze-trace/-/analyze-trace-0.10.1.tgz", + "integrity": "sha512-RnlSOPh14QbopGCApgkSx5UBgGda5MX1cHqp2fsqfiDyCwGL/m1jaeB9fzu7didVS81LQqGZZuxFBcg8YU8EVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "exit": "^0.1.2", + "jsonparse": "^1.3.1", + "jsonstream-next": "^3.0.0", + "p-limit": "^3.1.0", + "split2": "^3.2.2", + "treeify": "^1.1.0", + "yargs": "^16.2.0" + }, + "bin": { + "analyze-trace": "bin/analyze-trace", + "print-trace-types": "bin/print-trace-types", + "simplify-trace-types": "bin/simplify-trace-types" + } + }, + "node_modules/@typescript/analyze-trace/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.0.tgz", + "integrity": "sha512-hvJUjNVeBMp77qPINuUvYXj4FyWeeMMKZkxEATEU3hqBAQ7qdTBCUFT7Sp0Zu0faeEtFf+ldXxMEDr/bk73ISg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -1675,6 +1783,17 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/arktype": { + "version": "2.0.0-rc.32", + "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.0.0-rc.32.tgz", + "integrity": "sha512-C8KqauKkLK8+IMIlWIMsK41LtCf0OKd8ukX23NtnsM3IT44ZK5V3LToa4Q5kM4tLfRe/lqy1JuPCVi03NTeazA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/schema": "0.32.0", + "@ark/util": "0.32.0" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -2431,6 +2550,15 @@ "node": ">=0.8.x" } }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -3036,6 +3164,33 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsonstream-next": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonstream-next/-/jsonstream-next-3.0.0.tgz", + "integrity": "sha512-aAi6oPhdt7BKyQn1SrIIGZBt0ukKuOUE1qV6kJ3GgioSOYzsRc8z9Hfr1BVmacA/jLe9nARfmgMGgn68BqIAgg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through2": "^4.0.2" + }, + "bin": { + "jsonstream-next": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -3230,6 +3385,16 @@ "node": ">=16.14" } }, + "node_modules/make-synchronized": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/make-synchronized/-/make-synchronized-0.2.9.tgz", + "integrity": "sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/fisker/make-synchronized?sponsor=1" + } + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -4173,10 +4338,11 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -5100,6 +5266,16 @@ "node": ">=0.8" } }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/tinyglobby": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", @@ -5154,6 +5330,16 @@ "node": ">=8.0" } }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", diff --git a/package.json b/package.json index 03712fef5..9e98a2acf 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ }, "scripts": { "clean": "rm -rf dist & rm -rf test/node/dist & rm -rf test/browser/bundle.js & rm -rf helpers", + "bench:ts": "npm run build && cd ./test/ts-benchmarks && tsx ./index.ts", "test": "npm run build && npm run test:node:build && npm run test:node:run && npm run test:typings && npm run test:esmimports && npm run test:exports", "test:node:build": "tsc -p test/node", "test:node": "npm run build && npm run test:node:build && npm run test:node:run", @@ -82,6 +83,7 @@ ], "devDependencies": { "@arethetypeswrong/cli": "^0.17.0", + "@ark/attest": "^0.36.0", "@types/better-sqlite3": "^7.6.11", "@types/chai": "^4.3.17", "@types/chai-as-promised": "^7.1.8", diff --git a/src/parser/select-from-parser.ts b/src/parser/select-from-parser.ts index 521daafd5..385360ece 100644 --- a/src/parser/select-from-parser.ts +++ b/src/parser/select-from-parser.ts @@ -11,19 +11,18 @@ export type SelectFrom< DB, TB extends keyof DB, TE extends TableExpressionOrList, -> = - TE extends ReadonlyArray - ? SelectQueryBuilder, FromTables, {}> - : TE extends keyof DB & string - ? // This branch creates a good-looking type for the most common case: - // selectFrom('person') --> SelectQueryBuilder. - // ExtractTableAlias is needed for the case where DB == any. Without it: - // selectFrom('person as p') --> SelectQueryBuilder - SelectQueryBuilder, {}> - : // This branch creates a good-looking type for common aliased case: - // selectFrom('person as p') --> SelectQueryBuilder. - TE extends `${infer T} as ${infer A}` - ? T extends keyof DB - ? SelectQueryBuilder, TB | A, {}> - : never - : SelectQueryBuilder, FromTables, {}> +> = TE extends keyof DB & string + ? // This branch creates a good-looking type for the most common case: + // selectFrom('person') --> SelectQueryBuilder. + // ExtractTableAlias is needed for the case where DB == any. Without it: + // selectFrom('person as p') --> SelectQueryBuilder + SelectQueryBuilder, {}> + : // This branch creates a good-looking type for common aliased case: + // selectFrom('person as p') --> SelectQueryBuilder. + TE extends `${infer T} as ${infer A}` + ? T extends keyof DB + ? SelectQueryBuilder, TB | A, {}> + : never + : TE extends ReadonlyArray + ? SelectQueryBuilder, FromTables, {}> + : SelectQueryBuilder, FromTables, {}> diff --git a/test/ts-benchmarks/index.ts b/test/ts-benchmarks/index.ts new file mode 100644 index 000000000..81d8c59b6 --- /dev/null +++ b/test/ts-benchmarks/index.ts @@ -0,0 +1 @@ +import './selectFrom.bench.js' diff --git a/test/ts-benchmarks/selectFrom.bench.ts b/test/ts-benchmarks/selectFrom.bench.ts new file mode 100644 index 000000000..ec90beea9 --- /dev/null +++ b/test/ts-benchmarks/selectFrom.bench.ts @@ -0,0 +1,98 @@ +import { bench } from '@ark/attest' +import type { DB } from '../typings/test-d/huge-db.test-d' +import type { Kysely } from '../../dist/esm/index.js' + +declare const kysely: Kysely +declare const kyselyAny: Kysely + +bench.baseline(() => {}) + +bench('kysely.selectFrom(table)', () => { + return kysely.selectFrom('table_fff4c6195261874920bc7ce92d67d2c2') +}).types([372, 'instantiations']) + +bench('kysely.selectFrom(~table)', () => { + return kysely.selectFrom('my_table2') +}).types([6864, 'instantiations']) + +bench('kysely.selectFrom(table as alias)', () => { + return kysely.selectFrom('my_table as mt') +}).types([385, 'instantiations']) + +bench('kysely.selectFrom([table])', () => { + return kysely.selectFrom(['my_table']) +}).types([427, 'instantiations']) + +bench('kysely.selectFrom([~table])', () => { + return kysely.selectFrom(['my_table2']) +}).types([6914, 'instantiations']) + +bench('kysely.selectFrom([table as alias])', () => { + return kysely.selectFrom(['my_table as mt']) +}).types([427, 'instantiations']) + +bench('kysely.selectFrom([table, table])', () => { + return kysely.selectFrom([ + 'my_table', + 'table_000a8a0cb7f265a624c851d3e7f8b946', + ]) +}).types([427, 'instantiations']) + +bench('kysely.selectFrom([table, ~table])', () => { + return kysely.selectFrom([ + 'my_table', + 'table_000a8a0cb7f265a624c851d3e7f8b9462', + ]) +}).types([6917, 'instantiations']) + +bench('kysely.selectFrom([table as alias, table as alias])', () => { + return kysely.selectFrom([ + 'my_table as mt', + 'table_000a8a0cb7f265a624c851d3e7f8b946 as t', + ]) +}).types([427, 'instantiations']) + +bench('kyselyAny.selectFrom(table)', () => { + return kyselyAny.selectFrom('table_fff4c6195261874920bc7ce92d67d2c2') +}).types([124, 'instantiations']) + +bench('kyselyAny.selectFrom(~table)', () => { + return kyselyAny.selectFrom('my_table2') +}).types([124, 'instantiations']) + +bench('kyselyAny.selectFrom(table as alias)', () => { + return kyselyAny.selectFrom('my_table as mt') +}).types([124, 'instantiations']) + +bench('kyselyAny.selectFrom([table])', () => { + return kyselyAny.selectFrom(['my_table']) +}).types([165, 'instantiations']) + +bench('kyselyAny.selectFrom([~table])', () => { + return kyselyAny.selectFrom(['my_table2']) +}).types([179, 'instantiations']) + +bench('kyselyAny.selectFrom([table as alias])', () => { + return kyselyAny.selectFrom(['my_table as mt']) +}).types([179, 'instantiations']) + +bench('kyselyAny.selectFrom([table, table])', () => { + return kyselyAny.selectFrom([ + 'my_table', + 'table_000a8a0cb7f265a624c851d3e7f8b946', + ]) +}).types([179, 'instantiations']) + +bench('kyselyAny.selectFrom([table, ~table])', () => { + return kyselyAny.selectFrom([ + 'my_table', + 'table_000a8a0cb7f265a624c851d3e7f8b9462', + ]) +}).types([179, 'instantiations']) + +bench('kyselyAny.selectFrom([table as alias, table as alias])', () => { + return kyselyAny.selectFrom([ + 'my_table as mt', + 'table_000a8a0cb7f265a624c851d3e7f8b946 as t', + ]) +}).types([179, 'instantiations']) diff --git a/test/ts-benchmarks/tsconfig.json b/test/ts-benchmarks/tsconfig.json new file mode 100644 index 000000000..348736603 --- /dev/null +++ b/test/ts-benchmarks/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig-base.json", + "include": ["**/*.ts"], +} From 18ba1d3c91e243134a7c7326c00ea4ecf9bce930 Mon Sep 17 00:00:00 2001 From: Drew Marshall <31295028+drew-marsh@users.noreply.github.com> Date: Sun, 12 Jan 2025 09:05:16 -0800 Subject: [PATCH 20/28] Add outer and cross apply (mssql) (#1074) Co-authored-by: Drew Marshall <31295028+drew-marsh@users.noreply.github.com> Co-authored-by: Drew Marshall Co-authored-by: igalklebanov --- src/operation-node/join-node.ts | 2 + src/parser/join-parser.ts | 19 +++-- src/query-builder/select-query-builder.ts | 86 ++++++++++++++++++-- src/query-compiler/default-query-compiler.ts | 2 + test/node/src/join.test.ts | 68 ++++++++++++++++ test/typings/test-d/join.test-d.ts | 81 ++++++++++++++++++ 6 files changed, 247 insertions(+), 11 deletions(-) diff --git a/src/operation-node/join-node.ts b/src/operation-node/join-node.ts index cbbe93a67..c2a784fb8 100644 --- a/src/operation-node/join-node.ts +++ b/src/operation-node/join-node.ts @@ -10,6 +10,8 @@ export type JoinType = | 'LateralInnerJoin' | 'LateralLeftJoin' | 'Using' + | 'OuterApply' + | 'CrossApply' export interface JoinNode extends OperationNode { readonly kind: 'JoinNode' diff --git a/src/parser/join-parser.ts b/src/parser/join-parser.ts index 981b80131..fe62119f7 100644 --- a/src/parser/join-parser.ts +++ b/src/parser/join-parser.ts @@ -1,18 +1,18 @@ import { JoinNode, JoinType } from '../operation-node/join-node.js' +import { JoinBuilder } from '../query-builder/join-builder.js' import { AnyColumn, AnyColumnWithTable, DrainOuterGeneric, } from '../util/type-utils.js' +import { parseReferentialBinaryOperation } from './binary-operation-parser.js' +import { createJoinBuilder } from './parse-utils.js' import { - TableExpression, - parseTableExpression, From, FromTables, + TableExpression, + parseTableExpression, } from './table-parser.js' -import { parseReferentialBinaryOperation } from './binary-operation-parser.js' -import { JoinBuilder } from '../query-builder/join-builder.js' -import { createJoinBuilder } from './parse-utils.js' export type JoinReferenceExpression< DB, @@ -41,6 +41,8 @@ export function parseJoin(joinType: JoinType, args: any[]): JoinNode { return parseSingleOnJoin(joinType, args[0], args[1], args[2]) } else if (args.length === 2) { return parseCallbackJoin(joinType, args[0], args[1]) + } else if (args.length === 1) { + return parseOnlessJoin(joinType, args[0]) } else { throw new Error('not implemented') } @@ -66,3 +68,10 @@ function parseSingleOnJoin( parseReferentialBinaryOperation(lhsColumn, '=', rhsColumn), ) } + +function parseOnlessJoin( + joinType: JoinType, + from: TableExpression, +): JoinNode { + return JoinNode.create(joinType, parseTableExpression(from)) +} diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index cb6126cca..f13c1e829 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -579,13 +579,13 @@ export interface SelectQueryBuilder selectAll(): SelectQueryBuilder> /** - * Joins another table to the query using an inner join. + * Joins another table to the query using an `inner join`. * * ### Examples * * * - * Simple inner joins can be done by providing a table name and two columns to join: + * Simple `inner join`s can be done by providing a table name and two columns to join: * * ```ts * const result = await db @@ -721,7 +721,7 @@ export interface SelectQueryBuilder ): SelectQueryBuilderWithInnerJoin /** - * Just like {@link innerJoin} but adds a left join instead of an inner join. + * Just like {@link innerJoin} but adds a `left join` instead of an `inner join`. */ leftJoin< TE extends TableExpression, @@ -742,7 +742,7 @@ export interface SelectQueryBuilder ): SelectQueryBuilderWithLeftJoin /** - * Just like {@link innerJoin} but adds a right join instead of an inner join. + * Just like {@link innerJoin} but adds a `right join` instead of an `inner join`. */ rightJoin< TE extends TableExpression, @@ -763,7 +763,9 @@ export interface SelectQueryBuilder ): SelectQueryBuilderWithRightJoin /** - * Just like {@link innerJoin} but adds a full join instead of an inner join. + * Just like {@link innerJoin} but adds a `full join` instead of an `inner join`. + * + * This is only supported by some dialects like PostgreSQL, MS SQL Server and SQLite. */ fullJoin< TE extends TableExpression, @@ -786,6 +788,8 @@ export interface SelectQueryBuilder /** * Just like {@link innerJoin} but adds a lateral join instead of an inner join. * + * This is only supported by some dialects like PostgreSQL and MySQL. + * * ### Examples * * ```ts @@ -835,7 +839,10 @@ export interface SelectQueryBuilder ): SelectQueryBuilderWithInnerJoin /** - * Just like {@link innerJoin} but adds a lateral left join instead of an inner join. + * Just like {@link innerJoin} but adds a `left join lateral` instead of an `inner join`. + * + * This is only supported by some dialects like PostgreSQL and MySQL. + * * ### Examples * * ```ts @@ -884,6 +891,53 @@ export interface SelectQueryBuilder callback: FN, ): SelectQueryBuilderWithLeftJoin + /** + * Joins another table to the query using a `cross apply`. + * + * This is only supported by some dialects like MS SQL Server. + * + * ### Examples + * + * ```ts + * await db.selectFrom('person') + * .crossApply( + * (eb) => + * eb.selectFrom('pet') + * .select('name') + * .whereRef('pet.owner_id', '=', 'person.id') + * .as('p') + * ) + * .select(['first_name', 'p.name']) + * .orderBy('first_name') + * .execute() + * ``` + * + * The generated SQL (MS SQL Server): + * + * ```sql + * select "person"."first_name", "p"."name" + * from "person" + * cross apply ( + * select "name" + * from "pet" + * where "pet"."owner_id" = "person"."id" + * ) as "p" + * order by "first_name" + * ``` + */ + crossApply>( + table: TE, + ): SelectQueryBuilderWithInnerJoin + + /** + * Just like {@link crossApply} but adds an `outer apply` instead of a `cross apply`. + * + * This is only supported by some dialects like MS SQL Server. + */ + outerApply>( + table: TE, + ): SelectQueryBuilderWithLeftJoin + /** * Adds an `order by` clause to the query. * @@ -2395,6 +2449,26 @@ class SelectQueryBuilderImpl }) } + crossApply(table: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('CrossApply', [table]), + ), + }) + } + + outerApply(table: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('OuterApply', [table]), + ), + }) + } + orderBy(...args: any[]): SelectQueryBuilder { return new SelectQueryBuilderImpl({ ...this.#props, diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index a93652262..aa2cc83c4 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -1815,5 +1815,7 @@ const JOIN_TYPE_SQL: Readonly> = freeze({ FullJoin: 'full join', LateralInnerJoin: 'inner join lateral', LateralLeftJoin: 'left join lateral', + OuterApply: 'outer apply', + CrossApply: 'cross apply', Using: 'using', }) diff --git a/test/node/src/join.test.ts b/test/node/src/join.test.ts index 6031f7520..ad0699071 100644 --- a/test/node/src/join.test.ts +++ b/test/node/src/join.test.ts @@ -780,5 +780,73 @@ for (const dialect of DIALECTS) { }) }) } + + if (dialect === 'mssql') { + describe('apply', () => { + it('should cross apply an expression', async () => { + const q = ctx.db + .selectFrom('person') + .crossApply((eb) => + eb + .selectFrom('pet') + .whereRef('pet.owner_id', '=', 'person.id') + .select('pet.name') + .as('pets'), + ) + .select(['person.first_name', 'pets.name']) + .orderBy('pets.name') + + testSql(q, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + mssql: { + sql: `select "person"."first_name", "pets"."name" from "person" cross apply (select "pet"."name" from "pet" where "pet"."owner_id" = "person"."id") as "pets" order by "pets"."name"`, + parameters: [], + }, + }) + + const result = await q.execute() + + expect(result).to.deep.equal([ + { first_name: 'Jennifer', name: 'Catto' }, + { first_name: 'Arnold', name: 'Doggo' }, + { first_name: 'Sylvester', name: 'Hammo' }, + ]) + }) + + it('should outer apply an expression', async () => { + const q = ctx.db + .selectFrom('person') + .outerApply((eb) => + eb + .selectFrom('pet') + .whereRef('pet.owner_id', '=', 'person.id') + .select('pet.name') + .as('pets'), + ) + .select(['person.first_name', 'pets.name']) + .orderBy('pets.name') + + testSql(q, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + mssql: { + sql: `select "person"."first_name", "pets"."name" from "person" outer apply (select "pet"."name" from "pet" where "pet"."owner_id" = "person"."id") as "pets" order by "pets"."name"`, + parameters: [], + }, + }) + + const result = await q.execute() + + expect(result).to.deep.equal([ + { first_name: 'Jennifer', name: 'Catto' }, + { first_name: 'Arnold', name: 'Doggo' }, + { first_name: 'Sylvester', name: 'Hammo' }, + ]) + }) + }) + } }) } diff --git a/test/typings/test-d/join.test-d.ts b/test/typings/test-d/join.test-d.ts index 6f4c0278e..e5d6b858e 100644 --- a/test/typings/test-d/join.test-d.ts +++ b/test/typings/test-d/join.test-d.ts @@ -158,6 +158,87 @@ async function testJoin(db: Kysely) { .innerJoin('pet', 'pet.owner_id', 'person.id') .set({ last_name: 'Jennifer' }) .where('pet.id', '=', '1') + .execute() + + const r9 = await db + .selectFrom('person') + .innerJoinLateral( + (eb) => + eb + .selectFrom('pet') + .whereRef('pet.owner_id', '=', 'person.id') + .select('pet.name') + .as('pets'), + (jb) => jb.onTrue(), + ) + .select(['person.first_name', 'pets.name']) + .execute() + + expectType< + { + first_name: string + name: string + }[] + >(r9) + + const r10 = await db + .selectFrom('person') + .leftJoinLateral( + (eb) => + eb + .selectFrom('pet') + .whereRef('pet.owner_id', '=', 'person.id') + .select('pet.name') + .as('pets'), + (jb) => jb.onTrue(), + ) + .select(['person.first_name', 'pets.name']) + .execute() + + expectType< + { + first_name: string + name: string | null + }[] + >(r10) + + const r11 = await db + .selectFrom('person') + .crossApply((eb) => + eb + .selectFrom('pet') + .whereRef('pet.owner_id', '=', 'person.id') + .select('pet.name') + .as('pets'), + ) + .select(['person.first_name', 'pets.name']) + .execute() + + expectType< + { + first_name: string + name: string + }[] + >(r11) + + const r12 = await db + .selectFrom('person') + .outerApply((eb) => + eb + .selectFrom('pet') + .whereRef('pet.owner_id', '=', 'person.id') + .select('pet.name') + .as('pets'), + ) + .select(['person.first_name', 'pets.name']) + .execute() + + expectType< + { + first_name: string + name: string | null + }[] + >(r12) // Refer to table that's not joined expectError( From 7e1dcaa5cff3cae4a1bab88c029731de6303d759 Mon Sep 17 00:00:00 2001 From: Simon Schick Date: Fri, 17 Jan 2025 17:45:41 +0100 Subject: [PATCH 21/28] Support json_agg(column_ref) (#1316) Co-authored-by: Simon Schick <392589+SimonSimCity@users.noreply.github.com> Co-authored-by: Igal Klebanov --- src/query-builder/function-module.ts | 38 +++++++++++++++++++-- test/node/src/json.test.ts | 28 +++++++++++++++ test/typings/test-d/postgres-json.test-d.ts | 14 ++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/query-builder/function-module.ts b/src/query-builder/function-module.ts index 028e27500..8e013998b 100644 --- a/src/query-builder/function-module.ts +++ b/src/query-builder/function-module.ts @@ -663,9 +663,13 @@ export interface FunctionModule { any(expr: Expression>): ExpressionWrapper /** - * Creates a json_agg function call. + * Creates a `json_agg` function call. * - * This function is only available on PostgreSQL. + * This is only supported by some dialects like PostgreSQL. + * + * ### Examples + * + * You can use it on table expressions: * * ```ts * await db.selectFrom('person') @@ -683,6 +687,28 @@ export interface FunctionModule { * inner join "pet" on "pet"."owner_id" = "person"."id" * group by "person"."first_name" * ``` + * + * or on columns: + * + * ```ts + * await db.selectFrom('person') + * .innerJoin('pet', 'pet.owner_id', 'person.id') + * .select((eb) => [ + * 'first_name', + * eb.fn.jsonAgg('pet.name').as('pet_names'), + * ]) + * .groupBy('person.first_name') + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select "first_name", json_agg("pet"."name") AS "pet_names" + * from "person" + * inner join "pet" ON "pet"."owner_id" = "person"."id" + * group by "person"."first_name" + * ``` */ jsonAgg>( table: T, @@ -696,6 +722,14 @@ export interface FunctionModule { : never > + jsonAgg>( + column: RE, + ): AggregateFunctionBuilder< + DB, + TB, + ExtractTypeFromStringReference[] | null + > + /** * Creates a to_json function call. * diff --git a/test/node/src/json.test.ts b/test/node/src/json.test.ts index 5a275da7d..4f0933253 100644 --- a/test/node/src/json.test.ts +++ b/test/node/src/json.test.ts @@ -319,6 +319,34 @@ for (const dialect of DIALECTS) { ]) }) + it('should aggregate a column using json_agg', async () => { + const res = await db + .selectFrom('pet') + .leftJoin('person', 'person.id', 'pet.owner_id') + .select((eb) => [ + eb.fn.jsonAgg('pet.name').as('petName'), + 'person.first_name', + ]) + .groupBy('person.first_name') + .execute() + + expect(res).to.have.length(3) + expect(res).to.containSubset([ + { + first_name: 'Jennifer', + petName: ['Catto'], + }, + { + first_name: 'Arnold', + petName: ['Doggo'], + }, + { + first_name: 'Sylvester', + petName: ['Hammo'], + }, + ]) + }) + it('should jsonify a joined table using to_json', async () => { const res = await db .selectFrom('person') diff --git a/test/typings/test-d/postgres-json.test-d.ts b/test/typings/test-d/postgres-json.test-d.ts index 898ffe5c9..15a66ae01 100644 --- a/test/typings/test-d/postgres-json.test-d.ts +++ b/test/typings/test-d/postgres-json.test-d.ts @@ -169,6 +169,20 @@ async function testPostgresJsonAgg(db: Kysely) { status: string | null }[] }>(r4) + + const r5 = await db + .selectFrom('person') + .innerJoin('pet', 'pet.owner_id', 'person.id') + .select((eb) => ['first_name', eb.fn.jsonAgg('owner_id').as('pet_names')]) + .groupBy('person.first_name') + .execute() + + expectType< + { + first_name: string + pet_names: number[] | null + }[] + >(r5) } async function testPostgresToJson(db: Kysely) { From ab5b365da1320351691e40047a89fa52bce3799a Mon Sep 17 00:00:00 2001 From: Ersin Akinci <5427394+ersinakinci@users.noreply.github.com> Date: Sat, 18 Jan 2025 10:31:07 -0700 Subject: [PATCH 22/28] Add support for cross join and cross join lateral (#1325) Co-authored-by: Igal Klebanov --- src/operation-node/join-node.ts | 2 + src/query-builder/select-query-builder.ts | 65 +++++++++++++++++++ src/query-compiler/default-query-compiler.ts | 2 + test/node/src/join.test.ts | 66 ++++++++++++++++++++ 4 files changed, 135 insertions(+) diff --git a/src/operation-node/join-node.ts b/src/operation-node/join-node.ts index c2a784fb8..ba2bfa4e4 100644 --- a/src/operation-node/join-node.ts +++ b/src/operation-node/join-node.ts @@ -7,8 +7,10 @@ export type JoinType = | 'LeftJoin' | 'RightJoin' | 'FullJoin' + | 'CrossJoin' | 'LateralInnerJoin' | 'LateralLeftJoin' + | 'LateralCrossJoin' | 'Using' | 'OuterApply' | 'CrossApply' diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index f13c1e829..79c74eb96 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -785,6 +785,13 @@ export interface SelectQueryBuilder callback: FN, ): SelectQueryBuilderWithFullJoin + /** + * Just like {@link innerJoin} but adds a `cross join` instead of an `inner join`. + */ + crossJoin>( + table: TE, + ): SelectQueryBuilderWithInnerJoin + /** * Just like {@link innerJoin} but adds a lateral join instead of an inner join. * @@ -891,6 +898,44 @@ export interface SelectQueryBuilder callback: FN, ): SelectQueryBuilderWithLeftJoin + /** + * Just like {@link innerJoin} but adds a `cross join lateral` instead of an `inner join`. + * + * This is only supported by some dialects like PostgreSQL. + * + * ### Examples + * + * ```ts + * await db.selectFrom('person') + * .crossJoinLateral( + * (eb) => + * eb.selectFrom('pet') + * .select('name') + * .whereRef('pet.owner_id', '=', 'person.id') + * .as('p') + * ) + * .select(['first_name', 'p.name']) + * .orderBy('first_name') + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select "person"."first_name", "p"."name" + * from "person" + * cross join lateral ( + * select "name" + * from "pet" + * where "pet"."owner_id" = "person"."id" + * ) as "p" + * order by "first_name" + * ``` + */ + crossJoinLateral>( + table: TE, + ): SelectQueryBuilderWithInnerJoin + /** * Joins another table to the query using a `cross apply`. * @@ -2429,6 +2474,16 @@ class SelectQueryBuilderImpl }) } + crossJoin(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('CrossJoin', args), + ), + }) + } + innerJoinLateral(...args: any): any { return new SelectQueryBuilderImpl({ ...this.#props, @@ -2449,6 +2504,16 @@ class SelectQueryBuilderImpl }) } + crossJoinLateral(...args: any): any { + return new SelectQueryBuilderImpl({ + ...this.#props, + queryNode: QueryNode.cloneWithJoin( + this.#props.queryNode, + parseJoin('LateralCrossJoin', args), + ), + }) + } + crossApply(table: any): any { return new SelectQueryBuilderImpl({ ...this.#props, diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index aa2cc83c4..63bb1072f 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -1813,8 +1813,10 @@ const JOIN_TYPE_SQL: Readonly> = freeze({ LeftJoin: 'left join', RightJoin: 'right join', FullJoin: 'full join', + CrossJoin: 'cross join', LateralInnerJoin: 'inner join lateral', LateralLeftJoin: 'left join lateral', + LateralCrossJoin: 'cross join lateral', OuterApply: 'outer apply', CrossApply: 'cross apply', Using: 'using', diff --git a/test/node/src/join.test.ts b/test/node/src/join.test.ts index ad0699071..7dc10ca86 100644 --- a/test/node/src/join.test.ts +++ b/test/node/src/join.test.ts @@ -711,6 +711,37 @@ for (const dialect of DIALECTS) { }) } + describe('cross join', () => { + it(`should cross join a table`, async () => { + const query = ctx.db + .selectFrom('person') + .crossJoin('pet') + .selectAll() + .orderBy('person.first_name') + + testSql(query, dialect, { + postgres: { + sql: `select * from "person" cross join "pet" order by "person"."first_name"`, + parameters: [], + }, + mysql: { + sql: 'select * from `person` cross join `pet` order by `person`.`first_name`', + parameters: [], + }, + mssql: { + sql: `select * from "person" cross join "pet" order by "person"."first_name"`, + parameters: [], + }, + sqlite: { + sql: `select * from "person" cross join "pet" order by "person"."first_name"`, + parameters: [], + }, + }) + + await query.execute() + }) + }) + if (dialect === 'postgres') { describe('lateral join', () => { it('should join an expression laterally', async () => { @@ -778,6 +809,41 @@ for (const dialect of DIALECTS) { { first_name: 'Sylvester', name: 'Hammo' }, ]) }) + + it('should cross join an expression laterally', async () => { + const query = ctx.db + .selectFrom('person') + .crossJoinLateral( + (eb) => + eb + .selectFrom('pet') + .innerJoin('person as owner', 'owner.id', 'pet.owner_id') + .select('name') + .whereRef('owner.gender', '=', 'person.gender') + .as('p'), + ) + .select(['first_name', 'p.name']) + .orderBy(['first_name', 'p.name']) + + testSql(query, dialect, { + postgres: { + sql: `select "first_name", "p"."name" from "person" cross join lateral (select "name" from "pet" inner join "person" as "owner" on "owner"."id" = "pet"."owner_id" where "owner"."gender" = "person"."gender") as "p" order by "first_name", "p"."name"`, + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const res = await query.execute() + expect(res).to.eql([ + { first_name: 'Arnold', name: 'Doggo' }, + { first_name: 'Arnold', name: 'Hammo' }, + { first_name: 'Jennifer', name: 'Catto' }, + { first_name: 'Sylvester', name: 'Doggo' }, + { first_name: 'Sylvester', name: 'Hammo' }, + ]) + }) }) } From c95f499c49d39342b2d259ea24a3a704a1dda336 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 18 Jan 2025 20:00:36 +0200 Subject: [PATCH 23/28] dry up joins. --- src/query-builder/delete-query-builder.ts | 31 +++------ src/query-builder/select-query-builder.ts | 83 +++++------------------ src/query-builder/update-query-builder.ts | 31 +++------ 3 files changed, 35 insertions(+), 110 deletions(-) diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index 069fbb7e4..a79703b5e 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -78,6 +78,7 @@ import { SelectExpressionFromOutputCallback, SelectExpressionFromOutputExpression, } from './output-interface.js' +import { JoinType } from '../operation-node/join-node.js' export class DeleteQueryBuilder implements @@ -407,13 +408,7 @@ export class DeleteQueryBuilder >(table: TE, callback: FN): DeleteQueryBuilderWithInnerJoin innerJoin(...args: any): any { - return new DeleteQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('InnerJoin', args), - ), - }) + return this.#join('InnerJoin', args) } /** @@ -431,13 +426,7 @@ export class DeleteQueryBuilder >(table: TE, callback: FN): DeleteQueryBuilderWithLeftJoin leftJoin(...args: any): any { - return new DeleteQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LeftJoin', args), - ), - }) + return this.#join('LeftJoin', args) } /** @@ -455,13 +444,7 @@ export class DeleteQueryBuilder >(table: TE, callback: FN): DeleteQueryBuilderWithRightJoin rightJoin(...args: any): any { - return new DeleteQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('RightJoin', args), - ), - }) + return this.#join('RightJoin', args) } /** @@ -479,11 +462,15 @@ export class DeleteQueryBuilder >(table: TE, callback: FN): DeleteQueryBuilderWithFullJoin fullJoin(...args: any): any { + return this.#join('FullJoin', args) + } + + #join(joinType: JoinType, args: any[]): any { return new DeleteQueryBuilder({ ...this.#props, queryNode: QueryNode.cloneWithJoin( this.#props.queryNode, - parseJoin('FullJoin', args), + parseJoin(joinType, args), ), }) } diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index 79c74eb96..edb9811fa 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -82,6 +82,7 @@ import { FetchModifier } from '../operation-node/fetch-node.js' import { parseFetch } from '../parser/fetch-parser.js' import { TopModifier } from '../operation-node/top-node.js' import { parseTop } from '../parser/top-parser.js' +import { JoinType } from '../operation-node/join-node.js' export interface SelectQueryBuilder extends WhereInterface, @@ -2435,101 +2436,51 @@ class SelectQueryBuilderImpl } innerJoin(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('InnerJoin', args), - ), - }) + return this.#join('InnerJoin', args) } leftJoin(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LeftJoin', args), - ), - }) + return this.#join('LeftJoin', args) } rightJoin(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('RightJoin', args), - ), - }) + return this.#join('RightJoin', args) } fullJoin(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('FullJoin', args), - ), - }) + return this.#join('FullJoin', args) } crossJoin(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('CrossJoin', args), - ), - }) + return this.#join('CrossJoin', args) } innerJoinLateral(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LateralInnerJoin', args), - ), - }) + return this.#join('LateralInnerJoin', args) } leftJoinLateral(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LateralLeftJoin', args), - ), - }) + return this.#join('LateralLeftJoin', args) } crossJoinLateral(...args: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LateralCrossJoin', args), - ), - }) + return this.#join('LateralCrossJoin', args) } - crossApply(table: any): any { - return new SelectQueryBuilderImpl({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('CrossApply', [table]), - ), - }) + crossApply(...args: any): any { + return this.#join('CrossApply', args) + } + + outerApply(...args: any[]): any { + return this.#join('OuterApply', args) } - outerApply(table: any): any { + #join(joinType: JoinType, args: any[]): any { return new SelectQueryBuilderImpl({ ...this.#props, queryNode: QueryNode.cloneWithJoin( this.#props.queryNode, - parseJoin('OuterApply', [table]), + parseJoin(joinType, args), ), }) } diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index d35615cad..7555277e2 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -79,6 +79,7 @@ import { SelectExpressionFromOutputCallback, SelectExpressionFromOutputExpression, } from './output-interface.js' +import { JoinType } from '../operation-node/join-node.js' export class UpdateQueryBuilder implements @@ -365,13 +366,7 @@ export class UpdateQueryBuilder >(table: TE, callback: FN): UpdateQueryBuilderWithInnerJoin innerJoin(...args: any): any { - return new UpdateQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('InnerJoin', args), - ), - }) + return this.#join('InnerJoin', args) } /** @@ -393,13 +388,7 @@ export class UpdateQueryBuilder >(table: TE, callback: FN): UpdateQueryBuilderWithLeftJoin leftJoin(...args: any): any { - return new UpdateQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('LeftJoin', args), - ), - }) + return this.#join('LeftJoin', args) } /** @@ -421,13 +410,7 @@ export class UpdateQueryBuilder >(table: TE, callback: FN): UpdateQueryBuilderWithRightJoin rightJoin(...args: any): any { - return new UpdateQueryBuilder({ - ...this.#props, - queryNode: QueryNode.cloneWithJoin( - this.#props.queryNode, - parseJoin('RightJoin', args), - ), - }) + return this.#join('RightJoin', args) } /** @@ -449,11 +432,15 @@ export class UpdateQueryBuilder >(table: TE, callback: FN): UpdateQueryBuilderWithFullJoin fullJoin(...args: any): any { + return this.#join('FullJoin', args) + } + + #join(joinType: JoinType, args: any[]): any { return new UpdateQueryBuilder({ ...this.#props, queryNode: QueryNode.cloneWithJoin( this.#props.queryNode, - parseJoin('FullJoin', args), + parseJoin(joinType, args), ), }) } From 6eaf75496ed4acc183546808ff643f570e956fe9 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 18 Jan 2025 20:43:31 +0200 Subject: [PATCH 24/28] improve join tests dialect coverage. --- test/node/src/join.test.ts | 92 +++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/test/node/src/join.test.ts b/test/node/src/join.test.ts index 7dc10ca86..56e689081 100644 --- a/test/node/src/join.test.ts +++ b/test/node/src/join.test.ts @@ -654,37 +654,38 @@ for (const dialect of DIALECTS) { }) }) - if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { - describe('right join', () => { - it(`should right join a table`, async () => { - const query = ctx.db - .selectFrom('person') - .rightJoin('pet', 'pet.owner_id', 'person.id') - .selectAll() - .orderBy('person.first_name') - - testSql(query, dialect, { - postgres: { - sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, - parameters: [], - }, - mysql: { - sql: `select * from \`person\` right join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, - parameters: [], - }, - mssql: { - sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, - parameters: [], - }, - sqlite: NOT_SUPPORTED, - }) + describe('right join', () => { + it(`should right join a table`, async () => { + const query = ctx.db + .selectFrom('person') + .rightJoin('pet', 'pet.owner_id', 'person.id') + .selectAll() + .orderBy('person.first_name') - await query.execute() + testSql(query, dialect, { + postgres: { + sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, + mysql: { + sql: `select * from \`person\` right join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, + parameters: [], + }, + mssql: { + sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, + sqlite: { + sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, }) + + await query.execute() }) - } + }) - if (dialect === 'postgres' || dialect === 'mssql') { + if (dialect === 'postgres' || dialect === 'mssql' || dialect === 'sqlite') { describe('full join', () => { it(`should full join a table`, async () => { const query = ctx.db @@ -703,7 +704,10 @@ for (const dialect of DIALECTS) { sql: `select * from "person" full join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], }, - sqlite: NOT_SUPPORTED, + sqlite: { + sql: `select * from "person" full join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, }) await query.execute() @@ -742,7 +746,7 @@ for (const dialect of DIALECTS) { }) }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mysql') { describe('lateral join', () => { it('should join an expression laterally', async () => { const query = ctx.db @@ -764,7 +768,10 @@ for (const dialect of DIALECTS) { sql: `select "first_name", "p"."name" from "person" inner join lateral (select "name" from "pet" where "pet"."owner_id" = "person"."id") as "p" on true order by "first_name"`, parameters: [], }, - mysql: NOT_SUPPORTED, + mysql: { + sql: `select \`first_name\`, \`p\`.\`name\` from \`person\` inner join lateral (select \`name\` from \`pet\` where \`pet\`.\`owner_id\` = \`person\`.\`id\`) as \`p\` on true order by \`first_name\``, + parameters: [], + }, mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -797,7 +804,10 @@ for (const dialect of DIALECTS) { sql: `select "first_name", "p"."name" from "person" left join lateral (select "name" from "pet" where "pet"."owner_id" = "person"."id") as "p" on true order by "first_name"`, parameters: [], }, - mysql: NOT_SUPPORTED, + mysql: { + sql: `select \`first_name\`, \`p\`.\`name\` from \`person\` left join lateral (select \`name\` from \`pet\` where \`pet\`.\`owner_id\` = \`person\`.\`id\`) as \`p\` on true order by \`first_name\``, + parameters: [], + }, mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -813,14 +823,13 @@ for (const dialect of DIALECTS) { it('should cross join an expression laterally', async () => { const query = ctx.db .selectFrom('person') - .crossJoinLateral( - (eb) => - eb - .selectFrom('pet') - .innerJoin('person as owner', 'owner.id', 'pet.owner_id') - .select('name') - .whereRef('owner.gender', '=', 'person.gender') - .as('p'), + .crossJoinLateral((eb) => + eb + .selectFrom('pet') + .innerJoin('person as owner', 'owner.id', 'pet.owner_id') + .select('name') + .whereRef('owner.gender', '=', 'person.gender') + .as('p'), ) .select(['first_name', 'p.name']) .orderBy(['first_name', 'p.name']) @@ -830,7 +839,10 @@ for (const dialect of DIALECTS) { sql: `select "first_name", "p"."name" from "person" cross join lateral (select "name" from "pet" inner join "person" as "owner" on "owner"."id" = "pet"."owner_id" where "owner"."gender" = "person"."gender") as "p" order by "first_name", "p"."name"`, parameters: [], }, - mysql: NOT_SUPPORTED, + mysql: { + sql: `select \`first_name\`, \`p\`.\`name\` from \`person\` cross join lateral (select \`name\` from \`pet\` inner join \`person\` as \`owner\` on \`owner\`.\`id\` = \`pet\`.\`owner_id\` where \`owner\`.\`gender\` = \`person\`.\`gender\`) as \`p\` order by \`first_name\`, \`p\`.\`name\``, + parameters: [], + }, mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) From ca1163220d171cfb9f571a754498899b6dc55b9b Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 18 Jan 2025 21:17:30 +0200 Subject: [PATCH 25/28] minor ci tweaks. .. --- .github/workflows/preview.yml | 19 ++++++++++++++++++- .github/workflows/test.yml | 33 +++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2b9c88ccb..4ee9f13e6 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -1,6 +1,23 @@ name: preview -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + paths-ignore: + - '.github/workflows/test.yml' + - 'assets/**' + - 'docs/**' + - 'example/**' + - 'site/**' + - 'test/**' + - '.npmignore' + - '.prettierignore' + - '.prettierrc' + - '.renovaterc.json' + - '*.md' + - 'LICENSE' jobs: release: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 04897e3a6..894e9583b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,34 @@ name: tests on: push: - branches: [master, v*] + branches: + - master + paths-ignore: + - '.github/workflows/preview.yml' + - 'assets/**' + - 'docs/**' + - 'example/**' + - 'site/**' + - '.npmignore' + - '.prettierignore' + - '.prettierrc' + - '.renovaterc.json' + - '*.md' + - 'LICENSE' pull_request: - branches: [master, v*] + paths-ignore: + - '.github/workflows/preview.yml' + - 'assets/**' + - 'docs/**' + - 'example/**' + - 'site/**' + - '.npmignore' + - '.prettierignore' + - '.prettierrc' + - '.renovaterc.json' + - '*.md' + - 'LICENSE' + workflow_dispatch: jobs: node: @@ -14,7 +39,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.x, 23.x] steps: - uses: actions/checkout@v4 @@ -44,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.x, 23.x] steps: - uses: actions/checkout@v4 From 5af5f8097079ec2e0176e511ad8b7a5eba85ba31 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Sat, 25 Jan 2025 23:36:48 +0200 Subject: [PATCH 26/28] revisiting `orderBy`. (#1326) --- src/dialect/mssql/mssql-query-compiler.ts | 17 ++ src/index.ts | 7 + src/operation-node/collate-node.ts | 24 ++ src/operation-node/delete-query-node.ts | 30 ++- .../operation-node-transformer.ts | 10 + src/operation-node/operation-node-visitor.ts | 3 + src/operation-node/operation-node.ts | 1 + src/operation-node/order-by-item-node.ts | 16 +- src/operation-node/query-node.ts | 22 ++ src/operation-node/select-query-node.ts | 30 ++- src/operation-node/update-query-node.ts | 2 + src/parser/collate-parser.ts | 8 + src/parser/order-by-parser.ts | 120 +++++++--- .../aggregate-function-builder.ts | 102 +++++++-- src/query-builder/delete-query-builder.ts | 110 ++++------ src/query-builder/order-by-interface.ts | 202 +++++++++++++++++ src/query-builder/order-by-item-builder.ts | 85 ++++++++ src/query-builder/over-builder.ts | 58 ++++- src/query-builder/select-query-builder.ts | 169 ++++---------- src/query-builder/update-query-builder.ts | 63 ++++++ src/query-compiler/default-query-compiler.ts | 21 ++ test/node/src/order-by.test.ts | 113 ++++++++++ test/node/src/test-setup.ts | 4 +- test/node/src/update.test.ts | 5 +- test/ts-benchmarks/index.ts | 3 +- test/ts-benchmarks/order-by.bench.ts | 206 ++++++++++++++++++ ...lectFrom.bench.ts => select-from.bench.ts} | 7 +- 27 files changed, 1146 insertions(+), 292 deletions(-) create mode 100644 src/operation-node/collate-node.ts create mode 100644 src/parser/collate-parser.ts create mode 100644 src/query-builder/order-by-interface.ts create mode 100644 src/query-builder/order-by-item-builder.ts create mode 100644 test/ts-benchmarks/order-by.bench.ts rename test/ts-benchmarks/{selectFrom.bench.ts => select-from.bench.ts} (95%) diff --git a/src/dialect/mssql/mssql-query-compiler.ts b/src/dialect/mssql/mssql-query-compiler.ts index 3f68e51d2..2ed6dafdb 100644 --- a/src/dialect/mssql/mssql-query-compiler.ts +++ b/src/dialect/mssql/mssql-query-compiler.ts @@ -4,6 +4,9 @@ import { DropColumnNode } from '../../operation-node/drop-column-node.js' import { OffsetNode } from '../../operation-node/offset-node.js' import { MergeQueryNode } from '../../operation-node/merge-query-node.js' import { DefaultQueryCompiler } from '../../query-compiler/default-query-compiler.js' +import { CollateNode } from '../../operation-node/collate-node.js' + +const COLLATION_CHAR_REGEX = /^[a-z0-9_]$/i export class MssqlQueryCompiler extends DefaultQueryCompiler { protected override getCurrentParameterPlaceholder(): string { @@ -85,6 +88,20 @@ export class MssqlQueryCompiler extends DefaultQueryCompiler { this.append(';') } + protected override visitCollate(node: CollateNode): void { + this.append('collate ') + + const { name } = node.collation + + for (const char of name) { + if (!COLLATION_CHAR_REGEX.test(char)) { + throw new Error(`Invalid collation: ${name}`) + } + } + + this.append(name) + } + protected override announcesNewColumnDataType(): boolean { return false } diff --git a/src/index.ts b/src/index.ts index d95d35cc7..fcaca48cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ export * from './query-builder/where-interface.js' export * from './query-builder/returning-interface.js' export * from './query-builder/output-interface.js' export * from './query-builder/having-interface.js' +export * from './query-builder/order-by-interface.js' export * from './query-builder/select-query-builder.js' export * from './query-builder/insert-query-builder.js' export * from './query-builder/update-query-builder.js' @@ -28,6 +29,7 @@ export * from './query-builder/case-builder.js' export * from './query-builder/json-path-builder.js' export * from './query-builder/merge-query-builder.js' export * from './query-builder/merge-result.js' +export * from './query-builder/order-by-item-builder.js' export * from './raw-builder/raw-builder.js' export * from './raw-builder/sql.js' @@ -125,6 +127,7 @@ export * from './operation-node/binary-operation-node.js' export * from './operation-node/case-node.js' export * from './operation-node/cast-node.js' export * from './operation-node/check-constraint-node.js' +export * from './operation-node/collate-node.js' export * from './operation-node/column-definition-node.js' export * from './operation-node/column-node.js' export * from './operation-node/column-update-node.js' @@ -269,6 +272,9 @@ export { UpdateObject } from './parser/update-set-parser.js' export { OrderByExpression, OrderByDirectionExpression, + OrderByModifiers, + OrderByDirection, + OrderByModifiersCallbackExpression, } from './parser/order-by-parser.js' export { ComparisonOperatorExpression, @@ -281,3 +287,4 @@ export { OperandExpression, ExpressionOrFactory, } from './parser/expression-parser.js' +export { Collation } from './parser/collate-parser.js' diff --git a/src/operation-node/collate-node.ts b/src/operation-node/collate-node.ts new file mode 100644 index 000000000..1a7483132 --- /dev/null +++ b/src/operation-node/collate-node.ts @@ -0,0 +1,24 @@ +import { freeze } from '../util/object-utils.js' +import { IdentifierNode } from './identifier-node.js' +import { OperationNode } from './operation-node.js' + +export interface CollateNode extends OperationNode { + readonly kind: 'CollateNode' + readonly collation: IdentifierNode +} + +/** + * @internal + */ +export const CollateNode = { + is(node: OperationNode): node is CollateNode { + return node.kind === 'CollateNode' + }, + + create(collation: string): CollateNode { + return freeze({ + kind: 'CollateNode', + collation: IdentifierNode.create(collation), + }) + }, +} diff --git a/src/operation-node/delete-query-node.ts b/src/operation-node/delete-query-node.ts index 9930da3a4..f0df97a1d 100644 --- a/src/operation-node/delete-query-node.ts +++ b/src/operation-node/delete-query-node.ts @@ -12,6 +12,7 @@ import { ExplainNode } from './explain-node.js' import { UsingNode } from './using-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' +import { QueryNode } from './query-node.js' export interface DeleteQueryNode extends OperationNode { readonly kind: 'DeleteQueryNode' @@ -45,24 +46,21 @@ export const DeleteQueryNode = freeze({ }) }, - cloneWithOrderByItems( - deleteNode: DeleteQueryNode, + // TODO: remove in v0.29 + /** + * @deprecated Use `QueryNode.cloneWithoutOrderBy` instead. + */ + cloneWithOrderByItems: ( + node: DeleteQueryNode, items: ReadonlyArray, - ): DeleteQueryNode { - return freeze({ - ...deleteNode, - orderBy: deleteNode.orderBy - ? OrderByNode.cloneWithItems(deleteNode.orderBy, items) - : OrderByNode.create(items), - }) - }, + ) => QueryNode.cloneWithOrderByItems(node, items), - cloneWithoutOrderBy(deleteNode: DeleteQueryNode): DeleteQueryNode { - return freeze({ - ...deleteNode, - orderBy: undefined, - }) - }, + // TODO: remove in v0.29 + /** + * @deprecated Use `QueryNode.cloneWithoutOrderBy` instead. + */ + cloneWithoutOrderBy: (node: DeleteQueryNode) => + QueryNode.cloneWithoutOrderBy(node), cloneWithLimit( deleteNode: DeleteQueryNode, diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index af306c39e..7ac4001ed 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -96,6 +96,7 @@ import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' import { OrActionNode } from './or-action-node.js' +import { CollateNode } from './collate-node.js' /** * Transforms an operation node tree into another one. @@ -233,6 +234,7 @@ export class OperationNodeTransformer { TopNode: this.transformTop.bind(this), OutputNode: this.transformOutput.bind(this), OrActionNode: this.transformOrAction.bind(this), + CollateNode: this.transformCollate.bind(this), }) transformNode(node: T): T { @@ -502,6 +504,8 @@ export class OperationNodeTransformer { kind: 'OrderByItemNode', orderBy: this.transformNode(node.orderBy), direction: this.transformNode(node.direction), + collation: this.transformNode(node.collation), + nulls: node.nulls, }) } @@ -534,6 +538,7 @@ export class OperationNodeTransformer { limit: this.transformNode(node.limit), top: this.transformNode(node.top), output: this.transformNode(node.output), + orderBy: this.transformNode(node.orderBy), }) } @@ -1140,4 +1145,9 @@ export class OperationNodeTransformer { // An Object.freezed leaf node. No need to clone. return node } + + protected transformCollate(node: CollateNode): CollateNode { + // An Object.freezed leaf node. No need to clone. + return node + } } diff --git a/src/operation-node/operation-node-visitor.ts b/src/operation-node/operation-node-visitor.ts index 70d308ec0..ccaf4e03e 100644 --- a/src/operation-node/operation-node-visitor.ts +++ b/src/operation-node/operation-node-visitor.ts @@ -98,6 +98,7 @@ import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' import { OrActionNode } from './or-action-node.js' +import { CollateNode } from './collate-node.js' export abstract class OperationNodeVisitor { protected readonly nodeStack: OperationNode[] = [] @@ -203,6 +204,7 @@ export abstract class OperationNodeVisitor { TopNode: this.visitTop.bind(this), OutputNode: this.visitOutput.bind(this), OrActionNode: this.visitOrAction.bind(this), + CollateNode: this.visitCollate.bind(this), }) protected readonly visitNode = (node: OperationNode): void => { @@ -318,4 +320,5 @@ export abstract class OperationNodeVisitor { protected abstract visitTop(node: TopNode): void protected abstract visitOutput(node: OutputNode): void protected abstract visitOrAction(node: OrActionNode): void + protected abstract visitCollate(node: CollateNode): void } diff --git a/src/operation-node/operation-node.ts b/src/operation-node/operation-node.ts index a893a3951..bead77d4d 100644 --- a/src/operation-node/operation-node.ts +++ b/src/operation-node/operation-node.ts @@ -94,6 +94,7 @@ export type OperationNodeKind = | 'TopNode' | 'OutputNode' | 'OrActionNode' + | 'CollateNode' export interface OperationNode { readonly kind: OperationNodeKind diff --git a/src/operation-node/order-by-item-node.ts b/src/operation-node/order-by-item-node.ts index 5a7c2bff7..b362aabad 100644 --- a/src/operation-node/order-by-item-node.ts +++ b/src/operation-node/order-by-item-node.ts @@ -1,11 +1,15 @@ import { freeze } from '../util/object-utils.js' +import { CollateNode } from './collate-node.js' import { OperationNode } from './operation-node.js' +export type OrderByItemNodeProps = Omit + export interface OrderByItemNode extends OperationNode { readonly kind: 'OrderByItemNode' readonly orderBy: OperationNode - // TODO(samiko): Do we need an OrderByDirectionNode for consistency? readonly direction?: OperationNode + readonly nulls?: 'first' | 'last' + readonly collation?: CollateNode } /** @@ -23,4 +27,14 @@ export const OrderByItemNode = freeze({ direction, }) }, + + cloneWith( + node: OrderByItemNode, + props: OrderByItemNodeProps, + ): OrderByItemNode { + return freeze({ + ...node, + ...props, + }) + }, }) diff --git a/src/operation-node/query-node.ts b/src/operation-node/query-node.ts index c1725a132..e6aa5a657 100644 --- a/src/operation-node/query-node.ts +++ b/src/operation-node/query-node.ts @@ -14,6 +14,8 @@ import { Expression } from '../expression/expression.js' import { MergeQueryNode } from './merge-query-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' +import { OrderByNode } from './order-by-node.js' +import { OrderByItemNode } from './order-by-item-node.js' export type QueryNode = | SelectQueryNode @@ -29,6 +31,7 @@ type HasExplain = { explain?: ExplainNode } type HasTop = { top?: TopNode } type HasOutput = { output?: OutputNode } type HasEndModifiers = { endModifiers?: ReadonlyArray } +type HasOrderBy = { orderBy?: OrderByNode } /** * @internal @@ -127,4 +130,23 @@ export const QueryNode = freeze({ : OutputNode.create(selections), }) }, + + cloneWithOrderByItems( + node: T, + items: ReadonlyArray, + ): T { + return freeze({ + ...node, + orderBy: node.orderBy + ? OrderByNode.cloneWithItems(node.orderBy, items) + : OrderByNode.create(items), + }) + }, + + cloneWithoutOrderBy(node: T): T { + return freeze({ + ...node, + orderBy: undefined, + }) + }, }) diff --git a/src/operation-node/select-query-node.ts b/src/operation-node/select-query-node.ts index e111d3dfd..7686fd896 100644 --- a/src/operation-node/select-query-node.ts +++ b/src/operation-node/select-query-node.ts @@ -17,6 +17,7 @@ import { ExplainNode } from './explain-node.js' import { SetOperationNode } from './set-operation-node.js' import { FetchNode } from './fetch-node.js' import { TopNode } from './top-node.js' +import { QueryNode } from './query-node.js' export interface SelectQueryNode extends OperationNode { readonly kind: 'SelectQueryNode' @@ -101,17 +102,14 @@ export const SelectQueryNode = freeze({ }) }, - cloneWithOrderByItems( - selectNode: SelectQueryNode, + // TODO: remove in v0.29 + /** + * @deprecated Use `QueryNode.cloneWithoutOrderBy` instead. + */ + cloneWithOrderByItems: ( + node: SelectQueryNode, items: ReadonlyArray, - ): SelectQueryNode { - return freeze({ - ...selectNode, - orderBy: selectNode.orderBy - ? OrderByNode.cloneWithItems(selectNode.orderBy, items) - : OrderByNode.create(items), - }) - }, + ) => QueryNode.cloneWithOrderByItems(node, items), cloneWithGroupByItems( selectNode: SelectQueryNode, @@ -200,12 +198,12 @@ export const SelectQueryNode = freeze({ }) }, - cloneWithoutOrderBy(select: SelectQueryNode): SelectQueryNode { - return freeze({ - ...select, - orderBy: undefined, - }) - }, + // TODO: remove in v0.29 + /** + * @deprecated Use `QueryNode.cloneWithoutOrderBy` instead. + */ + cloneWithoutOrderBy: (node: SelectQueryNode) => + QueryNode.cloneWithoutOrderBy(node), cloneWithoutGroupBy(select: SelectQueryNode): SelectQueryNode { return freeze({ diff --git a/src/operation-node/update-query-node.ts b/src/operation-node/update-query-node.ts index a815a45c9..a06e82ce4 100644 --- a/src/operation-node/update-query-node.ts +++ b/src/operation-node/update-query-node.ts @@ -13,6 +13,7 @@ import { LimitNode } from './limit-node.js' import { TopNode } from './top-node.js' import { OutputNode } from './output-node.js' import { ListNode } from './list-node.js' +import { OrderByNode } from './order-by-node.js' export type UpdateValuesNode = ValueListNode | PrimitiveValueListNode @@ -30,6 +31,7 @@ export interface UpdateQueryNode extends OperationNode { readonly limit?: LimitNode readonly top?: TopNode readonly output?: OutputNode + readonly orderBy?: OrderByNode } /** diff --git a/src/parser/collate-parser.ts b/src/parser/collate-parser.ts new file mode 100644 index 000000000..35d6a185b --- /dev/null +++ b/src/parser/collate-parser.ts @@ -0,0 +1,8 @@ +export type Collation = + // anything super common or simple should be added here. + // https://sqlite.org/datatype3.html#collating_sequences + | 'nocase' + | 'binary' + | 'rtrim' + // otherwise, allow any string, while still providing autocompletion. + | (string & {}) diff --git a/src/parser/order-by-parser.ts b/src/parser/order-by-parser.ts index cc2aeb704..a03d60ab6 100644 --- a/src/parser/order-by-parser.ts +++ b/src/parser/order-by-parser.ts @@ -1,11 +1,33 @@ -import { isDynamicReferenceBuilder } from '../dynamic/dynamic-reference-builder.js' -import { Expression } from '../expression/expression.js' +import { + DynamicReferenceBuilder, + isDynamicReferenceBuilder, +} from '../dynamic/dynamic-reference-builder.js' +import { Expression, isExpression } from '../expression/expression.js' import { OperationNode } from '../operation-node/operation-node.js' import { OrderByItemNode } from '../operation-node/order-by-item-node.js' import { RawNode } from '../operation-node/raw-node.js' -import { isExpressionOrFactory, parseExpression } from './expression-parser.js' -import { StringReference, parseStringReference } from './reference-parser.js' -import { ReferenceExpression } from './reference-parser.js' +import { OrderByItemBuilder } from '../query-builder/order-by-item-builder.js' +import { logOnce } from '../util/log-once.js' +import { + ExpressionOrFactory, + isExpressionOrFactory, + parseExpression, +} from './expression-parser.js' +import { + ReferenceExpression, + StringReference, + parseStringReference, +} from './reference-parser.js' + +export type OrderByExpression = + | StringReference + | (keyof O & string) + | ExpressionOrFactory + | DynamicReferenceBuilder + +export type OrderByModifiers = + | OrderByDirection + | OrderByModifiersCallbackExpression export type OrderByDirection = 'asc' | 'desc' @@ -13,20 +35,32 @@ export function isOrderByDirection(thing: unknown): thing is OrderByDirection { return thing === 'asc' || thing === 'desc' } +export type OrderByModifiersCallbackExpression = ( + builder: OrderByItemBuilder, +) => OrderByItemBuilder + +// TODO: remove in v0.29 +/** + * @deprecated performance reasons, use {@link OrderByExpression} instead. + */ export type DirectedOrderByStringReference = `${ | StringReference | (keyof O & string)} ${OrderByDirection}` +// TODO: remove in v0.29 +/** + * @deprecated replaced with {@link OrderByModifiers} + */ +export type OrderByDirectionExpression = OrderByDirection | Expression + +// TODO: remove in v0.29 +/** + * @deprecated use {@link OrderByExpression} instead. + */ export type UndirectedOrderByExpression = | ReferenceExpression | (keyof O & string) -export type OrderByExpression = - | UndirectedOrderByExpression - | DirectedOrderByStringReference - -export type OrderByDirectionExpression = OrderByDirection | Expression - export function parseOrderBy(args: any[]): OrderByItemNode[] { if (args.length === 2) { return [parseOrderByItem(args[0], args[1])] @@ -36,6 +70,10 @@ export function parseOrderBy(args: any[]): OrderByItemNode[] { const [orderBy] = args if (Array.isArray(orderBy)) { + logOnce( + 'orderBy(array) is deprecated, use multiple orderBy calls instead.', + ) + return orderBy.map((item) => parseOrderByItem(item)) } @@ -48,23 +86,20 @@ export function parseOrderBy(args: any[]): OrderByItemNode[] { } export function parseOrderByItem( - ref: ReferenceExpression, - direction?: OrderByDirectionExpression, + expr: OrderByExpression, + modifiers?: OrderByModifiers, ): OrderByItemNode { - const parsedRef = parseOrderByExpression(ref) + const parsedRef = parseOrderByExpression(expr) if (OrderByItemNode.is(parsedRef)) { - if (direction) { + if (modifiers) { throw new Error('Cannot specify direction twice!') } return parsedRef } - return OrderByItemNode.create( - parsedRef, - parseOrderByDirectionExpression(direction), - ) + return parseOrderByWithModifiers(parsedRef, modifiers) } function parseOrderByExpression( @@ -81,29 +116,46 @@ function parseOrderByExpression( const [ref, direction] = expr.split(' ') if (direction) { - if (!isOrderByDirection(direction)) { - throw new Error(`Invalid order by direction: ${direction}`) - } - - return OrderByItemNode.create( - parseStringReference(ref), - parseOrderByDirectionExpression(direction as any), + logOnce( + "`orderBy('column asc')` is deprecated. Use `orderBy('column', 'asc')` instead.", ) + + return parseOrderByWithModifiers(parseStringReference(ref), direction) } return parseStringReference(expr) } -function parseOrderByDirectionExpression( - expr?: OrderByDirectionExpression, -): OperationNode | undefined { - if (!expr) { - return undefined +function parseOrderByWithModifiers( + expr: OperationNode, + modifiers: + | string + | OrderByModifiersCallbackExpression + // TODO: remove in v0.29 + | Expression + | undefined, +): OrderByItemNode { + if (typeof modifiers === 'string') { + if (!isOrderByDirection(modifiers)) { + throw new Error(`Invalid order by direction: ${modifiers}`) + } + + return OrderByItemNode.create(expr, RawNode.createWithSql(modifiers)) + } + + if (isExpression(modifiers)) { + logOnce( + "`orderBy(..., expr)` is deprecated. Use `orderBy(..., 'asc')` or `orderBy(..., (ob) => ...)` instead.", + ) + + return OrderByItemNode.create(expr, modifiers.toOperationNode()) } - if (expr === 'asc' || expr === 'desc') { - return RawNode.createWithSql(expr) + const node = OrderByItemNode.create(expr) + + if (!modifiers) { + return node } - return expr.toOperationNode() + return modifiers(new OrderByItemBuilder({ node })).toOperationNode() } diff --git a/src/query-builder/aggregate-function-builder.ts b/src/query-builder/aggregate-function-builder.ts index ae4cb64a6..d9a8403c5 100644 --- a/src/query-builder/aggregate-function-builder.ts +++ b/src/query-builder/aggregate-function-builder.ts @@ -9,10 +9,7 @@ import { AliasedExpression, Expression, } from '../expression/expression.js' -import { - ReferenceExpression, - SimpleReferenceExpression, -} from '../parser/reference-parser.js' +import { ReferenceExpression } from '../parser/reference-parser.js' import { ComparisonOperatorExpression, OperandValueExpressionOrList, @@ -22,12 +19,16 @@ import { import { SqlBool } from '../util/type-utils.js' import { ExpressionOrFactory } from '../parser/expression-parser.js' import { - OrderByDirectionExpression, + DirectedOrderByStringReference, + OrderByExpression, + OrderByModifiers, parseOrderBy, } from '../parser/order-by-parser.js' +import { OrderByInterface } from './order-by-interface.js' +import { QueryNode } from '../operation-node/query-node.js' export class AggregateFunctionBuilder - implements AliasableExpression + implements OrderByInterface, AliasableExpression { readonly #props: AggregateFunctionBuilderProps @@ -124,15 +125,53 @@ export class AggregateFunctionBuilder * inner join "pet" ON "pet"."owner_id" = "person"."id" * ``` */ - orderBy>( - orderBy: OE, - direction?: OrderByDirectionExpression, - ): AggregateFunctionBuilder { + orderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): AggregateFunctionBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2-2.6x more compile-time instantiations compared to multiple chained `orderBy(expr, modifiers?)` calls (in `order by` clauses with reasonable item counts), and has broken autocompletion. + */ + orderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >(exprs: ReadonlyArray): AggregateFunctionBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2.9x more compile-time instantiations compared to a `orderBy(expr, direction)` call. + */ + orderBy>( + expr: OE, + ): AggregateFunctionBuilder + + // TODO: remove in v0.29 + /** + * @deprecated Use `orderBy(expr, (ob) => ...)` instead. + */ + orderBy>( + expr: OE, + modifiers: Expression, + ): AggregateFunctionBuilder + + orderBy(...args: any[]): any { return new AggregateFunctionBuilder({ ...this.#props, - aggregateFunctionNode: AggregateFunctionNode.cloneWithOrderBy( + aggregateFunctionNode: QueryNode.cloneWithOrderByItems( + this.#props.aggregateFunctionNode, + parseOrderBy(args), + ), + }) + } + + clearOrderBy(): AggregateFunctionBuilder { + return new AggregateFunctionBuilder({ + ...this.#props, + aggregateFunctionNode: QueryNode.cloneWithoutOrderBy( this.#props.aggregateFunctionNode, - parseOrderBy([orderBy, direction]), ), }) } @@ -165,15 +204,44 @@ export class AggregateFunctionBuilder * from "person" * ``` */ - withinGroupOrderBy>( - orderBy: OE, - direction?: OrderByDirectionExpression, - ): AggregateFunctionBuilder { + withinGroupOrderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): AggregateFunctionBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2-2.6x more compile-time instantiations compared to multiple chained `withinGroupOrderBy(expr, modifiers?)` calls (in `order by` clauses with reasonable item counts), and has broken autocompletion. + */ + withinGroupOrderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >(exprs: ReadonlyArray): AggregateFunctionBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2.9x more compile-time instantiations compared to a `withinGroupOrderBy(expr, direction)` call. + */ + withinGroupOrderBy>( + expr: OE, + ): AggregateFunctionBuilder + + // TODO: remove in v0.29 + /** + * @deprecated Use `withinGroupOrderBy(expr, (ob) => ...)` instead. + */ + withinGroupOrderBy>( + expr: OE, + modifiers: Expression, + ): AggregateFunctionBuilder + + withinGroupOrderBy(...args: any[]): any { return new AggregateFunctionBuilder({ ...this.#props, aggregateFunctionNode: AggregateFunctionNode.cloneWithOrderBy( this.#props.aggregateFunctionNode, - parseOrderBy([orderBy, direction]), + parseOrderBy(args), true, ), }) diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index a79703b5e..22e0ca23a 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -51,9 +51,10 @@ import { DeleteResult } from './delete-result.js' import { DeleteQueryNode } from '../operation-node/delete-query-node.js' import { LimitNode } from '../operation-node/limit-node.js' import { - OrderByDirectionExpression, OrderByExpression, parseOrderBy, + OrderByModifiers, + DirectedOrderByStringReference, } from '../parser/order-by-parser.js' import { Explainable, ExplainFormat } from '../util/explainable.js' import { AliasedExpression, Expression } from '../expression/expression.js' @@ -79,12 +80,14 @@ import { SelectExpressionFromOutputExpression, } from './output-interface.js' import { JoinType } from '../operation-node/join-node.js' +import { OrderByInterface } from './order-by-interface.js' export class DeleteQueryBuilder implements WhereInterface, MultiTableReturningInterface, OutputInterface, + OrderByInterface, OperationNodeSource, Compilable, Explainable, @@ -711,79 +714,60 @@ export class DeleteQueryBuilder } /** - * Clears the `order by` clause from the query. - * - * ### Examples - * - * ```ts - * await db.deleteFrom('pet') - * .returningAll() - * .where('name', '=', 'Max') - * .orderBy('id') - * .clearOrderBy() - * .execute() - * ``` - * - * The generated SQL(PostgreSQL): - * - * ```sql - * delete from "pet" where "name" = "Max" returning * - * ``` + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. */ - clearOrderBy(): DeleteQueryBuilder { - return new DeleteQueryBuilder({ - ...this.#props, - queryNode: DeleteQueryNode.cloneWithoutOrderBy(this.#props.queryNode), - }) - } + orderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): DeleteQueryBuilder + // TODO: remove in v0.29 /** - * Adds an `order by` clause to the query. - * - * `orderBy` calls are additive. To order by multiple columns, call `orderBy` - * multiple times. - * - * The first argument is the expression to order by and the second is the - * order (`asc` or `desc`). - * - * An `order by` clause in a delete query is only supported by some dialects - * like MySQL. - * - * See {@link SelectQueryBuilder.orderBy} for more examples. - * - * ### Examples - * - * Delete 5 oldest items in a table: - * - * ```ts - * await db - * .deleteFrom('pet') - * .orderBy('created_at') - * .limit(5) - * .execute() - * ``` - * - * The generated SQL (MySQL): - * - * ```sql - * delete from `pet` - * order by `created_at` - * limit ? - * ``` + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + * @deprecated It does ~2-2.6x more compile-time instantiations compared to multiple chained `orderBy(expr, modifiers?)` calls (in `order by` clauses with reasonable item counts), and has broken autocompletion. */ - orderBy( - orderBy: OrderByExpression, - direction?: OrderByDirectionExpression, - ): DeleteQueryBuilder { + orderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >(exprs: ReadonlyArray): DeleteQueryBuilder + + // TODO: remove in v0.29 + /** + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + * @deprecated It does ~2.9x more compile-time instantiations compared to a `orderBy(expr, direction)` call. + */ + orderBy>( + expr: OE, + ): DeleteQueryBuilder + + // TODO: remove in v0.29 + /** + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + * @deprecated Use `orderBy(expr, (ob) => ...)` instead. + */ + orderBy>( + expr: OE, + modifiers: Expression, + ): DeleteQueryBuilder + + orderBy(...args: any[]): any { return new DeleteQueryBuilder({ ...this.#props, - queryNode: DeleteQueryNode.cloneWithOrderByItems( + queryNode: QueryNode.cloneWithOrderByItems( this.#props.queryNode, - parseOrderBy([orderBy, direction]), + parseOrderBy(args), ), }) } + clearOrderBy(): DeleteQueryBuilder { + return new DeleteQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithoutOrderBy(this.#props.queryNode), + }) + } + /** * Adds a limit clause to the query. * diff --git a/src/query-builder/order-by-interface.ts b/src/query-builder/order-by-interface.ts new file mode 100644 index 000000000..29800a7bc --- /dev/null +++ b/src/query-builder/order-by-interface.ts @@ -0,0 +1,202 @@ +import { Expression } from '../expression/expression.js' +import { + OrderByExpression, + DirectedOrderByStringReference, + OrderByModifiers, +} from '../parser/order-by-parser.js' + +export interface OrderByInterface { + /** + * Adds an `order by` clause to the query. + * + * `orderBy` calls are additive. Meaning, additional `orderBy` calls append to + * the existing order by clause. + * + * `orderBy` is supported in select queries on all dialects. In MySQL, you can + * also use `orderBy` in update and delete queries. + * + * In a single call you can add a single column/expression or multiple columns/expressions. + * + * Single column/expression calls can have 1-2 arguments. The first argument is + * the expression to order by, while the second optional argument is the direction + * (`asc` or `desc`), a callback that accepts and returns an {@link OrderByItemBuilder} + * or an expression. + * + * See {@link clearOrderBy} to remove the `order by` clause from a query. + * + * ### Examples + * + * Single column/expression per call: + * + * ```ts + * await db + * .selectFrom('person') + * .select('person.first_name as fn') + * .orderBy('id') + * .orderBy('fn', 'desc') + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select "person"."first_name" as "fn" + * from "person" + * order by "id", "fn" desc + * ``` + * + * Multiple columns/expressions per call: + * + * ```ts + * await db + * .selectFrom('person') + * .select('person.first_name as fn') + * .orderBy(['id', 'fn']) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select "person"."first_name" as "fn" + * from "person" + * order by "id", "fn" + * ``` + * + * Building advanced modifiers: + * + * ```ts + * await db + * .selectFrom('person') + * .select('person.first_name as fn') + * .orderBy('id', (ob) => ob.desc().nullsFirst()) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select "person"."first_name" as "fn" + * from "person" + * order by "id" desc nulls first + * ``` + * + * The order by expression can also be a raw sql expression or a subquery + * in addition to column references: + * + * ```ts + * import { sql } from 'kysely' + * + * await db + * .selectFrom('person') + * .selectAll() + * .orderBy((eb) => eb.selectFrom('pet') + * .select('pet.name') + * .whereRef('pet.owner_id', '=', 'person.id') + * .limit(1) + * ) + * .orderBy( + * sql`concat(first_name, last_name) asc` + * ) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select * + * from "person" + * order by + * ( select "pet"."name" + * from "pet" + * where "pet"."owner_id" = "person"."id" + * limit $1 + * ) asc, + * concat(first_name, last_name) asc + * ``` + * + * `dynamic.ref` can be used to refer to columns not known at + * compile time: + * + * ```ts + * async function someQuery(orderBy: string) { + * const { ref } = db.dynamic + * + * return await db + * .selectFrom('person') + * .select('person.first_name as fn') + * .orderBy(ref(orderBy)) + * .execute() + * } + * + * someQuery('fn') + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select "person"."first_name" as "fn" + * from "person" + * order by "fn" + * ``` + */ + orderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): OrderByInterface + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2-2.6x more compile-time instantiations compared to multiple chained `orderBy(expr, modifiers?)` calls (in `order by` clauses with reasonable item counts), and has broken autocompletion. + */ + orderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >( + exprs: ReadonlyArray, + ): OrderByInterface + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2.9x more compile-time instantiations compared to a `orderBy(expr, direction)` call. + */ + orderBy>( + expr: OE, + ): OrderByInterface + + // TODO: remove in v0.29 + /** + * @deprecated Use `orderBy(expr, (ob) => ...)` instead. + */ + orderBy>( + expr: OE, + modifiers: Expression, + ): OrderByInterface + + /** + * Clears the `order by` clause from the query. + * + * See {@link orderBy} for adding an `order by` clause or item to a query. + * + * ### Examples + * + * ```ts + * const query = db + * .selectFrom('person') + * .selectAll() + * .orderBy('id', 'desc') + * + * const results = await query + * .clearOrderBy() + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * select * from "person" + * ``` + */ + clearOrderBy(): OrderByInterface +} diff --git a/src/query-builder/order-by-item-builder.ts b/src/query-builder/order-by-item-builder.ts new file mode 100644 index 000000000..f2ad8323c --- /dev/null +++ b/src/query-builder/order-by-item-builder.ts @@ -0,0 +1,85 @@ +import { CollateNode } from '../operation-node/collate-node.js' +import { OperationNodeSource } from '../operation-node/operation-node-source.js' +import { OrderByItemNode } from '../operation-node/order-by-item-node.js' +import { RawNode } from '../operation-node/raw-node.js' +import { Collation } from '../parser/collate-parser.js' +import { freeze } from '../util/object-utils.js' + +export class OrderByItemBuilder implements OperationNodeSource { + readonly #props: OrderByItemBuilderProps + + constructor(props: OrderByItemBuilderProps) { + this.#props = freeze(props) + } + + /** + * Adds `desc` to the `order by` item. + * + * See {@link asc} for the opposite. + */ + desc(): OrderByItemBuilder { + return new OrderByItemBuilder({ + node: OrderByItemNode.cloneWith(this.#props.node, { + direction: RawNode.createWithSql('desc'), + }), + }) + } + + /** + * Adds `asc` to the `order by` item. + * + * See {@link desc} for the opposite. + */ + asc(): OrderByItemBuilder { + return new OrderByItemBuilder({ + node: OrderByItemNode.cloneWith(this.#props.node, { + direction: RawNode.createWithSql('asc'), + }), + }) + } + + /** + * Adds `nulls last` to the `order by` item. + * + * This is only supported by some dialects like PostgreSQL and SQLite. + * + * See {@link nullsFirst} for the opposite. + */ + nullsLast(): OrderByItemBuilder { + return new OrderByItemBuilder({ + node: OrderByItemNode.cloneWith(this.#props.node, { nulls: 'last' }), + }) + } + + /** + * Adds `nulls first` to the `order by` item. + * + * This is only supported by some dialects like PostgreSQL and SQLite. + * + * See {@link nullsLast} for the opposite. + */ + nullsFirst(): OrderByItemBuilder { + return new OrderByItemBuilder({ + node: OrderByItemNode.cloneWith(this.#props.node, { nulls: 'first' }), + }) + } + + /** + * Adds `collate ` to the `order by` item. + */ + collate(collation: Collation): OrderByItemBuilder { + return new OrderByItemBuilder({ + node: OrderByItemNode.cloneWith(this.#props.node, { + collation: CollateNode.create(collation), + }), + }) + } + + toOperationNode(): OrderByItemNode { + return this.#props.node + } +} + +export interface OrderByItemBuilderProps { + readonly node: OrderByItemNode +} diff --git a/src/query-builder/over-builder.ts b/src/query-builder/over-builder.ts index 675f4950c..f945ce57a 100644 --- a/src/query-builder/over-builder.ts +++ b/src/query-builder/over-builder.ts @@ -1,8 +1,11 @@ -import { DynamicReferenceBuilder } from '../dynamic/dynamic-reference-builder.js' +import { Expression } from '../expression/expression.js' import { OperationNodeSource } from '../operation-node/operation-node-source.js' import { OverNode } from '../operation-node/over-node.js' +import { QueryNode } from '../operation-node/query-node.js' import { - OrderByDirectionExpression, + DirectedOrderByStringReference, + OrderByExpression, + OrderByModifiers, parseOrderBy, } from '../parser/order-by-parser.js' import { @@ -10,11 +13,11 @@ import { PartitionByExpression, PartitionByExpressionOrList, } from '../parser/partition-by-parser.js' -import { StringReference } from '../parser/reference-parser.js' import { freeze } from '../util/object-utils.js' +import { OrderByInterface } from './order-by-interface.js' export class OverBuilder - implements OperationNodeSource + implements OrderByInterface, OperationNodeSource { readonly #props: OverBuilderProps @@ -23,7 +26,7 @@ export class OverBuilder } /** - * Adds an order by clause item inside the over function. + * Adds an `order by` clause or item inside the `over` function. * * ```ts * const result = await db @@ -43,18 +46,53 @@ export class OverBuilder * from "person" * ``` */ - orderBy | DynamicReferenceBuilder>( - orderBy: OE, - direction?: OrderByDirectionExpression, - ): OverBuilder { + orderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): OverBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2-2.6x more compile-time instantiations compared to multiple chained `orderBy(expr, modifiers?)` calls (in `order by` clauses with reasonable item counts), and has broken autocompletion. + */ + orderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >(exprs: ReadonlyArray): OverBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2.9x more compile-time instantiations compared to a `orderBy(expr, direction)` call. + */ + orderBy>( + expr: OE, + ): OverBuilder + + // TODO: remove in v0.29 + /** + * @deprecated Use `orderBy(expr, (ob) => ...)` instead. + */ + orderBy>( + expr: OE, + modifiers: Expression, + ): OverBuilder + + orderBy(...args: any[]): any { return new OverBuilder({ overNode: OverNode.cloneWithOrderByItems( this.#props.overNode, - parseOrderBy([orderBy, direction]), + parseOrderBy(args), ), }) } + clearOrderBy(): OverBuilder { + return new OverBuilder({ + overNode: QueryNode.cloneWithoutOrderBy(this.#props.overNode), + }) + } + /** * Adds partition by clause item/s inside the over function. * diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts index edb9811fa..e87b4a67f 100644 --- a/src/query-builder/select-query-builder.ts +++ b/src/query-builder/select-query-builder.ts @@ -34,10 +34,9 @@ import { SqlBool, } from '../util/type-utils.js' import { - OrderByDirectionExpression, - OrderByExpression, DirectedOrderByStringReference, - UndirectedOrderByExpression, + OrderByExpression, + OrderByModifiers, parseOrderBy, } from '../parser/order-by-parser.js' import { LimitNode } from '../operation-node/limit-node.js' @@ -83,10 +82,12 @@ import { parseFetch } from '../parser/fetch-parser.js' import { TopModifier } from '../operation-node/top-node.js' import { parseTop } from '../parser/top-parser.js' import { JoinType } from '../operation-node/join-node.js' +import { OrderByInterface } from './order-by-interface.js' export interface SelectQueryBuilder extends WhereInterface, HavingInterface, + OrderByInterface, SelectQueryBuilderExpression, Compilable, Explainable, @@ -984,130 +985,6 @@ export interface SelectQueryBuilder table: TE, ): SelectQueryBuilderWithLeftJoin - /** - * Adds an `order by` clause to the query. - * - * `orderBy` calls are additive. Meaning, additional `orderBy` calls append to - * the existing order by clause. - * - * In a single call you can add a single column/expression or multiple columns/expressions. - * - * Single column/expression calls can have 1-2 arguments. The first argument is - * the expression to order by (optionally including the direction) while the second - * optional argument is the direction (`asc` or `desc`). - * - * ### Examples - * - * Single column/expression per call: - * - * ```ts - * await db - * .selectFrom('person') - * .select('person.first_name as fn') - * .orderBy('id') - * .orderBy('fn desc') - * .execute() - * ``` - * - * ```ts - * await db - * .selectFrom('person') - * .select('person.first_name as fn') - * .orderBy('id') - * .orderBy('fn', 'desc') - * .execute() - * ``` - * - * The generated SQL (PostgreSQL): - * - * ```sql - * select "person"."first_name" as "fn" - * from "person" - * order by "id" asc, "fn" desc - * ``` - * - * Multiple columns/expressions per call: - * - * ```ts - * await db - * .selectFrom('person') - * .select('person.first_name as fn') - * .orderBy(['id', 'fn desc']) - * .execute() - * ``` - * - * The order by expression can also be a raw sql expression or a subquery - * in addition to column references: - * - * ```ts - * import { sql } from 'kysely' - * - * await db - * .selectFrom('person') - * .selectAll() - * .orderBy((eb) => eb.selectFrom('pet') - * .select('pet.name') - * .whereRef('pet.owner_id', '=', 'person.id') - * .limit(1) - * ) - * .orderBy( - * sql`concat(first_name, last_name)` - * ) - * .execute() - * ``` - * - * The generated SQL (PostgreSQL): - * - * ```sql - * select * - * from "person" - * order by - * ( select "pet"."name" - * from "pet" - * where "pet"."owner_id" = "person"."id" - * limit 1 - * ) asc, - * concat(first_name, last_name) asc - * ``` - * - * `dynamic.ref` can be used to refer to columns not known at - * compile time: - * - * ```ts - * async function someQuery(orderBy: string) { - * const { ref } = db.dynamic - * - * return await db - * .selectFrom('person') - * .select('person.first_name as fn') - * .orderBy(ref(orderBy)) - * .execute() - * } - * - * someQuery('fn') - * ``` - * - * The generated SQL (PostgreSQL): - * - * ```sql - * select "person"."first_name" as "fn" - * from "person" - * order by "fn" asc - * ``` - */ - orderBy>( - orderBy: OE, - direction?: OrderByDirectionExpression, - ): SelectQueryBuilder - - orderBy>( - ref: OE, - ): SelectQueryBuilder - - orderBy>( - refs: ReadonlyArray, - ): SelectQueryBuilder - /** * Adds a `group by` clause to the query. * @@ -1210,6 +1087,40 @@ export interface SelectQueryBuilder groupBy: GE, ): SelectQueryBuilder + orderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): SelectQueryBuilder + + // TODO: remove in v0.29 + /** + * @deprecated It does ~2-2.5x more compile-time instantiations than multiple `orderBy(expr, modifiers?)` calls, and has broken autocompletion. + */ + orderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >( + exprs: ReadonlyArray, + ): SelectQueryBuilder + + // TODO: remove in v0.29 + /** + * @deprecated Use orderBy(expr, direction) instead. + */ + orderBy>( + expr: OE, + ): SelectQueryBuilder + + // TODO: remove in v0.29 + /** + * @deprecated Use `orderBy(expr, (ob) => ...)` instead. + */ + orderBy>( + expr: OE, + modifiers: Expression, + ): SelectQueryBuilder + /** * Adds a limit clause to the query. * @@ -2488,7 +2399,7 @@ class SelectQueryBuilderImpl orderBy(...args: any[]): SelectQueryBuilder { return new SelectQueryBuilderImpl({ ...this.#props, - queryNode: SelectQueryNode.cloneWithOrderByItems( + queryNode: QueryNode.cloneWithOrderByItems( this.#props.queryNode, parseOrderBy(args), ), @@ -2662,7 +2573,7 @@ class SelectQueryBuilderImpl clearOrderBy(): SelectQueryBuilder { return new SelectQueryBuilderImpl({ ...this.#props, - queryNode: SelectQueryNode.cloneWithoutOrderBy(this.#props.queryNode), + queryNode: QueryNode.cloneWithoutOrderBy(this.#props.queryNode), }) } diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index 7555277e2..5c95b9396 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -80,12 +80,20 @@ import { SelectExpressionFromOutputExpression, } from './output-interface.js' import { JoinType } from '../operation-node/join-node.js' +import { OrderByInterface } from './order-by-interface.js' +import { + DirectedOrderByStringReference, + OrderByExpression, + OrderByModifiers, + parseOrderBy, +} from '../parser/order-by-parser.js' export class UpdateQueryBuilder implements WhereInterface, MultiTableReturningInterface, OutputInterface, + OrderByInterface, OperationNodeSource, Compilable, Explainable, @@ -445,6 +453,61 @@ export class UpdateQueryBuilder }) } + /** + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + */ + orderBy>( + expr: OE, + modifiers?: OrderByModifiers, + ): UpdateQueryBuilder + + // TODO: remove in v0.29 + /** + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + * @deprecated It does ~2-2.6x more compile-time instantiations compared to multiple chained `orderBy(expr, modifiers?)` calls (in `order by` clauses with reasonable item counts), and has broken autocompletion. + */ + orderBy< + OE extends + | OrderByExpression + | DirectedOrderByStringReference, + >(exprs: ReadonlyArray): UpdateQueryBuilder + + // TODO: remove in v0.29 + /** + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + * @deprecated It does ~2.9x more compile-time instantiations compared to a `orderBy(expr, direction)` call. + */ + orderBy>( + expr: OE, + ): UpdateQueryBuilder + + // TODO: remove in v0.29 + /** + * @description This is only supported by some dialects like MySQL or SQLite with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. + * @deprecated Use `orderBy(expr, (ob) => ...)` instead. + */ + orderBy>( + expr: OE, + modifiers: Expression, + ): UpdateQueryBuilder + + orderBy(...args: any[]): any { + return new UpdateQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithOrderByItems( + this.#props.queryNode, + parseOrderBy(args), + ), + }) + } + + clearOrderBy(): UpdateQueryBuilder { + return new UpdateQueryBuilder({ + ...this.#props, + queryNode: QueryNode.cloneWithoutOrderBy(this.#props.queryNode), + }) + } + /** * Adds a limit clause to the update query for supported databases, such as MySQL. * diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 63bb1072f..67207a4fe 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -114,6 +114,7 @@ import { OutputNode } from '../operation-node/output-node.js' import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' import { OrActionNode } from '../operation-node/or-action-node.js' import { logOnce } from '../util/log-once.js' +import { CollateNode } from '../operation-node/collate-node.js' export class DefaultQueryCompiler extends OperationNodeVisitor @@ -758,10 +759,20 @@ export class DefaultQueryCompiler protected override visitOrderByItem(node: OrderByItemNode): void { this.visitNode(node.orderBy) + if (node.collation) { + this.append(' ') + this.visitNode(node.collation) + } + if (node.direction) { this.append(' ') this.visitNode(node.direction) } + + if (node.nulls) { + this.append(' nulls ') + this.append(node.nulls) + } } protected override visitGroupBy(node: GroupByNode): void { @@ -835,6 +846,11 @@ export class DefaultQueryCompiler this.visitNode(node.where) } + if (node.orderBy) { + this.append(' ') + this.visitNode(node.orderBy) + } + if (node.limit) { this.append(' ') this.visitNode(node.limit) @@ -1684,6 +1700,11 @@ export class DefaultQueryCompiler this.append(node.action) } + protected override visitCollate(node: CollateNode): void { + this.append('collate ') + this.visitNode(node.collation) + } + protected append(str: string): void { this.#sql += str } diff --git a/test/node/src/order-by.test.ts b/test/node/src/order-by.test.ts index f205c2c44..7595c0623 100644 --- a/test/node/src/order-by.test.ts +++ b/test/node/src/order-by.test.ts @@ -268,6 +268,119 @@ for (const dialect of DIALECTS) { await query.execute() }) + it('order by a direction via builder', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .orderBy('last_name', (ob) => ob.desc()) + .orderBy('first_name', (ob) => ob.asc()) + + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" order by "last_name" desc, "first_name" asc', + parameters: [], + }, + mysql: { + sql: 'select * from `person` order by `last_name` desc, `first_name` asc', + parameters: [], + }, + mssql: { + sql: 'select * from "person" order by "last_name" desc, "first_name" asc', + parameters: [], + }, + sqlite: { + sql: 'select * from "person" order by "last_name" desc, "first_name" asc', + parameters: [], + }, + }) + + await query.execute() + }) + + if (dialect === 'postgres' || dialect === 'sqlite') { + it('order by nulls first', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .orderBy('last_name', (ob) => ob.desc().nullsFirst()) + .orderBy('first_name', (ob) => ob.nullsFirst()) + + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" order by "last_name" desc nulls first, "first_name" nulls first', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" order by "last_name" desc nulls first, "first_name" nulls first', + parameters: [], + }, + }) + + await query.execute() + }) + + it('order by nulls last', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .orderBy('last_name', (ob) => ob.desc().nullsLast()) + .orderBy('first_name', (ob) => ob.nullsLast()) + + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" order by "last_name" desc nulls last, "first_name" nulls last', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" order by "last_name" desc nulls last, "first_name" nulls last', + parameters: [], + }, + }) + + await query.execute() + }) + } + + it('order by collate', async () => { + const collation = { + postgres: 'pg_c_utf8', + mysql: 'utf8mb4_general_ci', + mssql: 'Latin1_General_CI_AS', + sqlite: 'nocase', + }[dialect] + + const query = ctx.db + .selectFrom('person') + .selectAll() + .orderBy('last_name', (ob) => ob.collate(collation).desc()) + .orderBy('first_name', (ob) => ob.collate(collation)) + + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" order by "last_name" collate "pg_c_utf8" desc, "first_name" collate "pg_c_utf8"', + parameters: [], + }, + mysql: { + sql: 'select * from `person` order by `last_name` collate `utf8mb4_general_ci` desc, `first_name` collate `utf8mb4_general_ci`', + parameters: [], + }, + mssql: { + sql: 'select * from "person" order by "last_name" collate Latin1_General_CI_AS desc, "first_name" collate Latin1_General_CI_AS', + parameters: [], + }, + sqlite: { + sql: 'select * from "person" order by "last_name" collate "nocase" desc, "first_name" collate "nocase"', + parameters: [], + }, + }) + + await query.execute() + }) + if (dialect === 'postgres') { it('order by raw expression in direction', async () => { const query = ctx.db diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 1c6fce5ef..1b75acb62 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -38,7 +38,7 @@ import { } from '../../../' import { OrderByDirection, - UndirectedOrderByExpression, + OrderByExpression, } from '../../../dist/cjs/parser/order-by-parser' import type { ConnectionConfiguration } from 'tedious' @@ -509,7 +509,7 @@ export function limit>( export function orderBy>( orderBy: QB extends SelectQueryBuilder - ? UndirectedOrderByExpression + ? OrderByExpression : never, direction: OrderByDirection | undefined, dialect: BuiltInDialect, diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index adcc8849e..1997e787a 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -547,16 +547,17 @@ for (const dialect of DIALECTS) { expect(result.numChangedRows).to.equal(0n) }) - it('should limit the amount of updated rows', async () => { + it('should order and limit the amount of updated rows', async () => { const query = ctx.db .updateTable('person') .set({ first_name: 'Foo' }) + .orderBy('first_name') .limit(2) testSql(query, dialect, { postgres: NOT_SUPPORTED, mysql: { - sql: 'update `person` set `first_name` = ? limit ?', + sql: 'update `person` set `first_name` = ? order by `first_name` limit ?', parameters: ['Foo', 2], }, mssql: NOT_SUPPORTED, diff --git a/test/ts-benchmarks/index.ts b/test/ts-benchmarks/index.ts index 81d8c59b6..deff90a96 100644 --- a/test/ts-benchmarks/index.ts +++ b/test/ts-benchmarks/index.ts @@ -1 +1,2 @@ -import './selectFrom.bench.js' +import './select-from.bench.js' +import './order-by.bench.js' diff --git a/test/ts-benchmarks/order-by.bench.ts b/test/ts-benchmarks/order-by.bench.ts new file mode 100644 index 000000000..695f32b9f --- /dev/null +++ b/test/ts-benchmarks/order-by.bench.ts @@ -0,0 +1,206 @@ +import { bench } from '@ark/attest' +import type { DB } from '../typings/test-d/huge-db.test-d' +import { + type SelectQueryBuilder, + sql, + type Kysely, +} from '../../dist/esm/index.js' + +declare const kysely: Kysely +declare const kyselyAny: Kysely + +let query: SelectQueryBuilder< + DB, + 'my_table', + { + e862ca: string | null + } +> +let queryAny: SelectQueryBuilder + +console.log('orderBy.bench.ts:\n') + +bench.baseline(() => { + query = kysely + .selectFrom('my_table') + .select('my_table.col_6f7a0a5f582c69dd4c6be0a819e862cb as e862ca') + + queryAny = kyselyAny + .selectFrom('my_table') + .select('my_table.col_6f7a0a5f582c69dd4c6be0a819e862cb as e862ca') +}) + +bench('kysely..orderBy(column)', () => + query.orderBy('col_164b7896ec8e770207febe0812c5f052'), +).types([118, 'instantiations']) + +bench('kysely..orderBy(~column)', () => + // @ts-expect-error + query.orderBy('col_164b7896ec8e770207febe0812c5f052_'), +).types([365, 'instantiations']) + +bench('kysely..orderBy(O)', () => query.orderBy('e862ca')).types([ + 118, + 'instantiations', +]) + +bench('kysely..orderBy(column, asc)', () => + query.orderBy('col_164b7896ec8e770207febe0812c5f052', 'asc'), +).types([118, 'instantiations']) + +bench('kysely..orderBy(~column, asc)', () => + // @ts-expect-error + query.orderBy('col_164b7896ec8e770207febe0812c5f052_', 'asc'), +).types([183, 'instantiations']) + +bench('kysely..orderBy(column, ~asc)', () => + // @ts-expect-error + query.orderBy('col_164b7896ec8e770207febe0812c5f052', 'asc_'), +).types([171, 'instantiations']) + +bench('kysely..orderBy(column, desc)', () => + query.orderBy('col_164b7896ec8e770207febe0812c5f052', 'desc'), +).types([118, 'instantiations']) + +bench('kysely..orderBy(column, ob)', async () => + query.orderBy('col_164b7896ec8e770207febe0812c5f052', (ob) => + ob.asc().nullsLast(), + ), +).types([119, 'instantiations']) + +bench('kysely..orderBy(sql)', () => + query.orderBy(sql`col_164b7896ec8e770207febe0812c5f052 asc nulls first`), +).types([146, 'instantiations']) + +bench('kysely..orderBy(select)', () => + query.orderBy( + kysely + .selectFrom('table_000a8a0cb7f265a624c851d3e7f8b946') + .select( + 'table_000a8a0cb7f265a624c851d3e7f8b946.col_454ff479a3b5a9ef082d9be9ac02a6f4', + ) + .limit(1), + ), +).types([671, 'instantiations']) + +bench('kysely..orderBy(eb => select)', () => + query.orderBy((eb) => + eb + .selectFrom('table_000a8a0cb7f265a624c851d3e7f8b946') + .select( + 'table_000a8a0cb7f265a624c851d3e7f8b946.col_454ff479a3b5a9ef082d9be9ac02a6f4', + ) + .limit(1), + ), +).types([882, 'instantiations']) + +bench('deprecated - kysely..orderBy(column desc)', () => + query.orderBy('col_164b7896ec8e770207febe0812c5f052 desc'), +).types([346, 'instantiations']) + +bench('deprecated - kysely..orderBy([column])', () => + query.orderBy(['col_164b7896ec8e770207febe0812c5f052']), +).types([307, 'instantiations']) + +bench('kysely..orderBy(column).orderBy(column)', () => + query + .orderBy('col_164b7896ec8e770207febe0812c5f052') + .orderBy('col_1d726898491fbca9a8dac855d2be1be8'), +).types([124, 'instantiations']) + +bench('deprecated - kysely..orderBy([column, column])', () => + query.orderBy([ + 'col_164b7896ec8e770207febe0812c5f052', + 'col_6f7a0a5f582c69dd4c6be0a819e862cb', + ]), +).types([307, 'instantiations']) + +bench('kysely..orderBy(column).orderBy(column, desc)', () => + query + .orderBy('col_1d726898491fbca9a8dac855d2be1be8') + .orderBy('col_164b7896ec8e770207febe0812c5f052', 'desc'), +).types([124, 'instantiations']) + +bench('deprecated - kysely..orderBy([column, column desc])', () => + query.orderBy([ + 'col_6f7a0a5f582c69dd4c6be0a819e862cb', + 'col_164b7896ec8e770207febe0812c5f052 desc', + ]), +).types([307, 'instantiations']) + +bench('kysely..orderBy(column).orderBy(column).orderBy(column)', () => + query + .orderBy('col_164b7896ec8e770207febe0812c5f052') + .orderBy('col_1d726898491fbca9a8dac855d2be1be8') + .orderBy('col_af4e225b70a9bbd83cc3bc0e7ef24cfa'), +).types([130, 'instantiations']) + +bench('deprecated - kysely..orderBy([column, column, column])', () => + query.orderBy([ + 'col_164b7896ec8e770207febe0812c5f052', + 'col_6f7a0a5f582c69dd4c6be0a819e862cb', + 'col_af4e225b70a9bbd83cc3bc0e7ef24cfa', + ]), +).types([307, 'instantiations']) + +// + +bench('kyselyAny..orderBy(column)', () => + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052'), +).types([118, 'instantiations']) + +bench('kyselyAny..orderBy(~column)', () => + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052_'), +).types([118, 'instantiations']) + +bench('kyselyAny..orderBy(O)', () => queryAny.orderBy('e862ca')).types([ + 118, + 'instantiations', +]) + +bench('kyselyAny..orderBy(column, asc)', () => + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052', 'asc'), +).types([118, 'instantiations']) + +bench('kyselyAny..orderBy(~column, asc)', () => + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052_', 'asc'), +).types([118, 'instantiations']) + +bench('kyselyAny..orderBy(column, ~asc)', () => + // @ts-expect-error + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052', 'asc_'), +).types([171, 'instantiations']) + +bench('kyselyAny..orderBy(column, desc)', () => + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052', 'desc'), +).types([118, 'instantiations']) + +bench('kyselyAny..orderBy(column, ob)', async () => + queryAny.orderBy('col_164b7896ec8e770207febe0812c5f052', (ob) => + ob.asc().nullsLast(), + ), +).types([119, 'instantiations']) + +bench('kyselyAny..orderBy(sql)', () => + queryAny.orderBy(sql`col_164b7896ec8e770207febe0812c5f052 asc nulls first`), +).types([146, 'instantiations']) + +bench('kyselyAny..orderBy(select)', () => + queryAny.orderBy( + kyselyAny + .selectFrom('table_000a8a0cb7f265a624c851d3e7f8b946') + .select( + 'table_000a8a0cb7f265a624c851d3e7f8b946.col_454ff479a3b5a9ef082d9be9ac02a6f4', + ) + .limit(1), + ), +).types([408, 'instantiations']) + +bench('kyselyAny..orderBy(eb => select)', () => + queryAny.orderBy((eb) => + eb + .selectFrom('table_000a8a0cb7f265a624c851d3e7f8b946') + .select('col_164b7896ec8e770207febe0812c5f052') + .limit(1), + ), +).types([529, 'instantiations']) diff --git a/test/ts-benchmarks/selectFrom.bench.ts b/test/ts-benchmarks/select-from.bench.ts similarity index 95% rename from test/ts-benchmarks/selectFrom.bench.ts rename to test/ts-benchmarks/select-from.bench.ts index ec90beea9..4427d699e 100644 --- a/test/ts-benchmarks/selectFrom.bench.ts +++ b/test/ts-benchmarks/select-from.bench.ts @@ -5,6 +5,8 @@ import type { Kysely } from '../../dist/esm/index.js' declare const kysely: Kysely declare const kyselyAny: Kysely +console.log('selectFrom.bench.ts:\n') + bench.baseline(() => {}) bench('kysely.selectFrom(table)', () => { @@ -12,6 +14,7 @@ bench('kysely.selectFrom(table)', () => { }).types([372, 'instantiations']) bench('kysely.selectFrom(~table)', () => { + // @ts-expect-error return kysely.selectFrom('my_table2') }).types([6864, 'instantiations']) @@ -24,6 +27,7 @@ bench('kysely.selectFrom([table])', () => { }).types([427, 'instantiations']) bench('kysely.selectFrom([~table])', () => { + // @ts-expect-error return kysely.selectFrom(['my_table2']) }).types([6914, 'instantiations']) @@ -41,6 +45,7 @@ bench('kysely.selectFrom([table, table])', () => { bench('kysely.selectFrom([table, ~table])', () => { return kysely.selectFrom([ 'my_table', + // @ts-expect-error 'table_000a8a0cb7f265a624c851d3e7f8b9462', ]) }).types([6917, 'instantiations']) @@ -66,7 +71,7 @@ bench('kyselyAny.selectFrom(table as alias)', () => { bench('kyselyAny.selectFrom([table])', () => { return kyselyAny.selectFrom(['my_table']) -}).types([165, 'instantiations']) +}).types([179, 'instantiations']) bench('kyselyAny.selectFrom([~table])', () => { return kyselyAny.selectFrom(['my_table2']) From 3b753c7297ba8ceeef846a43a22807cc0753fb1a Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Mon, 27 Jan 2025 01:20:10 +0200 Subject: [PATCH 27/28] add `queryId` to `CompiledQuery` to allow async communication between more components. (#176) --- src/dialect/mssql/mssql-driver.ts | 1 - src/dialect/mysql/mysql-driver.ts | 38 +- src/dialect/postgres/postgres-driver.ts | 55 +- src/dialect/sqlite/sqlite-driver.ts | 42 +- src/driver/database-connection.ts | 6 - src/index.ts | 1 + .../operation-node-transformer.ts | 799 +++++++++++------- src/plugin/camel-case/camel-case-plugin.ts | 2 +- .../camel-case/camel-case-transformer.ts | 8 +- .../deduplicate-joins-plugin.ts | 2 +- .../deduplicate-joins-transformer.ts | 22 +- .../handle-empty-in-lists-plugin.ts | 2 +- .../immediate-value/immediate-value-plugin.ts | 2 +- .../immediate-value-transformer.ts | 5 +- src/plugin/with-schema/with-schema-plugin.ts | 2 +- .../with-schema/with-schema-transformer.ts | 50 +- src/query-builder/delete-query-builder.ts | 7 +- src/query-builder/insert-query-builder.ts | 3 +- src/query-builder/update-query-builder.ts | 4 +- src/query-compiler/compiled-query.ts | 3 + src/query-compiler/default-query-compiler.ts | 4 +- src/query-compiler/query-compiler.ts | 3 +- src/query-executor/default-query-executor.ts | 5 +- src/query-executor/query-executor-base.ts | 34 +- src/schema/create-index-builder.ts | 5 +- test/node/src/query-id.test.ts | 75 ++ test/node/src/raw-sql.test.ts | 15 +- test/node/src/test-setup.ts | 3 +- 28 files changed, 740 insertions(+), 458 deletions(-) create mode 100644 test/node/src/query-id.test.ts diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 8ba60841e..4dad49e37 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -339,7 +339,6 @@ class MssqlRequest { (event: 'completed' | 'chunkReady' | 'error', error?: unknown) => void > readonly #tedious: Tedious - #error: Error | any[] | undefined #rowCount: number | undefined constructor(props: MssqlRequestProps) { diff --git a/src/dialect/mysql/mysql-driver.ts b/src/dialect/mysql/mysql-driver.ts index 084b6cb6f..4b89c8812 100644 --- a/src/dialect/mysql/mysql-driver.ts +++ b/src/dialect/mysql/mysql-driver.ts @@ -7,6 +7,7 @@ import { parseSavepointCommand } from '../../parser/savepoint-parser.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' import { QueryCompiler } from '../../query-compiler/query-compiler.js' import { isFunction, isObject, freeze } from '../../util/object-utils.js' +import { createQueryId } from '../../util/query-id.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' import { MysqlDialectConfig, @@ -98,7 +99,10 @@ export class MysqlDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('savepoint', savepointName)), + compileQuery( + parseSavepointCommand('savepoint', savepointName), + createQueryId(), + ), ) } @@ -108,7 +112,10 @@ export class MysqlDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('rollback to', savepointName)), + compileQuery( + parseSavepointCommand('rollback to', savepointName), + createQueryId(), + ), ) } @@ -118,7 +125,10 @@ export class MysqlDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('release savepoint', savepointName)), + compileQuery( + parseSavepointCommand('release savepoint', savepointName), + createQueryId(), + ), ) } @@ -157,16 +167,6 @@ class MysqlConnection implements DatabaseConnection { if (isOkPacket(result)) { const { insertId, affectedRows, changedRows } = result - const numAffectedRows = - affectedRows !== undefined && affectedRows !== null - ? BigInt(affectedRows) - : undefined - - const numChangedRows = - changedRows !== undefined && changedRows !== null - ? BigInt(changedRows) - : undefined - return { insertId: insertId !== undefined && @@ -174,10 +174,14 @@ class MysqlConnection implements DatabaseConnection { insertId.toString() !== '0' ? BigInt(insertId) : undefined, - // TODO: remove. - numUpdatedOrDeletedRows: numAffectedRows, - numAffectedRows, - numChangedRows, + numAffectedRows: + affectedRows !== undefined && affectedRows !== null + ? BigInt(affectedRows) + : undefined, + numChangedRows: + changedRows !== undefined && changedRows !== null + ? BigInt(changedRows) + : undefined, rows: [], } } else if (Array.isArray(result)) { diff --git a/src/dialect/postgres/postgres-driver.ts b/src/dialect/postgres/postgres-driver.ts index 0321e548b..e91e558a5 100644 --- a/src/dialect/postgres/postgres-driver.ts +++ b/src/dialect/postgres/postgres-driver.ts @@ -3,15 +3,11 @@ import { QueryResult, } from '../../driver/database-connection.js' import { Driver, TransactionSettings } from '../../driver/driver.js' -import { IdentifierNode } from '../../operation-node/identifier-node.js' -import { RawNode } from '../../operation-node/raw-node.js' import { parseSavepointCommand } from '../../parser/savepoint-parser.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' -import { - QueryCompiler, - RootOperationNode, -} from '../../query-compiler/query-compiler.js' +import { QueryCompiler } from '../../query-compiler/query-compiler.js' import { isFunction, freeze } from '../../util/object-utils.js' +import { createQueryId } from '../../util/query-id.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' import { PostgresCursorConstructor, @@ -91,7 +87,10 @@ export class PostgresDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('savepoint', savepointName)), + compileQuery( + parseSavepointCommand('savepoint', savepointName), + createQueryId(), + ), ) } @@ -101,7 +100,10 @@ export class PostgresDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('rollback to', savepointName)), + compileQuery( + parseSavepointCommand('rollback to', savepointName), + createQueryId(), + ), ) } @@ -111,7 +113,10 @@ export class PostgresDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('release', savepointName)), + compileQuery( + parseSavepointCommand('release', savepointName), + createQueryId(), + ), ) } @@ -143,28 +148,20 @@ class PostgresConnection implements DatabaseConnection { async executeQuery(compiledQuery: CompiledQuery): Promise> { try { - const result = await this.#client.query(compiledQuery.sql, [ - ...compiledQuery.parameters, - ]) - - if ( - result.command === 'INSERT' || - result.command === 'UPDATE' || - result.command === 'DELETE' || - result.command === 'MERGE' - ) { - const numAffectedRows = BigInt(result.rowCount) - - return { - // TODO: remove. - numUpdatedOrDeletedRows: numAffectedRows, - numAffectedRows, - rows: result.rows ?? [], - } - } + const { command, rowCount, rows } = await this.#client.query( + compiledQuery.sql, + [...compiledQuery.parameters], + ) return { - rows: result.rows ?? [], + numAffectedRows: + command === 'INSERT' || + command === 'UPDATE' || + command === 'DELETE' || + command === 'MERGE' + ? BigInt(rowCount) + : undefined, + rows: rows ?? [], } } catch (err) { throw extendStackTrace(err, new Error()) diff --git a/src/dialect/sqlite/sqlite-driver.ts b/src/dialect/sqlite/sqlite-driver.ts index 5aefb32ef..53a64369e 100644 --- a/src/dialect/sqlite/sqlite-driver.ts +++ b/src/dialect/sqlite/sqlite-driver.ts @@ -8,6 +8,7 @@ import { parseSavepointCommand } from '../../parser/savepoint-parser.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' import { QueryCompiler } from '../../query-compiler/query-compiler.js' import { freeze, isFunction } from '../../util/object-utils.js' +import { createQueryId } from '../../util/query-id.js' import { SqliteDatabase, SqliteDialectConfig } from './sqlite-dialect-config.js' export class SqliteDriver implements Driver { @@ -58,7 +59,10 @@ export class SqliteDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('savepoint', savepointName)), + compileQuery( + parseSavepointCommand('savepoint', savepointName), + createQueryId(), + ), ) } @@ -68,7 +72,10 @@ export class SqliteDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('rollback to', savepointName)), + compileQuery( + parseSavepointCommand('rollback to', savepointName), + createQueryId(), + ), ) } @@ -78,7 +85,10 @@ export class SqliteDriver implements Driver { compileQuery: QueryCompiler['compileQuery'], ): Promise { await connection.executeQuery( - compileQuery(parseSavepointCommand('release', savepointName)), + compileQuery( + parseSavepointCommand('release', savepointName), + createQueryId(), + ), ) } @@ -106,23 +116,19 @@ class SqliteConnection implements DatabaseConnection { return Promise.resolve({ rows: stmt.all(parameters) as O[], }) - } else { - const { changes, lastInsertRowid } = stmt.run(parameters) + } - const numAffectedRows = - changes !== undefined && changes !== null ? BigInt(changes) : undefined + const { changes, lastInsertRowid } = stmt.run(parameters) - return Promise.resolve({ - // TODO: remove. - numUpdatedOrDeletedRows: numAffectedRows, - numAffectedRows, - insertId: - lastInsertRowid !== undefined && lastInsertRowid !== null - ? BigInt(lastInsertRowid) - : undefined, - rows: [], - }) - } + return Promise.resolve({ + numAffectedRows: + changes !== undefined && changes !== null ? BigInt(changes) : undefined, + insertId: + lastInsertRowid !== undefined && lastInsertRowid !== null + ? BigInt(lastInsertRowid) + : undefined, + rows: [], + }) } async *streamQuery( diff --git a/src/driver/database-connection.ts b/src/driver/database-connection.ts index cca2b4647..6d9db8d33 100644 --- a/src/driver/database-connection.ts +++ b/src/driver/database-connection.ts @@ -14,12 +14,6 @@ export interface DatabaseConnection { } export interface QueryResult { - /** - * @deprecated use {@link QueryResult.numAffectedRows} instead. - */ - // TODO: remove. - readonly numUpdatedOrDeletedRows?: bigint - /** * This is defined for insert, update, delete and merge queries and contains * the number of rows the query inserted/updated/deleted. diff --git a/src/index.ts b/src/index.ts index fcaca48cd..ccbd695f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -238,6 +238,7 @@ export { } from './util/type-utils.js' export * from './util/infer-result.js' export { logOnce } from './util/log-once.js' +export { createQueryId, QueryId } from './util/query-id.js' export { SelectExpression, diff --git a/src/operation-node/operation-node-transformer.ts b/src/operation-node/operation-node-transformer.ts index 7ac4001ed..acae4e4e7 100644 --- a/src/operation-node/operation-node-transformer.ts +++ b/src/operation-node/operation-node-transformer.ts @@ -97,6 +97,7 @@ import { OutputNode } from './output-node.js' import { RefreshMaterializedViewNode } from './refresh-materialized-view-node.js' import { OrActionNode } from './or-action-node.js' import { CollateNode } from './collate-node.js' +import { QueryId } from '../util/query-id.js' /** * Transforms an operation node tree into another one. @@ -136,7 +137,11 @@ import { CollateNode } from './collate-node.js' export class OperationNodeTransformer { protected readonly nodeStack: OperationNode[] = [] - readonly #transformers: Record = freeze({ + readonly #transformers: Record< + OperationNodeKind, + // TODO: make `queryId` required in v0.29 ? + (node: any, queryId?: QueryId) => any + > = freeze({ AliasNode: this.transformAlias.bind(this), ColumnNode: this.transformColumn.bind(this), IdentifierNode: this.transformIdentifier.bind(this), @@ -237,381 +242,446 @@ export class OperationNodeTransformer { CollateNode: this.transformCollate.bind(this), }) - transformNode(node: T): T { + transformNode( + node: T, + queryId?: QueryId, + ): T { if (!node) { return node } this.nodeStack.push(node) - const out = this.transformNodeImpl(node) + const out = this.transformNodeImpl(node, queryId) this.nodeStack.pop() return freeze(out) as T } - protected transformNodeImpl(node: T): T { - return this.#transformers[node.kind](node) + protected transformNodeImpl( + node: T, + queryId?: QueryId, + ): T { + return this.#transformers[node.kind](node, queryId) } protected transformNodeList< T extends ReadonlyArray | undefined, - >(list: T): T { + >(list: T, queryId?: QueryId): T { if (!list) { return list } - return freeze(list.map((node) => this.transformNode(node))) as T + return freeze(list.map((node) => this.transformNode(node, queryId))) as T } - protected transformSelectQuery(node: SelectQueryNode): SelectQueryNode { + protected transformSelectQuery( + node: SelectQueryNode, + queryId?: QueryId, + ): SelectQueryNode { return requireAllProps({ kind: 'SelectQueryNode', - from: this.transformNode(node.from), - selections: this.transformNodeList(node.selections), - distinctOn: this.transformNodeList(node.distinctOn), - joins: this.transformNodeList(node.joins), - groupBy: this.transformNode(node.groupBy), - orderBy: this.transformNode(node.orderBy), - where: this.transformNode(node.where), - frontModifiers: this.transformNodeList(node.frontModifiers), - endModifiers: this.transformNodeList(node.endModifiers), - limit: this.transformNode(node.limit), - offset: this.transformNode(node.offset), - with: this.transformNode(node.with), - having: this.transformNode(node.having), - explain: this.transformNode(node.explain), - setOperations: this.transformNodeList(node.setOperations), - fetch: this.transformNode(node.fetch), - top: this.transformNode(node.top), - }) - } - - protected transformSelection(node: SelectionNode): SelectionNode { + from: this.transformNode(node.from, queryId), + selections: this.transformNodeList(node.selections, queryId), + distinctOn: this.transformNodeList(node.distinctOn, queryId), + joins: this.transformNodeList(node.joins, queryId), + groupBy: this.transformNode(node.groupBy, queryId), + orderBy: this.transformNode(node.orderBy, queryId), + where: this.transformNode(node.where, queryId), + frontModifiers: this.transformNodeList(node.frontModifiers, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), + limit: this.transformNode(node.limit, queryId), + offset: this.transformNode(node.offset, queryId), + with: this.transformNode(node.with, queryId), + having: this.transformNode(node.having, queryId), + explain: this.transformNode(node.explain, queryId), + setOperations: this.transformNodeList(node.setOperations, queryId), + fetch: this.transformNode(node.fetch, queryId), + top: this.transformNode(node.top, queryId), + }) + } + + protected transformSelection( + node: SelectionNode, + queryId?: QueryId, + ): SelectionNode { return requireAllProps({ kind: 'SelectionNode', - selection: this.transformNode(node.selection), + selection: this.transformNode(node.selection, queryId), }) } - protected transformColumn(node: ColumnNode): ColumnNode { + protected transformColumn(node: ColumnNode, queryId?: QueryId): ColumnNode { return requireAllProps({ kind: 'ColumnNode', - column: this.transformNode(node.column), + column: this.transformNode(node.column, queryId), }) } - protected transformAlias(node: AliasNode): AliasNode { + protected transformAlias(node: AliasNode, queryId?: QueryId): AliasNode { return requireAllProps({ kind: 'AliasNode', - node: this.transformNode(node.node), - alias: this.transformNode(node.alias), + node: this.transformNode(node.node, queryId), + alias: this.transformNode(node.alias, queryId), }) } - protected transformTable(node: TableNode): TableNode { + protected transformTable(node: TableNode, queryId?: QueryId): TableNode { return requireAllProps({ kind: 'TableNode', - table: this.transformNode(node.table), + table: this.transformNode(node.table, queryId), }) } - protected transformFrom(node: FromNode): FromNode { + protected transformFrom(node: FromNode, queryId?: QueryId): FromNode { return requireAllProps({ kind: 'FromNode', - froms: this.transformNodeList(node.froms), + froms: this.transformNodeList(node.froms, queryId), }) } - protected transformReference(node: ReferenceNode): ReferenceNode { + protected transformReference( + node: ReferenceNode, + queryId?: QueryId, + ): ReferenceNode { return requireAllProps({ kind: 'ReferenceNode', - column: this.transformNode(node.column), - table: this.transformNode(node.table), + column: this.transformNode(node.column, queryId), + table: this.transformNode(node.table, queryId), }) } - protected transformAnd(node: AndNode): AndNode { + protected transformAnd(node: AndNode, queryId?: QueryId): AndNode { return requireAllProps({ kind: 'AndNode', - left: this.transformNode(node.left), - right: this.transformNode(node.right), + left: this.transformNode(node.left, queryId), + right: this.transformNode(node.right, queryId), }) } - protected transformOr(node: OrNode): OrNode { + protected transformOr(node: OrNode, queryId?: QueryId): OrNode { return requireAllProps({ kind: 'OrNode', - left: this.transformNode(node.left), - right: this.transformNode(node.right), + left: this.transformNode(node.left, queryId), + right: this.transformNode(node.right, queryId), }) } - protected transformValueList(node: ValueListNode): ValueListNode { + protected transformValueList( + node: ValueListNode, + queryId?: QueryId, + ): ValueListNode { return requireAllProps({ kind: 'ValueListNode', - values: this.transformNodeList(node.values), + values: this.transformNodeList(node.values, queryId), }) } - protected transformParens(node: ParensNode): ParensNode { + protected transformParens(node: ParensNode, queryId?: QueryId): ParensNode { return requireAllProps({ kind: 'ParensNode', - node: this.transformNode(node.node), + node: this.transformNode(node.node, queryId), }) } - protected transformJoin(node: JoinNode): JoinNode { + protected transformJoin(node: JoinNode, queryId?: QueryId): JoinNode { return requireAllProps({ kind: 'JoinNode', joinType: node.joinType, - table: this.transformNode(node.table), - on: this.transformNode(node.on), + table: this.transformNode(node.table, queryId), + on: this.transformNode(node.on, queryId), }) } - protected transformRaw(node: RawNode): RawNode { + protected transformRaw(node: RawNode, queryId?: QueryId): RawNode { return requireAllProps({ kind: 'RawNode', sqlFragments: freeze([...node.sqlFragments]), - parameters: this.transformNodeList(node.parameters), + parameters: this.transformNodeList(node.parameters, queryId), }) } - protected transformWhere(node: WhereNode): WhereNode { + protected transformWhere(node: WhereNode, queryId?: QueryId): WhereNode { return requireAllProps({ kind: 'WhereNode', - where: this.transformNode(node.where), + where: this.transformNode(node.where, queryId), }) } - protected transformInsertQuery(node: InsertQueryNode): InsertQueryNode { + protected transformInsertQuery( + node: InsertQueryNode, + queryId?: QueryId, + ): InsertQueryNode { return requireAllProps({ kind: 'InsertQueryNode', - into: this.transformNode(node.into), - columns: this.transformNodeList(node.columns), - values: this.transformNode(node.values), - returning: this.transformNode(node.returning), - onConflict: this.transformNode(node.onConflict), - onDuplicateKey: this.transformNode(node.onDuplicateKey), - endModifiers: this.transformNodeList(node.endModifiers), - with: this.transformNode(node.with), + into: this.transformNode(node.into, queryId), + columns: this.transformNodeList(node.columns, queryId), + values: this.transformNode(node.values, queryId), + returning: this.transformNode(node.returning, queryId), + onConflict: this.transformNode(node.onConflict, queryId), + onDuplicateKey: this.transformNode(node.onDuplicateKey, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), + with: this.transformNode(node.with, queryId), ignore: node.ignore, - orAction: this.transformNode(node.orAction), + orAction: this.transformNode(node.orAction, queryId), replace: node.replace, - explain: this.transformNode(node.explain), + explain: this.transformNode(node.explain, queryId), defaultValues: node.defaultValues, - top: this.transformNode(node.top), - output: this.transformNode(node.output), + top: this.transformNode(node.top, queryId), + output: this.transformNode(node.output, queryId), }) } - protected transformValues(node: ValuesNode): ValuesNode { + protected transformValues(node: ValuesNode, queryId?: QueryId): ValuesNode { return requireAllProps({ kind: 'ValuesNode', - values: this.transformNodeList(node.values), + values: this.transformNodeList(node.values, queryId), }) } - protected transformDeleteQuery(node: DeleteQueryNode): DeleteQueryNode { + protected transformDeleteQuery( + node: DeleteQueryNode, + queryId?: QueryId, + ): DeleteQueryNode { return requireAllProps({ kind: 'DeleteQueryNode', - from: this.transformNode(node.from), - using: this.transformNode(node.using), - joins: this.transformNodeList(node.joins), - where: this.transformNode(node.where), - returning: this.transformNode(node.returning), - endModifiers: this.transformNodeList(node.endModifiers), - with: this.transformNode(node.with), - orderBy: this.transformNode(node.orderBy), - limit: this.transformNode(node.limit), - explain: this.transformNode(node.explain), - top: this.transformNode(node.top), - output: this.transformNode(node.output), - }) - } - - protected transformReturning(node: ReturningNode): ReturningNode { + from: this.transformNode(node.from, queryId), + using: this.transformNode(node.using, queryId), + joins: this.transformNodeList(node.joins, queryId), + where: this.transformNode(node.where, queryId), + returning: this.transformNode(node.returning, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), + with: this.transformNode(node.with, queryId), + orderBy: this.transformNode(node.orderBy, queryId), + limit: this.transformNode(node.limit, queryId), + explain: this.transformNode(node.explain, queryId), + top: this.transformNode(node.top, queryId), + output: this.transformNode(node.output, queryId), + }) + } + + protected transformReturning( + node: ReturningNode, + queryId?: QueryId, + ): ReturningNode { return requireAllProps({ kind: 'ReturningNode', - selections: this.transformNodeList(node.selections), + selections: this.transformNodeList(node.selections, queryId), }) } - protected transformCreateTable(node: CreateTableNode): CreateTableNode { + protected transformCreateTable( + node: CreateTableNode, + queryId?: QueryId, + ): CreateTableNode { return requireAllProps({ kind: 'CreateTableNode', - table: this.transformNode(node.table), - columns: this.transformNodeList(node.columns), - constraints: this.transformNodeList(node.constraints), + table: this.transformNode(node.table, queryId), + columns: this.transformNodeList(node.columns, queryId), + constraints: this.transformNodeList(node.constraints, queryId), temporary: node.temporary, ifNotExists: node.ifNotExists, onCommit: node.onCommit, - frontModifiers: this.transformNodeList(node.frontModifiers), - endModifiers: this.transformNodeList(node.endModifiers), - selectQuery: this.transformNode(node.selectQuery), + frontModifiers: this.transformNodeList(node.frontModifiers, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), + selectQuery: this.transformNode(node.selectQuery, queryId), }) } protected transformColumnDefinition( node: ColumnDefinitionNode, + queryId?: QueryId, ): ColumnDefinitionNode { return requireAllProps({ kind: 'ColumnDefinitionNode', - column: this.transformNode(node.column), - dataType: this.transformNode(node.dataType), - references: this.transformNode(node.references), + column: this.transformNode(node.column, queryId), + dataType: this.transformNode(node.dataType, queryId), + references: this.transformNode(node.references, queryId), primaryKey: node.primaryKey, autoIncrement: node.autoIncrement, unique: node.unique, notNull: node.notNull, unsigned: node.unsigned, - defaultTo: this.transformNode(node.defaultTo), - check: this.transformNode(node.check), - generated: this.transformNode(node.generated), - frontModifiers: this.transformNodeList(node.frontModifiers), - endModifiers: this.transformNodeList(node.endModifiers), + defaultTo: this.transformNode(node.defaultTo, queryId), + check: this.transformNode(node.check, queryId), + generated: this.transformNode(node.generated, queryId), + frontModifiers: this.transformNodeList(node.frontModifiers, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), nullsNotDistinct: node.nullsNotDistinct, identity: node.identity, ifNotExists: node.ifNotExists, }) } - protected transformAddColumn(node: AddColumnNode): AddColumnNode { + protected transformAddColumn( + node: AddColumnNode, + queryId?: QueryId, + ): AddColumnNode { return requireAllProps({ kind: 'AddColumnNode', - column: this.transformNode(node.column), + column: this.transformNode(node.column, queryId), }) } - protected transformDropTable(node: DropTableNode): DropTableNode { + protected transformDropTable( + node: DropTableNode, + queryId?: QueryId, + ): DropTableNode { return requireAllProps({ kind: 'DropTableNode', - table: this.transformNode(node.table), + table: this.transformNode(node.table, queryId), ifExists: node.ifExists, cascade: node.cascade, }) } - protected transformOrderBy(node: OrderByNode): OrderByNode { + protected transformOrderBy( + node: OrderByNode, + queryId?: QueryId, + ): OrderByNode { return requireAllProps({ kind: 'OrderByNode', - items: this.transformNodeList(node.items), + items: this.transformNodeList(node.items, queryId), }) } - protected transformOrderByItem(node: OrderByItemNode): OrderByItemNode { + protected transformOrderByItem( + node: OrderByItemNode, + queryId?: QueryId, + ): OrderByItemNode { return requireAllProps({ kind: 'OrderByItemNode', - orderBy: this.transformNode(node.orderBy), - direction: this.transformNode(node.direction), - collation: this.transformNode(node.collation), + orderBy: this.transformNode(node.orderBy, queryId), + direction: this.transformNode(node.direction, queryId), + collation: this.transformNode(node.collation, queryId), nulls: node.nulls, }) } - protected transformGroupBy(node: GroupByNode): GroupByNode { + protected transformGroupBy( + node: GroupByNode, + queryId?: QueryId, + ): GroupByNode { return requireAllProps({ kind: 'GroupByNode', - items: this.transformNodeList(node.items), + items: this.transformNodeList(node.items, queryId), }) } - protected transformGroupByItem(node: GroupByItemNode): GroupByItemNode { + protected transformGroupByItem( + node: GroupByItemNode, + queryId?: QueryId, + ): GroupByItemNode { return requireAllProps({ kind: 'GroupByItemNode', - groupBy: this.transformNode(node.groupBy), + groupBy: this.transformNode(node.groupBy, queryId), }) } - protected transformUpdateQuery(node: UpdateQueryNode): UpdateQueryNode { + protected transformUpdateQuery( + node: UpdateQueryNode, + queryId?: QueryId, + ): UpdateQueryNode { return requireAllProps({ kind: 'UpdateQueryNode', - table: this.transformNode(node.table), - from: this.transformNode(node.from), - joins: this.transformNodeList(node.joins), - where: this.transformNode(node.where), - updates: this.transformNodeList(node.updates), - returning: this.transformNode(node.returning), - endModifiers: this.transformNodeList(node.endModifiers), - with: this.transformNode(node.with), - explain: this.transformNode(node.explain), - limit: this.transformNode(node.limit), - top: this.transformNode(node.top), - output: this.transformNode(node.output), - orderBy: this.transformNode(node.orderBy), - }) - } - - protected transformColumnUpdate(node: ColumnUpdateNode): ColumnUpdateNode { + table: this.transformNode(node.table, queryId), + from: this.transformNode(node.from, queryId), + joins: this.transformNodeList(node.joins, queryId), + where: this.transformNode(node.where, queryId), + updates: this.transformNodeList(node.updates, queryId), + returning: this.transformNode(node.returning, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), + with: this.transformNode(node.with, queryId), + explain: this.transformNode(node.explain, queryId), + limit: this.transformNode(node.limit, queryId), + top: this.transformNode(node.top, queryId), + output: this.transformNode(node.output, queryId), + orderBy: this.transformNode(node.orderBy, queryId), + }) + } + + protected transformColumnUpdate( + node: ColumnUpdateNode, + queryId?: QueryId, + ): ColumnUpdateNode { return requireAllProps({ kind: 'ColumnUpdateNode', - column: this.transformNode(node.column), - value: this.transformNode(node.value), + column: this.transformNode(node.column, queryId), + value: this.transformNode(node.value, queryId), }) } - protected transformLimit(node: LimitNode): LimitNode { + protected transformLimit(node: LimitNode, queryId?: QueryId): LimitNode { return requireAllProps({ kind: 'LimitNode', - limit: this.transformNode(node.limit), + limit: this.transformNode(node.limit, queryId), }) } - protected transformOffset(node: OffsetNode): OffsetNode { + protected transformOffset(node: OffsetNode, queryId?: QueryId): OffsetNode { return requireAllProps({ kind: 'OffsetNode', - offset: this.transformNode(node.offset), + offset: this.transformNode(node.offset, queryId), }) } - protected transformOnConflict(node: OnConflictNode): OnConflictNode { + protected transformOnConflict( + node: OnConflictNode, + queryId?: QueryId, + ): OnConflictNode { return requireAllProps({ kind: 'OnConflictNode', - columns: this.transformNodeList(node.columns), - constraint: this.transformNode(node.constraint), - indexExpression: this.transformNode(node.indexExpression), - indexWhere: this.transformNode(node.indexWhere), - updates: this.transformNodeList(node.updates), - updateWhere: this.transformNode(node.updateWhere), + columns: this.transformNodeList(node.columns, queryId), + constraint: this.transformNode(node.constraint, queryId), + indexExpression: this.transformNode(node.indexExpression, queryId), + indexWhere: this.transformNode(node.indexWhere, queryId), + updates: this.transformNodeList(node.updates, queryId), + updateWhere: this.transformNode(node.updateWhere, queryId), doNothing: node.doNothing, }) } protected transformOnDuplicateKey( node: OnDuplicateKeyNode, + queryId?: QueryId, ): OnDuplicateKeyNode { return requireAllProps({ kind: 'OnDuplicateKeyNode', - updates: this.transformNodeList(node.updates), + updates: this.transformNodeList(node.updates, queryId), }) } - protected transformCreateIndex(node: CreateIndexNode): CreateIndexNode { + protected transformCreateIndex( + node: CreateIndexNode, + queryId?: QueryId, + ): CreateIndexNode { return requireAllProps({ kind: 'CreateIndexNode', - name: this.transformNode(node.name), - table: this.transformNode(node.table), - columns: this.transformNodeList(node.columns), + name: this.transformNode(node.name, queryId), + table: this.transformNode(node.table, queryId), + columns: this.transformNodeList(node.columns, queryId), unique: node.unique, - using: this.transformNode(node.using), + using: this.transformNode(node.using, queryId), ifNotExists: node.ifNotExists, - where: this.transformNode(node.where), + where: this.transformNode(node.where, queryId), nullsNotDistinct: node.nullsNotDistinct, }) } - protected transformList(node: ListNode): ListNode { + protected transformList(node: ListNode, queryId?: QueryId): ListNode { return requireAllProps({ kind: 'ListNode', - items: this.transformNodeList(node.items), + items: this.transformNodeList(node.items, queryId), }) } - protected transformDropIndex(node: DropIndexNode): DropIndexNode { + protected transformDropIndex( + node: DropIndexNode, + queryId?: QueryId, + ): DropIndexNode { return requireAllProps({ kind: 'DropIndexNode', - name: this.transformNode(node.name), - table: this.transformNode(node.table), + name: this.transformNode(node.name, queryId), + table: this.transformNode(node.table, queryId), ifExists: node.ifExists, cascade: node.cascade, }) @@ -619,52 +689,61 @@ export class OperationNodeTransformer { protected transformPrimaryKeyConstraint( node: PrimaryKeyConstraintNode, + queryId?: QueryId, ): PrimaryKeyConstraintNode { return requireAllProps({ kind: 'PrimaryKeyConstraintNode', - columns: this.transformNodeList(node.columns), - name: this.transformNode(node.name), + columns: this.transformNodeList(node.columns, queryId), + name: this.transformNode(node.name, queryId), }) } protected transformUniqueConstraint( node: UniqueConstraintNode, + queryId?: QueryId, ): UniqueConstraintNode { return requireAllProps({ kind: 'UniqueConstraintNode', - columns: this.transformNodeList(node.columns), - name: this.transformNode(node.name), + columns: this.transformNodeList(node.columns, queryId), + name: this.transformNode(node.name, queryId), nullsNotDistinct: node.nullsNotDistinct, }) } protected transformForeignKeyConstraint( node: ForeignKeyConstraintNode, + queryId?: QueryId, ): ForeignKeyConstraintNode { return requireAllProps({ kind: 'ForeignKeyConstraintNode', - columns: this.transformNodeList(node.columns), - references: this.transformNode(node.references), - name: this.transformNode(node.name), + columns: this.transformNodeList(node.columns, queryId), + references: this.transformNode(node.references, queryId), + name: this.transformNode(node.name, queryId), onDelete: node.onDelete, onUpdate: node.onUpdate, }) } - protected transformSetOperation(node: SetOperationNode): SetOperationNode { + protected transformSetOperation( + node: SetOperationNode, + queryId?: QueryId, + ): SetOperationNode { return requireAllProps({ kind: 'SetOperationNode', operator: node.operator, - expression: this.transformNode(node.expression), + expression: this.transformNode(node.expression, queryId), all: node.all, }) } - protected transformReferences(node: ReferencesNode): ReferencesNode { + protected transformReferences( + node: ReferencesNode, + queryId?: QueryId, + ): ReferencesNode { return requireAllProps({ kind: 'ReferencesNode', - table: this.transformNode(node.table), - columns: this.transformNodeList(node.columns), + table: this.transformNode(node.table, queryId), + columns: this.transformNodeList(node.columns, queryId), onDelete: node.onDelete, onUpdate: node.onUpdate, }) @@ -672,348 +751,422 @@ export class OperationNodeTransformer { protected transformCheckConstraint( node: CheckConstraintNode, + queryId?: QueryId, ): CheckConstraintNode { return requireAllProps({ kind: 'CheckConstraintNode', - expression: this.transformNode(node.expression), - name: this.transformNode(node.name), + expression: this.transformNode(node.expression, queryId), + name: this.transformNode(node.name, queryId), }) } - protected transformWith(node: WithNode): WithNode { + protected transformWith(node: WithNode, queryId?: QueryId): WithNode { return requireAllProps({ kind: 'WithNode', - expressions: this.transformNodeList(node.expressions), + expressions: this.transformNodeList(node.expressions, queryId), recursive: node.recursive, }) } protected transformCommonTableExpression( node: CommonTableExpressionNode, + queryId?: QueryId, ): CommonTableExpressionNode { return requireAllProps({ kind: 'CommonTableExpressionNode', - name: this.transformNode(node.name), + name: this.transformNode(node.name, queryId), materialized: node.materialized, - expression: this.transformNode(node.expression), + expression: this.transformNode(node.expression, queryId), }) } protected transformCommonTableExpressionName( node: CommonTableExpressionNameNode, + queryId?: QueryId, ): CommonTableExpressionNameNode { return requireAllProps({ kind: 'CommonTableExpressionNameNode', - table: this.transformNode(node.table), - columns: this.transformNodeList(node.columns), + table: this.transformNode(node.table, queryId), + columns: this.transformNodeList(node.columns, queryId), }) } - protected transformHaving(node: HavingNode): HavingNode { + protected transformHaving(node: HavingNode, queryId?: QueryId): HavingNode { return requireAllProps({ kind: 'HavingNode', - having: this.transformNode(node.having), + having: this.transformNode(node.having, queryId), }) } - protected transformCreateSchema(node: CreateSchemaNode): CreateSchemaNode { + protected transformCreateSchema( + node: CreateSchemaNode, + queryId?: QueryId, + ): CreateSchemaNode { return requireAllProps({ kind: 'CreateSchemaNode', - schema: this.transformNode(node.schema), + schema: this.transformNode(node.schema, queryId), ifNotExists: node.ifNotExists, }) } - protected transformDropSchema(node: DropSchemaNode): DropSchemaNode { + protected transformDropSchema( + node: DropSchemaNode, + queryId?: QueryId, + ): DropSchemaNode { return requireAllProps({ kind: 'DropSchemaNode', - schema: this.transformNode(node.schema), + schema: this.transformNode(node.schema, queryId), ifExists: node.ifExists, cascade: node.cascade, }) } - protected transformAlterTable(node: AlterTableNode): AlterTableNode { + protected transformAlterTable( + node: AlterTableNode, + queryId?: QueryId, + ): AlterTableNode { return requireAllProps({ kind: 'AlterTableNode', - table: this.transformNode(node.table), - renameTo: this.transformNode(node.renameTo), - setSchema: this.transformNode(node.setSchema), - columnAlterations: this.transformNodeList(node.columnAlterations), - addConstraint: this.transformNode(node.addConstraint), - dropConstraint: this.transformNode(node.dropConstraint), - addIndex: this.transformNode(node.addIndex), - dropIndex: this.transformNode(node.dropIndex), - }) - } - - protected transformDropColumn(node: DropColumnNode): DropColumnNode { + table: this.transformNode(node.table, queryId), + renameTo: this.transformNode(node.renameTo, queryId), + setSchema: this.transformNode(node.setSchema, queryId), + columnAlterations: this.transformNodeList( + node.columnAlterations, + queryId, + ), + addConstraint: this.transformNode(node.addConstraint, queryId), + dropConstraint: this.transformNode(node.dropConstraint, queryId), + addIndex: this.transformNode(node.addIndex, queryId), + dropIndex: this.transformNode(node.dropIndex, queryId), + }) + } + + protected transformDropColumn( + node: DropColumnNode, + queryId?: QueryId, + ): DropColumnNode { return requireAllProps({ kind: 'DropColumnNode', - column: this.transformNode(node.column), + column: this.transformNode(node.column, queryId), }) } - protected transformRenameColumn(node: RenameColumnNode): RenameColumnNode { + protected transformRenameColumn( + node: RenameColumnNode, + queryId?: QueryId, + ): RenameColumnNode { return requireAllProps({ kind: 'RenameColumnNode', - column: this.transformNode(node.column), - renameTo: this.transformNode(node.renameTo), + column: this.transformNode(node.column, queryId), + renameTo: this.transformNode(node.renameTo, queryId), }) } - protected transformAlterColumn(node: AlterColumnNode): AlterColumnNode { + protected transformAlterColumn( + node: AlterColumnNode, + queryId?: QueryId, + ): AlterColumnNode { return requireAllProps({ kind: 'AlterColumnNode', - column: this.transformNode(node.column), - dataType: this.transformNode(node.dataType), - dataTypeExpression: this.transformNode(node.dataTypeExpression), - setDefault: this.transformNode(node.setDefault), + column: this.transformNode(node.column, queryId), + dataType: this.transformNode(node.dataType, queryId), + dataTypeExpression: this.transformNode(node.dataTypeExpression, queryId), + setDefault: this.transformNode(node.setDefault, queryId), dropDefault: node.dropDefault, setNotNull: node.setNotNull, dropNotNull: node.dropNotNull, }) } - protected transformModifyColumn(node: ModifyColumnNode): ModifyColumnNode { + protected transformModifyColumn( + node: ModifyColumnNode, + queryId?: QueryId, + ): ModifyColumnNode { return requireAllProps({ kind: 'ModifyColumnNode', - column: this.transformNode(node.column), + column: this.transformNode(node.column, queryId), }) } - protected transformAddConstraint(node: AddConstraintNode): AddConstraintNode { + protected transformAddConstraint( + node: AddConstraintNode, + queryId?: QueryId, + ): AddConstraintNode { return requireAllProps({ kind: 'AddConstraintNode', - constraint: this.transformNode(node.constraint), + constraint: this.transformNode(node.constraint, queryId), }) } protected transformDropConstraint( node: DropConstraintNode, + queryId?: QueryId, ): DropConstraintNode { return requireAllProps({ kind: 'DropConstraintNode', - constraintName: this.transformNode(node.constraintName), + constraintName: this.transformNode(node.constraintName, queryId), ifExists: node.ifExists, modifier: node.modifier, }) } - protected transformCreateView(node: CreateViewNode): CreateViewNode { + protected transformCreateView( + node: CreateViewNode, + queryId?: QueryId, + ): CreateViewNode { return requireAllProps({ kind: 'CreateViewNode', - name: this.transformNode(node.name), + name: this.transformNode(node.name, queryId), temporary: node.temporary, orReplace: node.orReplace, ifNotExists: node.ifNotExists, materialized: node.materialized, - columns: this.transformNodeList(node.columns), - as: this.transformNode(node.as), + columns: this.transformNodeList(node.columns, queryId), + as: this.transformNode(node.as, queryId), }) } protected transformRefreshMaterializedView( node: RefreshMaterializedViewNode, + queryId?: QueryId, ): RefreshMaterializedViewNode { return requireAllProps({ kind: 'RefreshMaterializedViewNode', - name: this.transformNode(node.name), + name: this.transformNode(node.name, queryId), concurrently: node.concurrently, withNoData: node.withNoData, }) } - protected transformDropView(node: DropViewNode): DropViewNode { + protected transformDropView( + node: DropViewNode, + queryId?: QueryId, + ): DropViewNode { return requireAllProps({ kind: 'DropViewNode', - name: this.transformNode(node.name), + name: this.transformNode(node.name, queryId), ifExists: node.ifExists, materialized: node.materialized, cascade: node.cascade, }) } - protected transformGenerated(node: GeneratedNode): GeneratedNode { + protected transformGenerated( + node: GeneratedNode, + queryId?: QueryId, + ): GeneratedNode { return requireAllProps({ kind: 'GeneratedNode', byDefault: node.byDefault, always: node.always, identity: node.identity, stored: node.stored, - expression: this.transformNode(node.expression), + expression: this.transformNode(node.expression, queryId), }) } - protected transformDefaultValue(node: DefaultValueNode): DefaultValueNode { + protected transformDefaultValue( + node: DefaultValueNode, + queryId?: QueryId, + ): DefaultValueNode { return requireAllProps({ kind: 'DefaultValueNode', - defaultValue: this.transformNode(node.defaultValue), + defaultValue: this.transformNode(node.defaultValue, queryId), }) } - protected transformOn(node: OnNode): OnNode { + protected transformOn(node: OnNode, queryId?: QueryId): OnNode { return requireAllProps({ kind: 'OnNode', - on: this.transformNode(node.on), + on: this.transformNode(node.on, queryId), }) } protected transformSelectModifier( node: SelectModifierNode, + queryId?: QueryId, ): SelectModifierNode { return requireAllProps({ kind: 'SelectModifierNode', modifier: node.modifier, - rawModifier: this.transformNode(node.rawModifier), - of: this.transformNodeList(node.of), + rawModifier: this.transformNode(node.rawModifier, queryId), + of: this.transformNodeList(node.of, queryId), }) } - protected transformCreateType(node: CreateTypeNode): CreateTypeNode { + protected transformCreateType( + node: CreateTypeNode, + queryId?: QueryId, + ): CreateTypeNode { return requireAllProps({ kind: 'CreateTypeNode', - name: this.transformNode(node.name), - enum: this.transformNode(node.enum), + name: this.transformNode(node.name, queryId), + enum: this.transformNode(node.enum, queryId), }) } - protected transformDropType(node: DropTypeNode): DropTypeNode { + protected transformDropType( + node: DropTypeNode, + queryId?: QueryId, + ): DropTypeNode { return requireAllProps({ kind: 'DropTypeNode', - name: this.transformNode(node.name), + name: this.transformNode(node.name, queryId), ifExists: node.ifExists, }) } - protected transformExplain(node: ExplainNode): ExplainNode { + protected transformExplain( + node: ExplainNode, + queryId?: QueryId, + ): ExplainNode { return requireAllProps({ kind: 'ExplainNode', format: node.format, - options: this.transformNode(node.options), + options: this.transformNode(node.options, queryId), }) } protected transformSchemableIdentifier( node: SchemableIdentifierNode, + queryId?: QueryId, ): SchemableIdentifierNode { return requireAllProps({ kind: 'SchemableIdentifierNode', - schema: this.transformNode(node.schema), - identifier: this.transformNode(node.identifier), + schema: this.transformNode(node.schema, queryId), + identifier: this.transformNode(node.identifier, queryId), }) } protected transformAggregateFunction( node: AggregateFunctionNode, + queryId?: QueryId, ): AggregateFunctionNode { return requireAllProps({ kind: 'AggregateFunctionNode', func: node.func, - aggregated: this.transformNodeList(node.aggregated), + aggregated: this.transformNodeList(node.aggregated, queryId), distinct: node.distinct, - orderBy: this.transformNode(node.orderBy), - withinGroup: this.transformNode(node.withinGroup), - filter: this.transformNode(node.filter), - over: this.transformNode(node.over), + orderBy: this.transformNode(node.orderBy, queryId), + withinGroup: this.transformNode(node.withinGroup, queryId), + filter: this.transformNode(node.filter, queryId), + over: this.transformNode(node.over, queryId), }) } - protected transformOver(node: OverNode): OverNode { + protected transformOver(node: OverNode, queryId?: QueryId): OverNode { return requireAllProps({ kind: 'OverNode', - orderBy: this.transformNode(node.orderBy), - partitionBy: this.transformNode(node.partitionBy), + orderBy: this.transformNode(node.orderBy, queryId), + partitionBy: this.transformNode(node.partitionBy, queryId), }) } - protected transformPartitionBy(node: PartitionByNode): PartitionByNode { + protected transformPartitionBy( + node: PartitionByNode, + queryId?: QueryId, + ): PartitionByNode { return requireAllProps({ kind: 'PartitionByNode', - items: this.transformNodeList(node.items), + items: this.transformNodeList(node.items, queryId), }) } protected transformPartitionByItem( node: PartitionByItemNode, + queryId?: QueryId, ): PartitionByItemNode { return requireAllProps({ kind: 'PartitionByItemNode', - partitionBy: this.transformNode(node.partitionBy), + partitionBy: this.transformNode(node.partitionBy, queryId), }) } protected transformBinaryOperation( node: BinaryOperationNode, + queryId?: QueryId, ): BinaryOperationNode { return requireAllProps({ kind: 'BinaryOperationNode', - leftOperand: this.transformNode(node.leftOperand), - operator: this.transformNode(node.operator), - rightOperand: this.transformNode(node.rightOperand), + leftOperand: this.transformNode(node.leftOperand, queryId), + operator: this.transformNode(node.operator, queryId), + rightOperand: this.transformNode(node.rightOperand, queryId), }) } protected transformUnaryOperation( node: UnaryOperationNode, + queryId?: QueryId, ): UnaryOperationNode { return requireAllProps({ kind: 'UnaryOperationNode', - operator: this.transformNode(node.operator), - operand: this.transformNode(node.operand), + operator: this.transformNode(node.operator, queryId), + operand: this.transformNode(node.operand, queryId), }) } - protected transformUsing(node: UsingNode): UsingNode { + protected transformUsing(node: UsingNode, queryId?: QueryId): UsingNode { return requireAllProps({ kind: 'UsingNode', - tables: this.transformNodeList(node.tables), + tables: this.transformNodeList(node.tables, queryId), }) } - protected transformFunction(node: FunctionNode): FunctionNode { + protected transformFunction( + node: FunctionNode, + queryId?: QueryId, + ): FunctionNode { return requireAllProps({ kind: 'FunctionNode', func: node.func, - arguments: this.transformNodeList(node.arguments), + arguments: this.transformNodeList(node.arguments, queryId), }) } - protected transformCase(node: CaseNode): CaseNode { + protected transformCase(node: CaseNode, queryId?: QueryId): CaseNode { return requireAllProps({ kind: 'CaseNode', - value: this.transformNode(node.value), - when: this.transformNodeList(node.when), - else: this.transformNode(node.else), + value: this.transformNode(node.value, queryId), + when: this.transformNodeList(node.when, queryId), + else: this.transformNode(node.else, queryId), isStatement: node.isStatement, }) } - protected transformWhen(node: WhenNode): WhenNode { + protected transformWhen(node: WhenNode, queryId?: QueryId): WhenNode { return requireAllProps({ kind: 'WhenNode', - condition: this.transformNode(node.condition), - result: this.transformNode(node.result), + condition: this.transformNode(node.condition, queryId), + result: this.transformNode(node.result, queryId), }) } - protected transformJSONReference(node: JSONReferenceNode): JSONReferenceNode { + protected transformJSONReference( + node: JSONReferenceNode, + queryId?: QueryId, + ): JSONReferenceNode { return requireAllProps({ kind: 'JSONReferenceNode', - reference: this.transformNode(node.reference), - traversal: this.transformNode(node.traversal), + reference: this.transformNode(node.reference, queryId), + traversal: this.transformNode(node.traversal, queryId), }) } - protected transformJSONPath(node: JSONPathNode): JSONPathNode { + protected transformJSONPath( + node: JSONPathNode, + queryId?: QueryId, + ): JSONPathNode { return requireAllProps({ kind: 'JSONPathNode', - inOperator: this.transformNode(node.inOperator), - pathLegs: this.transformNodeList(node.pathLegs), + inOperator: this.transformNode(node.inOperator, queryId), + pathLegs: this.transformNodeList(node.pathLegs, queryId), }) } - protected transformJSONPathLeg(node: JSONPathLegNode): JSONPathLegNode { + protected transformJSONPathLeg( + node: JSONPathLegNode, + _queryId?: QueryId, + ): JSONPathLegNode { return requireAllProps({ kind: 'JSONPathLegNode', type: node.type, @@ -1023,36 +1176,43 @@ export class OperationNodeTransformer { protected transformJSONOperatorChain( node: JSONOperatorChainNode, + queryId?: QueryId, ): JSONOperatorChainNode { return requireAllProps({ kind: 'JSONOperatorChainNode', - operator: this.transformNode(node.operator), - values: this.transformNodeList(node.values), + operator: this.transformNode(node.operator, queryId), + values: this.transformNodeList(node.values, queryId), }) } - protected transformTuple(node: TupleNode): TupleNode { + protected transformTuple(node: TupleNode, queryId?: QueryId): TupleNode { return requireAllProps({ kind: 'TupleNode', - values: this.transformNodeList(node.values), + values: this.transformNodeList(node.values, queryId), }) } - protected transformMergeQuery(node: MergeQueryNode): MergeQueryNode { + protected transformMergeQuery( + node: MergeQueryNode, + queryId?: QueryId, + ): MergeQueryNode { return requireAllProps({ kind: 'MergeQueryNode', - into: this.transformNode(node.into), - using: this.transformNode(node.using), - whens: this.transformNodeList(node.whens), - with: this.transformNode(node.with), - top: this.transformNode(node.top), - endModifiers: this.transformNodeList(node.endModifiers), - output: this.transformNode(node.output), - returning: this.transformNode(node.returning), + into: this.transformNode(node.into, queryId), + using: this.transformNode(node.using, queryId), + whens: this.transformNodeList(node.whens, queryId), + with: this.transformNode(node.with, queryId), + top: this.transformNode(node.top, queryId), + endModifiers: this.transformNodeList(node.endModifiers, queryId), + output: this.transformNode(node.output, queryId), + returning: this.transformNode(node.returning, queryId), }) } - protected transformMatched(node: MatchedNode): MatchedNode { + protected transformMatched( + node: MatchedNode, + _queryId?: QueryId, + ): MatchedNode { return requireAllProps({ kind: 'MatchedNode', not: node.not, @@ -1060,34 +1220,37 @@ export class OperationNodeTransformer { }) } - protected transformAddIndex(node: AddIndexNode): AddIndexNode { + protected transformAddIndex( + node: AddIndexNode, + queryId?: QueryId, + ): AddIndexNode { return requireAllProps({ kind: 'AddIndexNode', - name: this.transformNode(node.name), - columns: this.transformNodeList(node.columns), + name: this.transformNode(node.name, queryId), + columns: this.transformNodeList(node.columns, queryId), unique: node.unique, - using: this.transformNode(node.using), + using: this.transformNode(node.using, queryId), ifNotExists: node.ifNotExists, }) } - protected transformCast(node: CastNode): CastNode { + protected transformCast(node: CastNode, queryId?: QueryId): CastNode { return requireAllProps({ kind: 'CastNode', - expression: this.transformNode(node.expression), - dataType: this.transformNode(node.dataType), + expression: this.transformNode(node.expression, queryId), + dataType: this.transformNode(node.dataType, queryId), }) } - protected transformFetch(node: FetchNode): FetchNode { + protected transformFetch(node: FetchNode, queryId?: QueryId): FetchNode { return requireAllProps({ kind: 'FetchNode', - rowCount: this.transformNode(node.rowCount), + rowCount: this.transformNode(node.rowCount, queryId), modifier: node.modifier, }) } - protected transformTop(node: TopNode): TopNode { + protected transformTop(node: TopNode, _queryId?: QueryId): TopNode { return requireAllProps({ kind: 'TopNode', expression: node.expression, @@ -1095,58 +1258,78 @@ export class OperationNodeTransformer { }) } - protected transformOutput(node: OutputNode): OutputNode { + protected transformOutput(node: OutputNode, queryId?: QueryId): OutputNode { return requireAllProps({ kind: 'OutputNode', - selections: this.transformNodeList(node.selections), + selections: this.transformNodeList(node.selections, queryId), }) } - protected transformDataType(node: DataTypeNode): DataTypeNode { + protected transformDataType( + node: DataTypeNode, + _queryId?: QueryId, + ): DataTypeNode { // An Object.freezed leaf node. No need to clone. return node } - protected transformSelectAll(node: SelectAllNode): SelectAllNode { + protected transformSelectAll( + node: SelectAllNode, + _queryId?: QueryId, + ): SelectAllNode { // An Object.freezed leaf node. No need to clone. return node } - protected transformIdentifier(node: IdentifierNode): IdentifierNode { + protected transformIdentifier( + node: IdentifierNode, + _queryId?: QueryId, + ): IdentifierNode { // An Object.freezed leaf node. No need to clone. return node } - protected transformValue(node: ValueNode): ValueNode { + protected transformValue(node: ValueNode, _queryId?: QueryId): ValueNode { // An Object.freezed leaf node. No need to clone. return node } protected transformPrimitiveValueList( node: PrimitiveValueListNode, + _queryId?: QueryId, ): PrimitiveValueListNode { // An Object.freezed leaf node. No need to clone. return node } - protected transformOperator(node: OperatorNode): OperatorNode { + protected transformOperator( + node: OperatorNode, + _queryId?: QueryId, + ): OperatorNode { // An Object.freezed leaf node. No need to clone. return node } protected transformDefaultInsertValue( node: DefaultInsertValueNode, + _queryId?: QueryId, ): DefaultInsertValueNode { // An Object.freezed leaf node. No need to clone. return node } - protected transformOrAction(node: OrActionNode): OrActionNode { + protected transformOrAction( + node: OrActionNode, + _queryId?: QueryId, + ): OrActionNode { // An Object.freezed leaf node. No need to clone. return node } - protected transformCollate(node: CollateNode): CollateNode { + protected transformCollate( + node: CollateNode, + _queryId?: QueryId, + ): CollateNode { // An Object.freezed leaf node. No need to clone. return node } diff --git a/src/plugin/camel-case/camel-case-plugin.ts b/src/plugin/camel-case/camel-case-plugin.ts index eeadd59d1..0b53b1c0f 100644 --- a/src/plugin/camel-case/camel-case-plugin.ts +++ b/src/plugin/camel-case/camel-case-plugin.ts @@ -133,7 +133,7 @@ export class CamelCasePlugin implements KyselyPlugin { } transformQuery(args: PluginTransformQueryArgs): RootOperationNode { - return this.#snakeCaseTransformer.transformNode(args.node) + return this.#snakeCaseTransformer.transformNode(args.node, args.queryId) } async transformResult( diff --git a/src/plugin/camel-case/camel-case-transformer.ts b/src/plugin/camel-case/camel-case-transformer.ts index 4a7a6aa0a..7d44de544 100644 --- a/src/plugin/camel-case/camel-case-transformer.ts +++ b/src/plugin/camel-case/camel-case-transformer.ts @@ -1,5 +1,6 @@ import { IdentifierNode } from '../../operation-node/identifier-node.js' import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js' +import { QueryId } from '../../util/query-id.js' import { StringMapper } from './camel-case.js' export class SnakeCaseTransformer extends OperationNodeTransformer { @@ -10,8 +11,11 @@ export class SnakeCaseTransformer extends OperationNodeTransformer { this.#snakeCase = snakeCase } - protected override transformIdentifier(node: IdentifierNode): IdentifierNode { - node = super.transformIdentifier(node) + protected override transformIdentifier( + node: IdentifierNode, + queryId: QueryId, + ): IdentifierNode { + node = super.transformIdentifier(node, queryId) return { ...node, diff --git a/src/plugin/deduplicate-joins/deduplicate-joins-plugin.ts b/src/plugin/deduplicate-joins/deduplicate-joins-plugin.ts index 456efdd5e..560a590e6 100644 --- a/src/plugin/deduplicate-joins/deduplicate-joins-plugin.ts +++ b/src/plugin/deduplicate-joins/deduplicate-joins-plugin.ts @@ -17,7 +17,7 @@ export class DeduplicateJoinsPlugin implements KyselyPlugin { readonly #transformer = new DeduplicateJoinsTransformer() transformQuery(args: PluginTransformQueryArgs): RootOperationNode { - return this.#transformer.transformNode(args.node) + return this.#transformer.transformNode(args.node, args.queryId) } transformResult( diff --git a/src/plugin/deduplicate-joins/deduplicate-joins-transformer.ts b/src/plugin/deduplicate-joins/deduplicate-joins-transformer.ts index daba46c6f..91fecd623 100644 --- a/src/plugin/deduplicate-joins/deduplicate-joins-transformer.ts +++ b/src/plugin/deduplicate-joins/deduplicate-joins-transformer.ts @@ -4,18 +4,28 @@ import { OperationNodeTransformer } from '../../operation-node/operation-node-tr import { SelectQueryNode } from '../../operation-node/select-query-node.js' import { UpdateQueryNode } from '../../operation-node/update-query-node.js' import { compare, freeze } from '../../util/object-utils.js' +import { QueryId } from '../../util/query-id.js' export class DeduplicateJoinsTransformer extends OperationNodeTransformer { - protected transformSelectQuery(node: SelectQueryNode): SelectQueryNode { - return this.#transformQuery(super.transformSelectQuery(node)) + protected transformSelectQuery( + node: SelectQueryNode, + queryId: QueryId, + ): SelectQueryNode { + return this.#transformQuery(super.transformSelectQuery(node, queryId)) } - protected transformUpdateQuery(node: UpdateQueryNode): UpdateQueryNode { - return this.#transformQuery(super.transformUpdateQuery(node)) + protected transformUpdateQuery( + node: UpdateQueryNode, + queryId: QueryId, + ): UpdateQueryNode { + return this.#transformQuery(super.transformUpdateQuery(node, queryId)) } - protected transformDeleteQuery(node: DeleteQueryNode): DeleteQueryNode { - return this.#transformQuery(super.transformDeleteQuery(node)) + protected transformDeleteQuery( + node: DeleteQueryNode, + queryId: QueryId, + ): DeleteQueryNode { + return this.#transformQuery(super.transformDeleteQuery(node, queryId)) } #transformQuery< diff --git a/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts b/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts index 615b087e7..f089dcb16 100644 --- a/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts +++ b/src/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.ts @@ -160,7 +160,7 @@ export class HandleEmptyInListsPlugin implements KyselyPlugin { } transformQuery(args: PluginTransformQueryArgs): RootOperationNode { - return this.#transformer.transformNode(args.node) + return this.#transformer.transformNode(args.node, args.queryId) } async transformResult( diff --git a/src/plugin/immediate-value/immediate-value-plugin.ts b/src/plugin/immediate-value/immediate-value-plugin.ts index ee6c1b13b..dccabb693 100644 --- a/src/plugin/immediate-value/immediate-value-plugin.ts +++ b/src/plugin/immediate-value/immediate-value-plugin.ts @@ -20,7 +20,7 @@ export class ImmediateValuePlugin implements KyselyPlugin { readonly #transformer = new ImmediateValueTransformer() transformQuery(args: PluginTransformQueryArgs): RootOperationNode { - return this.#transformer.transformNode(args.node) + return this.#transformer.transformNode(args.node, args.queryId) } transformResult( diff --git a/src/plugin/immediate-value/immediate-value-transformer.ts b/src/plugin/immediate-value/immediate-value-transformer.ts index 8b8fba9e1..d055b6b52 100644 --- a/src/plugin/immediate-value/immediate-value-transformer.ts +++ b/src/plugin/immediate-value/immediate-value-transformer.ts @@ -1,5 +1,6 @@ import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js' import { ValueNode } from '../../operation-node/value-node.js' +import { QueryId } from '../../util/query-id.js' /** * Transforms all ValueNodes to immediate. @@ -10,9 +11,9 @@ import { ValueNode } from '../../operation-node/value-node.js' * @internal */ export class ImmediateValueTransformer extends OperationNodeTransformer { - override transformValue(node: ValueNode): ValueNode { + override transformValue(node: ValueNode, queryId: QueryId): ValueNode { return { - ...super.transformValue(node), + ...super.transformValue(node, queryId), immediate: true, } } diff --git a/src/plugin/with-schema/with-schema-plugin.ts b/src/plugin/with-schema/with-schema-plugin.ts index 44ec832de..8aa45845d 100644 --- a/src/plugin/with-schema/with-schema-plugin.ts +++ b/src/plugin/with-schema/with-schema-plugin.ts @@ -16,7 +16,7 @@ export class WithSchemaPlugin implements KyselyPlugin { } transformQuery(args: PluginTransformQueryArgs): RootOperationNode { - return this.#transformer.transformNode(args.node) + return this.#transformer.transformNode(args.node, args.queryId) } async transformResult( diff --git a/src/plugin/with-schema/with-schema-transformer.ts b/src/plugin/with-schema/with-schema-transformer.ts index a6c4046ee..5c42a7d82 100644 --- a/src/plugin/with-schema/with-schema-transformer.ts +++ b/src/plugin/with-schema/with-schema-transformer.ts @@ -11,6 +11,7 @@ import { TableNode } from '../../operation-node/table-node.js' import { WithNode } from '../../operation-node/with-node.js' import { RootOperationNode } from '../../query-compiler/query-compiler.js' import { freeze } from '../../util/object-utils.js' +import { QueryId } from '../../util/query-id.js' // This object exist only so that we get a type error when a new RootOperationNode // is added. If you get a type error here, make sure to add the new root node and @@ -53,9 +54,12 @@ export class WithSchemaTransformer extends OperationNodeTransformer { this.#schema = schema } - protected override transformNodeImpl(node: T): T { + protected override transformNodeImpl( + node: T, + queryId: QueryId, + ): T { if (!this.#isRootOperationNode(node)) { - return super.transformNodeImpl(node) + return super.transformNodeImpl(node, queryId) } const ctes = this.#collectCTEs(node) @@ -70,7 +74,7 @@ export class WithSchemaTransformer extends OperationNodeTransformer { this.#schemableIds.add(table) } - const transformed = super.transformNodeImpl(node) + const transformed = super.transformNodeImpl(node, queryId) for (const table of tables) { this.#schemableIds.delete(table) @@ -85,8 +89,9 @@ export class WithSchemaTransformer extends OperationNodeTransformer { protected override transformSchemableIdentifier( node: SchemableIdentifierNode, + queryId: QueryId, ): SchemableIdentifierNode { - const transformed = super.transformSchemableIdentifier(node) + const transformed = super.transformSchemableIdentifier(node, queryId) if (transformed.schema || !this.#schemableIds.has(node.identifier.name)) { return transformed @@ -98,8 +103,11 @@ export class WithSchemaTransformer extends OperationNodeTransformer { } } - protected override transformReferences(node: ReferencesNode): ReferencesNode { - const transformed = super.transformReferences(node) + protected override transformReferences( + node: ReferencesNode, + queryId: QueryId, + ): ReferencesNode { + const transformed = super.transformReferences(node, queryId) if (transformed.table.table.schema) { return transformed @@ -116,17 +124,29 @@ export class WithSchemaTransformer extends OperationNodeTransformer { protected override transformAggregateFunction( node: AggregateFunctionNode, + queryId: QueryId, ): AggregateFunctionNode { return { - ...super.transformAggregateFunction({ ...node, aggregated: [] }), - aggregated: this.#transformTableArgsWithoutSchemas(node, 'aggregated'), + ...super.transformAggregateFunction({ ...node, aggregated: [] }, queryId), + aggregated: this.#transformTableArgsWithoutSchemas( + node, + queryId, + 'aggregated', + ), } } - protected override transformFunction(node: FunctionNode): FunctionNode { + protected override transformFunction( + node: FunctionNode, + queryId: QueryId, + ): FunctionNode { return { - ...super.transformFunction({ ...node, arguments: [] }), - arguments: this.#transformTableArgsWithoutSchemas(node, 'arguments'), + ...super.transformFunction({ ...node, arguments: [] }, queryId), + arguments: this.#transformTableArgsWithoutSchemas( + node, + queryId, + 'arguments', + ), } } @@ -135,17 +155,17 @@ export class WithSchemaTransformer extends OperationNodeTransformer { N extends { func: string } & { [K in A]: readonly OperationNode[] }, - >(node: N, argsKey: A): readonly OperationNode[] { + >(node: N, queryId: QueryId, argsKey: A): readonly OperationNode[] { return SCHEMALESS_FUNCTIONS[node.func] ? node[argsKey].map((arg) => !TableNode.is(arg) || arg.table.schema - ? this.transformNode(arg) + ? this.transformNode(arg, queryId) : { ...arg, - table: this.transformIdentifier(arg.table.identifier), + table: this.transformIdentifier(arg.table.identifier, queryId), }, ) - : this.transformNodeList(node[argsKey]) + : this.transformNodeList(node[argsKey], queryId) } #isRootOperationNode(node: OperationNode): node is RootOperationNode { diff --git a/src/query-builder/delete-query-builder.ts b/src/query-builder/delete-query-builder.ts index 22e0ca23a..bc97cb2c1 100644 --- a/src/query-builder/delete-query-builder.ts +++ b/src/query-builder/delete-query-builder.ts @@ -1074,12 +1074,7 @@ export class DeleteQueryBuilder return result.rows as any } - return [ - new DeleteResult( - // TODO: remove numUpdatedOrDeletedRows. - result.numAffectedRows ?? result.numUpdatedOrDeletedRows ?? BigInt(0), - ) as any, - ] + return [new DeleteResult(result.numAffectedRows ?? BigInt(0)) as any] } /** diff --git a/src/query-builder/insert-query-builder.ts b/src/query-builder/insert-query-builder.ts index c8ebfbc66..97e10dee8 100644 --- a/src/query-builder/insert-query-builder.ts +++ b/src/query-builder/insert-query-builder.ts @@ -1310,8 +1310,7 @@ export class InsertQueryBuilder return [ new InsertResult( result.insertId, - // TODO: remove numUpdatedOrDeletedRows. - result.numAffectedRows ?? result.numUpdatedOrDeletedRows, + result.numAffectedRows ?? BigInt(0), ) as any, ] } diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index 5c95b9396..4ab2515d2 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -1165,9 +1165,7 @@ export class UpdateQueryBuilder return [ new UpdateResult( - // TODO: remove numUpdatedOrDeletedRows. - // TODO: https://github.com/kysely-org/kysely/pull/431#discussion_r1172330899 - result.numAffectedRows ?? result.numUpdatedOrDeletedRows ?? BigInt(0), + result.numAffectedRows ?? BigInt(0), result.numChangedRows, ) as any, ] diff --git a/src/query-compiler/compiled-query.ts b/src/query-compiler/compiled-query.ts index b7a7dfc5d..f649a83e4 100644 --- a/src/query-compiler/compiled-query.ts +++ b/src/query-compiler/compiled-query.ts @@ -1,9 +1,11 @@ import { RawNode } from '../operation-node/raw-node.js' import { freeze } from '../util/object-utils.js' +import { createQueryId, QueryId } from '../util/query-id.js' import { RootOperationNode } from './query-compiler.js' export interface CompiledQuery { readonly query: RootOperationNode + readonly queryId: QueryId readonly sql: string readonly parameters: ReadonlyArray } @@ -14,6 +16,7 @@ export const CompiledQuery = freeze({ sql, query: RawNode.createWithSql(sql), parameters: freeze(parameters), + queryId: createQueryId(), }) }, }) diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 67207a4fe..31e31fcda 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -115,6 +115,7 @@ import { RefreshMaterializedViewNode } from '../operation-node/refresh-materiali import { OrActionNode } from '../operation-node/or-action-node.js' import { logOnce } from '../util/log-once.js' import { CollateNode } from '../operation-node/collate-node.js' +import { QueryId } from '../util/query-id.js' export class DefaultQueryCompiler extends OperationNodeVisitor @@ -127,7 +128,7 @@ export class DefaultQueryCompiler return this.#parameters.length } - compileQuery(node: RootOperationNode): CompiledQuery { + compileQuery(node: RootOperationNode, queryId: QueryId): CompiledQuery { this.#sql = '' this.#parameters = [] this.nodeStack.splice(0, this.nodeStack.length) @@ -136,6 +137,7 @@ export class DefaultQueryCompiler return freeze({ query: node, + queryId, sql: this.getSql(), parameters: [...this.#parameters], }) diff --git a/src/query-compiler/query-compiler.ts b/src/query-compiler/query-compiler.ts index 8a14b7a81..bd9d99a7f 100644 --- a/src/query-compiler/query-compiler.ts +++ b/src/query-compiler/query-compiler.ts @@ -13,6 +13,7 @@ import { MergeQueryNode } from '../operation-node/merge-query-node.js' import { QueryNode } from '../operation-node/query-node.js' import { RawNode } from '../operation-node/raw-node.js' import { RefreshMaterializedViewNode } from '../operation-node/refresh-materialized-view-node.js' +import { QueryId } from '../util/query-id.js' import { CompiledQuery } from './compiled-query.js' export type RootOperationNode = @@ -36,5 +37,5 @@ export type RootOperationNode = * a `QueryCompiler` compiles a query expressed as a tree of `OperationNodes` into SQL. */ export interface QueryCompiler { - compileQuery(node: RootOperationNode): CompiledQuery + compileQuery(node: RootOperationNode, queryId: QueryId): CompiledQuery } diff --git a/src/query-executor/default-query-executor.ts b/src/query-executor/default-query-executor.ts index 65b65c5e3..9f106d8dc 100644 --- a/src/query-executor/default-query-executor.ts +++ b/src/query-executor/default-query-executor.ts @@ -8,6 +8,7 @@ import { import { KyselyPlugin } from '../plugin/kysely-plugin.js' import { QueryExecutorBase } from './query-executor-base.js' import { DialectAdapter } from '../dialect/dialect-adapter.js' +import { QueryId } from '../util/query-id.js' export class DefaultQueryExecutor extends QueryExecutorBase { #compiler: QueryCompiler @@ -31,8 +32,8 @@ export class DefaultQueryExecutor extends QueryExecutorBase { return this.#adapter } - compileQuery(node: RootOperationNode): CompiledQuery { - return this.#compiler.compileQuery(node) + compileQuery(node: RootOperationNode, queryId: QueryId): CompiledQuery { + return this.#compiler.compileQuery(node, queryId) } provideConnection( diff --git a/src/query-executor/query-executor-base.ts b/src/query-executor/query-executor-base.ts index 5aec6a5f4..ab038cc20 100644 --- a/src/query-executor/query-executor-base.ts +++ b/src/query-executor/query-executor-base.ts @@ -10,9 +10,8 @@ import { freeze } from '../util/object-utils.js' import { QueryId } from '../util/query-id.js' import { DialectAdapter } from '../dialect/dialect-adapter.js' import { QueryExecutor } from './query-executor.js' -import { Deferred } from '../util/deferred.js' -import { logOnce } from '../util/log-once.js' import { provideControlledConnection } from '../util/provide-controlled-connection.js' +import { logOnce } from '../util/log-once.js' const NO_PLUGINS: ReadonlyArray = freeze([]) @@ -67,12 +66,14 @@ export abstract class QueryExecutorBase implements QueryExecutor { ): Promise> { return await this.provideConnection(async (connection) => { const result = await connection.executeQuery(compiledQuery) - const transformedResult = await this.#transformResult(result, queryId) - // TODO: remove. - warnOfOutdatedDriverOrPlugins(result, transformedResult) + if ('numUpdatedOrDeletedRows' in result) { + logOnce( + 'kysely:warning: outdated driver/plugin detected! `QueryResult.numUpdatedOrDeletedRows` has been replaced with `QueryResult.numAffectedRows`.', + ) + } - return transformedResult as any + return await this.#transformResult(result, queryId) }) } @@ -115,24 +116,3 @@ export abstract class QueryExecutorBase implements QueryExecutor { return result } } - -// TODO: remove. -function warnOfOutdatedDriverOrPlugins( - result: QueryResult, - transformedResult: QueryResult, -): void { - const { numAffectedRows } = result - - if ( - (numAffectedRows === undefined && - result.numUpdatedOrDeletedRows === undefined) || - (numAffectedRows !== undefined && - transformedResult.numAffectedRows !== undefined) - ) { - return - } - - logOnce( - 'kysely:warning: outdated driver/plugin detected! QueryResult.numUpdatedOrDeletedRows is deprecated and will be removed in a future release.', - ) -} diff --git a/src/schema/create-index-builder.ts b/src/schema/create-index-builder.ts index 28b215c6f..5432c7f51 100644 --- a/src/schema/create-index-builder.ts +++ b/src/schema/create-index-builder.ts @@ -270,7 +270,10 @@ export class CreateIndexBuilder ...this.#props, node: QueryNode.cloneWithWhere( this.#props.node, - transformer.transformNode(parseValueBinaryOperationOrExpression(args)), + transformer.transformNode( + parseValueBinaryOperationOrExpression(args), + this.#props.queryId, + ), ), }) } diff --git a/test/node/src/query-id.test.ts b/test/node/src/query-id.test.ts new file mode 100644 index 000000000..e70b9be45 --- /dev/null +++ b/test/node/src/query-id.test.ts @@ -0,0 +1,75 @@ +import { expect } from 'chai' +import { + DatabaseConnection, + DummyDriver, + Kysely, + PostgresAdapter, + PostgresIntrospector, + PostgresQueryCompiler, + RootOperationNode, + QueryId, +} from '../../..' +import { Database } from './test-setup' + +describe('queryId', () => { + const visits = new Map() + + const db = new Kysely({ + dialect: { + createAdapter: () => new PostgresAdapter(), + createDriver: () => + new (class extends DummyDriver { + async acquireConnection(): Promise { + // @ts-ignore + return { + executeQuery: async ({ queryId }) => { + checkIn(queryId, 'connection.executeQuery') + + return { + rows: [], + } + }, + } + } + })(), + createIntrospector: (db) => new PostgresIntrospector(db), + createQueryCompiler: () => + new (class SomeCompiler extends PostgresQueryCompiler { + compileQuery(node: RootOperationNode, queryId: QueryId) { + checkIn(queryId, 'compiler.compileQuery') + + return super.compileQuery(node, queryId) + } + })(), + }, + plugins: [ + { + transformQuery: (args) => { + checkIn(args.queryId, 'plugin.transformQuery') + + return args.node + }, + transformResult: async (args) => { + checkIn(args.queryId, 'plugin.transformResult') + + return args.result + }, + }, + ], + }) + + it('should pass query id around, allowing async communication between compilers, plugins and connections', async () => { + await db.selectFrom('person').where('id', '=', 1).execute() + + expect(Array.from(visits.values())[0]).to.deep.equal([ + 'plugin.transformQuery', + 'compiler.compileQuery', + 'connection.executeQuery', + 'plugin.transformResult', + ]) + }) + + function checkIn(queryId: QueryId, place: string): void { + visits.set(queryId, [...(visits.get(queryId) || []), place]) + } +}) diff --git a/test/node/src/raw-sql.test.ts b/test/node/src/raw-sql.test.ts index 2d1af2dca..48614a7be 100644 --- a/test/node/src/raw-sql.test.ts +++ b/test/node/src/raw-sql.test.ts @@ -1,4 +1,9 @@ -import { sql, CompiledQuery, DefaultQueryCompiler } from '../../../' +import { + sql, + CompiledQuery, + DefaultQueryCompiler, + createQueryId, +} from '../../../' import { clearDatabase, @@ -66,7 +71,7 @@ for (const dialect of DIALECTS) { let node = sql`before ${ctx.db .selectFrom('person') .selectAll()} after`.toOperationNode() - expect(compiler.compileQuery(node).sql).to.equal( + expect(compiler.compileQuery(node, createQueryId()).sql).to.equal( `before (select * from "person") after`, ) @@ -75,21 +80,21 @@ for (const dialect of DIALECTS) { last_name: 'Aniston', gender: 'female', })} after`.toOperationNode() - expect(compiler.compileQuery(node).sql).to.equal( + expect(compiler.compileQuery(node, createQueryId()).sql).to.equal( `before insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3) after`, ) node = sql`before ${ctx.db .deleteFrom('person') .where('id', '=', 1)} after`.toOperationNode() - expect(compiler.compileQuery(node).sql).to.equal( + expect(compiler.compileQuery(node, createQueryId()).sql).to.equal( `before delete from "person" where "id" = $1 after`, ) node = sql`before ${ctx.db .updateTable('person') .set('first_name', 'Jennifer')} after`.toOperationNode() - expect(compiler.compileQuery(node).sql).to.equal( + expect(compiler.compileQuery(node, createQueryId()).sql).to.equal( `before update "person" set "first_name" = $1 after`, ) }) diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 1b75acb62..a19fb2757 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -35,6 +35,7 @@ import { InsertObject, MssqlDialect, SelectQueryBuilder, + QueryId, } from '../../../' import { OrderByDirection, @@ -479,7 +480,7 @@ function createNoopTransformerPlugin(): KyselyPlugin { return { transformQuery(args: PluginTransformQueryArgs): RootOperationNode { - return transformer.transformNode(args.node) + return transformer.transformNode(args.node, args.queryId) }, async transformResult( From 74aed5d1197485705b10dce777248b2c9032a234 Mon Sep 17 00:00:00 2001 From: Reid Swan Date: Sat, 1 Feb 2025 16:02:34 +0200 Subject: [PATCH 28/28] feat: Add disableTransactions option to Migrator (#1335) Co-authored-by: Igal Klebanov --- src/migration/migrator.ts | 11 ++++++- test/node/src/migration.test.ts | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/migration/migrator.ts b/src/migration/migrator.ts index c8719713b..5ac9e9e4b 100644 --- a/src/migration/migrator.ts +++ b/src/migration/migrator.ts @@ -523,7 +523,7 @@ export class Migrator { } } - if (adapter.supportsTransactionalDdl) { + if (adapter.supportsTransactionalDdl && !this.#props.disableTransactions) { return this.#props.db.transaction().execute(run) } else { return this.#props.db.connection().execute(run) @@ -822,6 +822,15 @@ export interface MigratorProps { * Default is `name0.localeCompare(name1)`. */ readonly nameComparator?: (name0: string, name1: string) => number + + /** + * When `true`, don't run migrations in transactions even if the dialect supports transactional DDL. + * + * Default is `false`. + * + * This is useful when some migrations include queries that would fail otherwise. + */ + readonly disableTransactions?: boolean } /** diff --git a/test/node/src/migration.test.ts b/test/node/src/migration.test.ts index e612ad791..167e20226 100644 --- a/test/node/src/migration.test.ts +++ b/test/node/src/migration.test.ts @@ -10,6 +10,8 @@ import { Migrator, NO_MIGRATIONS, MigratorProps, + type QueryExecutor, + type Kysely, } from '../../../' import { @@ -19,7 +21,9 @@ import { initTest, TestContext, DIALECTS, + type Database, } from './test-setup.js' +import { createSandbox, type SinonSpy } from 'sinon' const CUSTOM_MIGRATION_SCHEMA = 'migrate' const CUSTOM_MIGRATION_TABLE = 'custom_migrations' @@ -652,6 +656,20 @@ for (const dialect of DIALECTS) { }) describe('migrateUp', () => { + const sandbox = createSandbox() + let transactionSpy: SinonSpy< + Parameters['transaction']>, + ReturnType['transaction']> + > + + beforeEach(() => { + transactionSpy = sandbox.spy(ctx.db, 'transaction') + }) + + afterEach(() => { + sandbox.restore() + }) + it('should migrate up one step', async () => { const [migrator, executedUpMethods] = createMigrations([ 'migration1', @@ -738,6 +756,42 @@ for (const dialect of DIALECTS) { expect(executedUpMethods1).to.eql(['migration1', 'migration3']) expect(executedUpMethods2).to.eql(['migration2', 'migration4']) }) + + it('should not execute in transaction if disableTransactions is true', async () => { + const [migrator, executedUpMethods] = createMigrations(['migration1'], { + disableTransactions: true, + }) + + const { results } = await migrator.migrateUp() + + expect(results).to.eql([ + { migrationName: 'migration1', direction: 'Up', status: 'Success' }, + ]) + + expect(executedUpMethods).to.eql(['migration1']) + + expect(transactionSpy.called).to.be.false + }) + + it('should execute in transaction if disableTransactions is false and transactionDdl supported', async () => { + const [migrator, executedUpMethods] = createMigrations(['migration1'], { + disableTransactions: false, + }) + + const { results } = await migrator.migrateUp() + + expect(results).to.eql([ + { migrationName: 'migration1', direction: 'Up', status: 'Success' }, + ]) + + expect(executedUpMethods).to.eql(['migration1']) + + if (ctx.db.getExecutor().adapter.supportsTransactionalDdl) { + expect(transactionSpy.called).to.be.true + } else { + expect(transactionSpy.called).to.be.false + } + }) }) describe('migrateDown', () => {