From 434cfe225633d7a847a1b6d47af89eb1b7ce1a24 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 15:26:49 -0500 Subject: [PATCH 01/40] [All] Added new `generated` configuration key to columns and column builders --- drizzle-orm/src/column-builder.ts | 35 ++++++++++++++++++++++++++++++- drizzle-orm/src/column.ts | 10 ++++++++- drizzle-orm/src/operations.ts | 7 ++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/drizzle-orm/src/column-builder.ts b/drizzle-orm/src/column-builder.ts index 7ef9b6d14..3a56cfc67 100644 --- a/drizzle-orm/src/column-builder.ts +++ b/drizzle-orm/src/column-builder.ts @@ -19,6 +19,16 @@ export type ColumnDataType = export type Dialect = 'pg' | 'mysql' | 'sqlite' | 'common'; +export type GeneratedStorageMode = 'virtual' | 'stored'; + +export type GeneratedType = 'always' | 'byDefault'; + +export type GeneratedColumnConfig = { + as: TDataType | SQL; + type?: GeneratedType; + mode?: GeneratedStorageMode; +}; + export interface ColumnBuilderBaseConfig { name: string; dataType: TDataType; @@ -26,23 +36,26 @@ export interface ColumnBuilderBaseConfig | undefined; } export type MakeColumnConfig< T extends ColumnBuilderBaseConfig, TTableName extends string, + TData = T extends { $type: infer U } ? U : T['data'], > = { name: T['name']; tableName: TTableName; dataType: T['dataType']; columnType: T['columnType']; - data: T extends { $type: infer U } ? U : T['data']; + data: TData; driverParam: T['driverParam']; notNull: T extends { notNull: true } ? true : false; hasDefault: T extends { hasDefault: true } ? true : false; enumValues: T['enumValues']; baseColumn: T extends { baseBuilder: infer U extends ColumnBuilderBase } ? BuildColumn : never; + generated: T['generated'] extends object ? GeneratedColumnConfig : undefined; } & {}; export type ColumnBuilderTypeConfig< @@ -60,6 +73,7 @@ export type ColumnBuilderTypeConfig< notNull: T extends { notNull: infer U } ? U : boolean; hasDefault: T extends { hasDefault: infer U } ? U : boolean; enumValues: T['enumValues']; + generated: GeneratedColumnConfig | undefined; } & TTypeConfig >; @@ -76,6 +90,7 @@ export type ColumnBuilderRuntimeConfig | undefined; } & TRuntimeConfig; export interface ColumnBuilderExtraConfig { @@ -100,11 +115,23 @@ export type $Type = T & { }; }; +export type HasGenerated = T & { + _: { + notNull: true; + hasDefault: true; + generated: TGenerated; + }; +}; + export interface ColumnBuilderBase< T extends ColumnBuilderBaseConfig = ColumnBuilderBaseConfig, TTypeConfig extends object = object, > { _: ColumnBuilderTypeConfig; + generatedAlwaysAs( + as: SQL | T['data'], + config?: Partial>, + ): HasGenerated; } // To understand how to use `ColumnBuilder` and `AnyColumnBuilder`, see `Column` and `AnyColumn` documentation. @@ -132,6 +159,7 @@ export abstract class ColumnBuilder< uniqueType: undefined, dataType, columnType, + generated: undefined, } as ColumnBuilderRuntimeConfig; } @@ -202,6 +230,11 @@ export abstract class ColumnBuilder< this.config.notNull = true; return this as TExtraConfig['primaryKeyHasDefault'] extends true ? HasDefault> : NotNull; } + + abstract generatedAlwaysAs( + as: SQL | T['data'], + config?: Partial>, + ): HasGenerated; } export type BuildColumn< diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index deacc073a..3f3bcbf80 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -1,4 +1,9 @@ -import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, ColumnDataType } from './column-builder.ts'; +import type { + ColumnBuilderBaseConfig, + ColumnBuilderRuntimeConfig, + ColumnDataType, + GeneratedColumnConfig, +} from './column-builder.ts'; import { entityKind } from './entity.ts'; import type { DriverValueMapper, SQL, SQLWrapper } from './sql/sql.ts'; import type { Table } from './table.ts'; @@ -25,6 +30,7 @@ export type ColumnTypeConfig, hasDefault: T['hasDefault']; enumValues: T['enumValues']; baseColumn: T extends { baseColumn: infer U } ? U : unknown; + generated: GeneratedColumnConfig | undefined; } & TTypeConfig; export type ColumnRuntimeConfig = ColumnBuilderRuntimeConfig< @@ -67,6 +73,7 @@ export abstract class Column< readonly dataType: T['dataType']; readonly columnType: T['columnType']; readonly enumValues: T['enumValues'] = undefined; + readonly generated: GeneratedColumnConfig | undefined = undefined; protected config: ColumnRuntimeConfig; @@ -86,6 +93,7 @@ export abstract class Column< this.uniqueType = config.uniqueType; this.dataType = config.dataType as T['dataType']; this.columnType = config.columnType; + this.generated = config.generated; } abstract getSQLType(): string; diff --git a/drizzle-orm/src/operations.ts b/drizzle-orm/src/operations.ts index 09cf41b8a..a79215836 100644 --- a/drizzle-orm/src/operations.ts +++ b/drizzle-orm/src/operations.ts @@ -8,10 +8,15 @@ export type RequiredKeyOnly = T extends A }> ? TKey : never; +export type NotGenerated = T extends AnyColumn<{ + generated: undefined; +}> ? TKey + : never; + export type OptionalKeyOnly< TKey extends string, T extends Column, -> = TKey extends RequiredKeyOnly ? never : TKey; +> = TKey extends RequiredKeyOnly ? never : TKey extends NotGenerated ? TKey : never; export type SelectedFieldsFlat = Record< string, From 0b2bb5ead80a30cf04061eab8ed6ca0788ac3a53 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 15:28:49 -0500 Subject: [PATCH 02/40] [MySql] Added generatedAlwaysAs method to all column builders and fixed types --- drizzle-orm/src/mysql-core/columns/bigint.ts | 2 ++ drizzle-orm/src/mysql-core/columns/binary.ts | 1 + drizzle-orm/src/mysql-core/columns/boolean.ts | 1 + drizzle-orm/src/mysql-core/columns/char.ts | 1 + drizzle-orm/src/mysql-core/columns/common.ts | 15 +++++++++++++++ drizzle-orm/src/mysql-core/columns/custom.ts | 1 + drizzle-orm/src/mysql-core/columns/date.ts | 2 ++ drizzle-orm/src/mysql-core/columns/datetime.ts | 3 ++- drizzle-orm/src/mysql-core/columns/decimal.ts | 1 + drizzle-orm/src/mysql-core/columns/double.ts | 1 + drizzle-orm/src/mysql-core/columns/enum.ts | 1 + drizzle-orm/src/mysql-core/columns/float.ts | 1 + drizzle-orm/src/mysql-core/columns/int.ts | 1 + drizzle-orm/src/mysql-core/columns/json.ts | 1 + drizzle-orm/src/mysql-core/columns/mediumint.ts | 1 + drizzle-orm/src/mysql-core/columns/real.ts | 1 + drizzle-orm/src/mysql-core/columns/serial.ts | 1 + drizzle-orm/src/mysql-core/columns/smallint.ts | 1 + drizzle-orm/src/mysql-core/columns/text.ts | 1 + drizzle-orm/src/mysql-core/columns/time.ts | 1 + drizzle-orm/src/mysql-core/columns/timestamp.ts | 2 ++ drizzle-orm/src/mysql-core/columns/tinyint.ts | 1 + drizzle-orm/src/mysql-core/columns/varbinary.ts | 1 + drizzle-orm/src/mysql-core/columns/varchar.ts | 1 + drizzle-orm/src/mysql-core/columns/year.ts | 1 + 25 files changed, 43 insertions(+), 1 deletion(-) diff --git a/drizzle-orm/src/mysql-core/columns/bigint.ts b/drizzle-orm/src/mysql-core/columns/bigint.ts index c80770d22..ca1eedb3f 100644 --- a/drizzle-orm/src/mysql-core/columns/bigint.ts +++ b/drizzle-orm/src/mysql-core/columns/bigint.ts @@ -11,6 +11,7 @@ export type MySqlBigInt53BuilderInitial = MySqlBigInt53Bui data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlBigInt53Builder> @@ -58,6 +59,7 @@ export type MySqlBigInt64BuilderInitial = MySqlBigInt64Bui data: bigint; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class MySqlBigInt64Builder> diff --git a/drizzle-orm/src/mysql-core/columns/binary.ts b/drizzle-orm/src/mysql-core/columns/binary.ts index 6deb385d8..87a8e0f8c 100644 --- a/drizzle-orm/src/mysql-core/columns/binary.ts +++ b/drizzle-orm/src/mysql-core/columns/binary.ts @@ -11,6 +11,7 @@ export type MySqlBinaryBuilderInitial = MySqlBinaryBuilder data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class MySqlBinaryBuilder> extends MySqlColumnBuilder< diff --git a/drizzle-orm/src/mysql-core/columns/boolean.ts b/drizzle-orm/src/mysql-core/columns/boolean.ts index a75131469..3a915e673 100644 --- a/drizzle-orm/src/mysql-core/columns/boolean.ts +++ b/drizzle-orm/src/mysql-core/columns/boolean.ts @@ -11,6 +11,7 @@ export type MySqlBooleanBuilderInitial = MySqlBooleanBuild data: boolean; driverParam: number | boolean; enumValues: undefined; + generated: undefined; }>; export class MySqlBooleanBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/char.ts b/drizzle-orm/src/mysql-core/columns/char.ts index 5466ec046..f871796a5 100644 --- a/drizzle-orm/src/mysql-core/columns/char.ts +++ b/drizzle-orm/src/mysql-core/columns/char.ts @@ -12,6 +12,7 @@ export type MySqlCharBuilderInitial; export class MySqlCharBuilder> extends MySqlColumnBuilder< diff --git a/drizzle-orm/src/mysql-core/columns/common.ts b/drizzle-orm/src/mysql-core/columns/common.ts index fe518cebb..85dca59ff 100644 --- a/drizzle-orm/src/mysql-core/columns/common.ts +++ b/drizzle-orm/src/mysql-core/columns/common.ts @@ -6,6 +6,7 @@ import type { ColumnBuilderRuntimeConfig, ColumnDataType, HasDefault, + HasGenerated, MakeColumnConfig, } from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; @@ -14,6 +15,7 @@ import { entityKind } from '~/entity.ts'; import type { ForeignKey, UpdateDeleteAction } from '~/mysql-core/foreign-keys.ts'; import { ForeignKeyBuilder } from '~/mysql-core/foreign-keys.ts'; import type { AnyMySqlTable, MySqlTable } from '~/mysql-core/table.ts'; +import type { SQL } from '~/sql/sql.ts'; import type { Update } from '~/utils.ts'; import { uniqueKeyName } from '../unique-constraint.ts'; @@ -30,6 +32,10 @@ export interface MySqlColumnBuilderBase< TTypeConfig extends object = object, > extends ColumnBuilderBase {} +export interface MySqlGeneratedColumnConfig { + mode?: 'virtual' | 'stored'; +} + export abstract class MySqlColumnBuilder< T extends ColumnBuilderBaseConfig = ColumnBuilderBaseConfig & { data: any; @@ -55,6 +61,15 @@ export abstract class MySqlColumnBuilder< return this; } + generatedAlwaysAs(as: SQL | T['data'], config?: MySqlGeneratedColumnConfig): HasGenerated { + this.config.generated = { + as, + type: 'always', + mode: config?.mode ?? 'virtual', + }; + return this as any; + } + /** @internal */ buildForeignKeys(column: MySqlColumn, table: MySqlTable): ForeignKey[] { return this.foreignKeyConfigs.map(({ ref, actions }) => { diff --git a/drizzle-orm/src/mysql-core/columns/custom.ts b/drizzle-orm/src/mysql-core/columns/custom.ts index 135bc8c09..1c5e2603f 100644 --- a/drizzle-orm/src/mysql-core/columns/custom.ts +++ b/drizzle-orm/src/mysql-core/columns/custom.ts @@ -14,6 +14,7 @@ export type ConvertCustomConfig = MySqlDateBuilder<{ data: Date; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class MySqlDateBuilder> extends MySqlColumnBuilder { @@ -55,6 +56,7 @@ export type MySqlDateStringBuilderInitial = MySqlDateStrin data: string; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class MySqlDateStringBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/datetime.ts b/drizzle-orm/src/mysql-core/columns/datetime.ts index cfe9ce0b7..040c57130 100644 --- a/drizzle-orm/src/mysql-core/columns/datetime.ts +++ b/drizzle-orm/src/mysql-core/columns/datetime.ts @@ -12,6 +12,7 @@ export type MySqlDateTimeBuilderInitial = MySqlDateTimeBui data: Date; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class MySqlDateTimeBuilder> @@ -68,8 +69,8 @@ export type MySqlDateTimeStringBuilderInitial = MySqlDateT columnType: 'MySqlDateTimeString'; data: string; driverParam: string | number; - enumValues: undefined; + generated: undefined; }>; export class MySqlDateTimeStringBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/decimal.ts b/drizzle-orm/src/mysql-core/columns/decimal.ts index db2bd78ac..fa25d9cdb 100644 --- a/drizzle-orm/src/mysql-core/columns/decimal.ts +++ b/drizzle-orm/src/mysql-core/columns/decimal.ts @@ -11,6 +11,7 @@ export type MySqlDecimalBuilderInitial = MySqlDecimalBuild data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class MySqlDecimalBuilder< diff --git a/drizzle-orm/src/mysql-core/columns/double.ts b/drizzle-orm/src/mysql-core/columns/double.ts index 52dc66f72..dd349cf27 100644 --- a/drizzle-orm/src/mysql-core/columns/double.ts +++ b/drizzle-orm/src/mysql-core/columns/double.ts @@ -11,6 +11,7 @@ export type MySqlDoubleBuilderInitial = MySqlDoubleBuilder data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlDoubleBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/enum.ts b/drizzle-orm/src/mysql-core/columns/enum.ts index a7d5399ed..1d8b4c1f5 100644 --- a/drizzle-orm/src/mysql-core/columns/enum.ts +++ b/drizzle-orm/src/mysql-core/columns/enum.ts @@ -13,6 +13,7 @@ export type MySqlEnumColumnBuilderInitial; export class MySqlEnumColumnBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/float.ts b/drizzle-orm/src/mysql-core/columns/float.ts index 71b0291f3..b66f1e05a 100644 --- a/drizzle-orm/src/mysql-core/columns/float.ts +++ b/drizzle-orm/src/mysql-core/columns/float.ts @@ -11,6 +11,7 @@ export type MySqlFloatBuilderInitial = MySqlFloatBuilder<{ data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlFloatBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/int.ts b/drizzle-orm/src/mysql-core/columns/int.ts index 4fa1bb936..dbfb85760 100644 --- a/drizzle-orm/src/mysql-core/columns/int.ts +++ b/drizzle-orm/src/mysql-core/columns/int.ts @@ -11,6 +11,7 @@ export type MySqlIntBuilderInitial = MySqlIntBuilder<{ data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlIntBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/json.ts b/drizzle-orm/src/mysql-core/columns/json.ts index 9e52d7bf8..f30ea1534 100644 --- a/drizzle-orm/src/mysql-core/columns/json.ts +++ b/drizzle-orm/src/mysql-core/columns/json.ts @@ -11,6 +11,7 @@ export type MySqlJsonBuilderInitial = MySqlJsonBuilder<{ data: unknown; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class MySqlJsonBuilder> extends MySqlColumnBuilder { diff --git a/drizzle-orm/src/mysql-core/columns/mediumint.ts b/drizzle-orm/src/mysql-core/columns/mediumint.ts index 9a9277fe0..268028b44 100644 --- a/drizzle-orm/src/mysql-core/columns/mediumint.ts +++ b/drizzle-orm/src/mysql-core/columns/mediumint.ts @@ -12,6 +12,7 @@ export type MySqlMediumIntBuilderInitial = MySqlMediumIntB data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlMediumIntBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/real.ts b/drizzle-orm/src/mysql-core/columns/real.ts index 37607d9c5..7dd41dda0 100644 --- a/drizzle-orm/src/mysql-core/columns/real.ts +++ b/drizzle-orm/src/mysql-core/columns/real.ts @@ -11,6 +11,7 @@ export type MySqlRealBuilderInitial = MySqlRealBuilder<{ data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlRealBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/serial.ts b/drizzle-orm/src/mysql-core/columns/serial.ts index 5a555c52a..da3f5d29c 100644 --- a/drizzle-orm/src/mysql-core/columns/serial.ts +++ b/drizzle-orm/src/mysql-core/columns/serial.ts @@ -19,6 +19,7 @@ export type MySqlSerialBuilderInitial = NotNull< data: number; driverParam: number; enumValues: undefined; + generated: undefined; }> > >; diff --git a/drizzle-orm/src/mysql-core/columns/smallint.ts b/drizzle-orm/src/mysql-core/columns/smallint.ts index e4653f5dd..fc1dd0d55 100644 --- a/drizzle-orm/src/mysql-core/columns/smallint.ts +++ b/drizzle-orm/src/mysql-core/columns/smallint.ts @@ -12,6 +12,7 @@ export type MySqlSmallIntBuilderInitial = MySqlSmallIntBui data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlSmallIntBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/text.ts b/drizzle-orm/src/mysql-core/columns/text.ts index 8a4a30822..72c232e16 100644 --- a/drizzle-orm/src/mysql-core/columns/text.ts +++ b/drizzle-orm/src/mysql-core/columns/text.ts @@ -14,6 +14,7 @@ export type MySqlTextBuilderInitial; export class MySqlTextBuilder> extends MySqlColumnBuilder< diff --git a/drizzle-orm/src/mysql-core/columns/time.ts b/drizzle-orm/src/mysql-core/columns/time.ts index d3a86dcc4..ae2251bda 100644 --- a/drizzle-orm/src/mysql-core/columns/time.ts +++ b/drizzle-orm/src/mysql-core/columns/time.ts @@ -11,6 +11,7 @@ export type MySqlTimeBuilderInitial = MySqlTimeBuilder<{ data: string; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class MySqlTimeBuilder> extends MySqlColumnBuilder< diff --git a/drizzle-orm/src/mysql-core/columns/timestamp.ts b/drizzle-orm/src/mysql-core/columns/timestamp.ts index 3b6df80d3..24e3b2650 100644 --- a/drizzle-orm/src/mysql-core/columns/timestamp.ts +++ b/drizzle-orm/src/mysql-core/columns/timestamp.ts @@ -12,6 +12,7 @@ export type MySqlTimestampBuilderInitial = MySqlTimestampB data: Date; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class MySqlTimestampBuilder> @@ -63,6 +64,7 @@ export type MySqlTimestampStringBuilderInitial = MySqlTime data: string; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class MySqlTimestampStringBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/tinyint.ts b/drizzle-orm/src/mysql-core/columns/tinyint.ts index 35a68cbd2..c749e6da8 100644 --- a/drizzle-orm/src/mysql-core/columns/tinyint.ts +++ b/drizzle-orm/src/mysql-core/columns/tinyint.ts @@ -12,6 +12,7 @@ export type MySqlTinyIntBuilderInitial = MySqlTinyIntBuild data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class MySqlTinyIntBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/varbinary.ts b/drizzle-orm/src/mysql-core/columns/varbinary.ts index a4a856509..be0a89cf6 100644 --- a/drizzle-orm/src/mysql-core/columns/varbinary.ts +++ b/drizzle-orm/src/mysql-core/columns/varbinary.ts @@ -11,6 +11,7 @@ export type MySqlVarBinaryBuilderInitial = MySqlVarBinaryB data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class MySqlVarBinaryBuilder> diff --git a/drizzle-orm/src/mysql-core/columns/varchar.ts b/drizzle-orm/src/mysql-core/columns/varchar.ts index 7db55563f..b692bf789 100644 --- a/drizzle-orm/src/mysql-core/columns/varchar.ts +++ b/drizzle-orm/src/mysql-core/columns/varchar.ts @@ -13,6 +13,7 @@ export type MySqlVarCharBuilderInitial; diff --git a/drizzle-orm/src/mysql-core/columns/year.ts b/drizzle-orm/src/mysql-core/columns/year.ts index 0e1a64d36..224de12e9 100644 --- a/drizzle-orm/src/mysql-core/columns/year.ts +++ b/drizzle-orm/src/mysql-core/columns/year.ts @@ -11,6 +11,7 @@ export type MySqlYearBuilderInitial = MySqlYearBuilder<{ data: number; driverParam: number; enumValues: undefined; + generated: undefined; }>; export class MySqlYearBuilder> extends MySqlColumnBuilder { From 4787fcb894a7e5fb18591c3a23e17913b4a5e00b Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 15:30:07 -0500 Subject: [PATCH 03/40] [Pg] Added `generatedAlwaysAs` and `generatedAsIdentity` methods to all Pg column builders and fixed types --- drizzle-orm/src/pg-core/columns/bigint.ts | 2 ++ drizzle-orm/src/pg-core/columns/bigserial.ts | 2 ++ drizzle-orm/src/pg-core/columns/boolean.ts | 1 + drizzle-orm/src/pg-core/columns/char.ts | 1 + drizzle-orm/src/pg-core/columns/cidr.ts | 1 + drizzle-orm/src/pg-core/columns/common.ts | 25 +++++++++++++++++++ drizzle-orm/src/pg-core/columns/custom.ts | 1 + drizzle-orm/src/pg-core/columns/date.ts | 2 ++ .../src/pg-core/columns/double-precision.ts | 1 + drizzle-orm/src/pg-core/columns/enum.ts | 1 + drizzle-orm/src/pg-core/columns/inet.ts | 1 + drizzle-orm/src/pg-core/columns/integer.ts | 1 + drizzle-orm/src/pg-core/columns/interval.ts | 1 + drizzle-orm/src/pg-core/columns/json.ts | 1 + drizzle-orm/src/pg-core/columns/jsonb.ts | 1 + drizzle-orm/src/pg-core/columns/macaddr.ts | 1 + drizzle-orm/src/pg-core/columns/macaddr8.ts | 1 + drizzle-orm/src/pg-core/columns/numeric.ts | 1 + drizzle-orm/src/pg-core/columns/real.ts | 1 + drizzle-orm/src/pg-core/columns/serial.ts | 1 + drizzle-orm/src/pg-core/columns/smallint.ts | 1 + .../src/pg-core/columns/smallserial.ts | 1 + drizzle-orm/src/pg-core/columns/text.ts | 1 + drizzle-orm/src/pg-core/columns/time.ts | 1 + drizzle-orm/src/pg-core/columns/timestamp.ts | 2 ++ drizzle-orm/src/pg-core/columns/uuid.ts | 1 + drizzle-orm/src/pg-core/columns/varchar.ts | 1 + 27 files changed, 55 insertions(+) diff --git a/drizzle-orm/src/pg-core/columns/bigint.ts b/drizzle-orm/src/pg-core/columns/bigint.ts index af2d8b036..fc3438dd5 100644 --- a/drizzle-orm/src/pg-core/columns/bigint.ts +++ b/drizzle-orm/src/pg-core/columns/bigint.ts @@ -12,6 +12,7 @@ export type PgBigInt53BuilderInitial = PgBigInt53Builder<{ data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class PgBigInt53Builder> extends PgColumnBuilder { @@ -51,6 +52,7 @@ export type PgBigInt64BuilderInitial = PgBigInt64Builder<{ data: bigint; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgBigInt64Builder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/bigserial.ts b/drizzle-orm/src/pg-core/columns/bigserial.ts index 69917678f..0cca21577 100644 --- a/drizzle-orm/src/pg-core/columns/bigserial.ts +++ b/drizzle-orm/src/pg-core/columns/bigserial.ts @@ -19,6 +19,7 @@ export type PgBigSerial53BuilderInitial = NotNull< data: number; driverParam: number; enumValues: undefined; + generated: undefined; }> > >; @@ -69,6 +70,7 @@ export type PgBigSerial64BuilderInitial = NotNull< data: bigint; driverParam: string; enumValues: undefined; + generated: undefined; }> > >; diff --git a/drizzle-orm/src/pg-core/columns/boolean.ts b/drizzle-orm/src/pg-core/columns/boolean.ts index 83135e3de..f4670f1a6 100644 --- a/drizzle-orm/src/pg-core/columns/boolean.ts +++ b/drizzle-orm/src/pg-core/columns/boolean.ts @@ -11,6 +11,7 @@ export type PgBooleanBuilderInitial = PgBooleanBuilder<{ data: boolean; driverParam: boolean; enumValues: undefined; + generated: undefined; }>; export class PgBooleanBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/char.ts b/drizzle-orm/src/pg-core/columns/char.ts index 85eb65954..9f33de4ae 100644 --- a/drizzle-orm/src/pg-core/columns/char.ts +++ b/drizzle-orm/src/pg-core/columns/char.ts @@ -12,6 +12,7 @@ export type PgCharBuilderInitial; export class PgCharBuilder> extends PgColumnBuilder< diff --git a/drizzle-orm/src/pg-core/columns/cidr.ts b/drizzle-orm/src/pg-core/columns/cidr.ts index 2f37d0348..9c2e9e19f 100644 --- a/drizzle-orm/src/pg-core/columns/cidr.ts +++ b/drizzle-orm/src/pg-core/columns/cidr.ts @@ -11,6 +11,7 @@ export type PgCidrBuilderInitial = PgCidrBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgCidrBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/common.ts b/drizzle-orm/src/pg-core/columns/common.ts index 17ba6b929..c11dfc81a 100644 --- a/drizzle-orm/src/pg-core/columns/common.ts +++ b/drizzle-orm/src/pg-core/columns/common.ts @@ -4,6 +4,8 @@ import type { ColumnBuilderExtraConfig, ColumnBuilderRuntimeConfig, ColumnDataType, + GeneratedColumnConfig, + HasGenerated, MakeColumnConfig, } from '~/column-builder.ts'; import { ColumnBuilder } from '~/column-builder.ts'; @@ -12,6 +14,7 @@ import { Column } from '~/column.ts'; import { entityKind, is } from '~/entity.ts'; import type { Update } from '~/utils.ts'; +import { type SQL, sql } from '~/index.ts'; import type { ForeignKey, UpdateDeleteAction } from '~/pg-core/foreign-keys.ts'; import { ForeignKeyBuilder } from '~/pg-core/foreign-keys.ts'; import type { AnyPgTable, PgTable } from '~/pg-core/table.ts'; @@ -32,6 +35,10 @@ export interface PgColumnBuilderBase< TTypeConfig extends object = object, > extends ColumnBuilderBase {} +export interface PgGeneratedColumnConfig { + type?: 'always' | 'byDefault'; +} + export abstract class PgColumnBuilder< T extends ColumnBuilderBaseConfig = ColumnBuilderBaseConfig, TRuntimeConfig extends object = object, @@ -52,6 +59,7 @@ export abstract class PgColumnBuilder< data: T['data'][]; driverParam: T['driverParam'][] | string; enumValues: T['enumValues']; + generated: GeneratedColumnConfig; } & (T extends { notNull: true } ? { notNull: true } : {}) & (T extends { hasDefault: true } ? { hasDefault: true } : {}), @@ -78,6 +86,23 @@ export abstract class PgColumnBuilder< return this; } + generatedAlwaysAs(as: SQL | T['data'], config?: PgGeneratedColumnConfig): HasGenerated { + this.config.generated = { + as, + type: config?.type ?? 'always', + mode: 'stored', + }; + return this as any; + } + + generatedAsIdentity(config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }): HasGenerated { + this.config.generated = { + as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, + type: config?.type ?? 'always', + }; + return this as any; + } + /** @internal */ buildForeignKeys(column: PgColumn, table: PgTable): ForeignKey[] { return this.foreignKeyConfigs.map(({ ref, actions }) => { diff --git a/drizzle-orm/src/pg-core/columns/custom.ts b/drizzle-orm/src/pg-core/columns/custom.ts index 7af6c73d1..4249e326c 100644 --- a/drizzle-orm/src/pg-core/columns/custom.ts +++ b/drizzle-orm/src/pg-core/columns/custom.ts @@ -14,6 +14,7 @@ export type ConvertCustomConfig = PgDateBuilder<{ data: Date; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgDateBuilder> extends PgDateColumnBaseBuilder { @@ -52,6 +53,7 @@ export type PgDateStringBuilderInitial = PgDateStringBuild data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgDateStringBuilder> diff --git a/drizzle-orm/src/pg-core/columns/double-precision.ts b/drizzle-orm/src/pg-core/columns/double-precision.ts index a6bbdc6ff..879c25ccb 100644 --- a/drizzle-orm/src/pg-core/columns/double-precision.ts +++ b/drizzle-orm/src/pg-core/columns/double-precision.ts @@ -11,6 +11,7 @@ export type PgDoublePrecisionBuilderInitial = PgDoublePrec data: number; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class PgDoublePrecisionBuilder> diff --git a/drizzle-orm/src/pg-core/columns/enum.ts b/drizzle-orm/src/pg-core/columns/enum.ts index 7f3840271..efc81ae90 100644 --- a/drizzle-orm/src/pg-core/columns/enum.ts +++ b/drizzle-orm/src/pg-core/columns/enum.ts @@ -13,6 +13,7 @@ export type PgEnumColumnBuilderInitial; const isPgEnumSym = Symbol.for('drizzle:isPgEnum'); diff --git a/drizzle-orm/src/pg-core/columns/inet.ts b/drizzle-orm/src/pg-core/columns/inet.ts index cdca1797d..f8e473de7 100644 --- a/drizzle-orm/src/pg-core/columns/inet.ts +++ b/drizzle-orm/src/pg-core/columns/inet.ts @@ -11,6 +11,7 @@ export type PgInetBuilderInitial = PgInetBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgInetBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/integer.ts b/drizzle-orm/src/pg-core/columns/integer.ts index 3ef9e248c..e83369760 100644 --- a/drizzle-orm/src/pg-core/columns/integer.ts +++ b/drizzle-orm/src/pg-core/columns/integer.ts @@ -11,6 +11,7 @@ type PgIntegerBuilderInitial = PgIntegerBuilder<{ data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class PgIntegerBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/interval.ts b/drizzle-orm/src/pg-core/columns/interval.ts index c8b77d9a2..c70dd0c04 100644 --- a/drizzle-orm/src/pg-core/columns/interval.ts +++ b/drizzle-orm/src/pg-core/columns/interval.ts @@ -12,6 +12,7 @@ export type PgIntervalBuilderInitial = PgIntervalBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgIntervalBuilder> diff --git a/drizzle-orm/src/pg-core/columns/json.ts b/drizzle-orm/src/pg-core/columns/json.ts index c6c869eb7..ddb97b67e 100644 --- a/drizzle-orm/src/pg-core/columns/json.ts +++ b/drizzle-orm/src/pg-core/columns/json.ts @@ -11,6 +11,7 @@ export type PgJsonBuilderInitial = PgJsonBuilder<{ data: unknown; driverParam: unknown; enumValues: undefined; + generated: undefined; }>; export class PgJsonBuilder> extends PgColumnBuilder< diff --git a/drizzle-orm/src/pg-core/columns/jsonb.ts b/drizzle-orm/src/pg-core/columns/jsonb.ts index 38d346b17..a44b26fd0 100644 --- a/drizzle-orm/src/pg-core/columns/jsonb.ts +++ b/drizzle-orm/src/pg-core/columns/jsonb.ts @@ -11,6 +11,7 @@ export type PgJsonbBuilderInitial = PgJsonbBuilder<{ data: unknown; driverParam: unknown; enumValues: undefined; + generated: undefined; }>; export class PgJsonbBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/macaddr.ts b/drizzle-orm/src/pg-core/columns/macaddr.ts index 189a56187..edc27f7c0 100644 --- a/drizzle-orm/src/pg-core/columns/macaddr.ts +++ b/drizzle-orm/src/pg-core/columns/macaddr.ts @@ -11,6 +11,7 @@ export type PgMacaddrBuilderInitial = PgMacaddrBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgMacaddrBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/macaddr8.ts b/drizzle-orm/src/pg-core/columns/macaddr8.ts index cb78fc0b4..fc611d063 100644 --- a/drizzle-orm/src/pg-core/columns/macaddr8.ts +++ b/drizzle-orm/src/pg-core/columns/macaddr8.ts @@ -11,6 +11,7 @@ export type PgMacaddr8BuilderInitial = PgMacaddr8Builder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgMacaddr8Builder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/numeric.ts b/drizzle-orm/src/pg-core/columns/numeric.ts index e3ea778e4..d740c1a4a 100644 --- a/drizzle-orm/src/pg-core/columns/numeric.ts +++ b/drizzle-orm/src/pg-core/columns/numeric.ts @@ -11,6 +11,7 @@ export type PgNumericBuilderInitial = PgNumericBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgNumericBuilder> extends PgColumnBuilder< diff --git a/drizzle-orm/src/pg-core/columns/real.ts b/drizzle-orm/src/pg-core/columns/real.ts index 9059384db..6abe81441 100644 --- a/drizzle-orm/src/pg-core/columns/real.ts +++ b/drizzle-orm/src/pg-core/columns/real.ts @@ -11,6 +11,7 @@ export type PgRealBuilderInitial = PgRealBuilder<{ data: number; driverParam: string | number; enumValues: undefined; + generated: undefined; }>; export class PgRealBuilder> extends PgColumnBuilder< diff --git a/drizzle-orm/src/pg-core/columns/serial.ts b/drizzle-orm/src/pg-core/columns/serial.ts index b4ac9ed6b..a15619a87 100644 --- a/drizzle-orm/src/pg-core/columns/serial.ts +++ b/drizzle-orm/src/pg-core/columns/serial.ts @@ -19,6 +19,7 @@ export type PgSerialBuilderInitial = NotNull< data: number; driverParam: number; enumValues: undefined; + generated: undefined; }> > >; diff --git a/drizzle-orm/src/pg-core/columns/smallint.ts b/drizzle-orm/src/pg-core/columns/smallint.ts index 23c5d47f6..2ae80b5d9 100644 --- a/drizzle-orm/src/pg-core/columns/smallint.ts +++ b/drizzle-orm/src/pg-core/columns/smallint.ts @@ -11,6 +11,7 @@ export type PgSmallIntBuilderInitial = PgSmallIntBuilder<{ data: number; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class PgSmallIntBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/smallserial.ts b/drizzle-orm/src/pg-core/columns/smallserial.ts index 7d02c306e..59688fbed 100644 --- a/drizzle-orm/src/pg-core/columns/smallserial.ts +++ b/drizzle-orm/src/pg-core/columns/smallserial.ts @@ -11,6 +11,7 @@ export type PgSmallSerialBuilderInitial = PgSmallSerialBui data: number; driverParam: number; enumValues: undefined; + generated: undefined; }>; export class PgSmallSerialBuilder> diff --git a/drizzle-orm/src/pg-core/columns/text.ts b/drizzle-orm/src/pg-core/columns/text.ts index 844e9182c..47c3c9045 100644 --- a/drizzle-orm/src/pg-core/columns/text.ts +++ b/drizzle-orm/src/pg-core/columns/text.ts @@ -12,6 +12,7 @@ type PgTextBuilderInitial; export class PgTextBuilder< diff --git a/drizzle-orm/src/pg-core/columns/time.ts b/drizzle-orm/src/pg-core/columns/time.ts index ff7772bb0..fe82c9142 100644 --- a/drizzle-orm/src/pg-core/columns/time.ts +++ b/drizzle-orm/src/pg-core/columns/time.ts @@ -13,6 +13,7 @@ export type PgTimeBuilderInitial = PgTimeBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgTimeBuilder> extends PgDateColumnBaseBuilder< diff --git a/drizzle-orm/src/pg-core/columns/timestamp.ts b/drizzle-orm/src/pg-core/columns/timestamp.ts index 3060bfb3f..85b341c74 100644 --- a/drizzle-orm/src/pg-core/columns/timestamp.ts +++ b/drizzle-orm/src/pg-core/columns/timestamp.ts @@ -13,6 +13,7 @@ export type PgTimestampBuilderInitial = PgTimestampBuilder data: Date; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgTimestampBuilder> @@ -74,6 +75,7 @@ export type PgTimestampStringBuilderInitial = PgTimestampS data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgTimestampStringBuilder> diff --git a/drizzle-orm/src/pg-core/columns/uuid.ts b/drizzle-orm/src/pg-core/columns/uuid.ts index 4c9ba04ed..24907ce99 100644 --- a/drizzle-orm/src/pg-core/columns/uuid.ts +++ b/drizzle-orm/src/pg-core/columns/uuid.ts @@ -12,6 +12,7 @@ export type PgUUIDBuilderInitial = PgUUIDBuilder<{ data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgUUIDBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/varchar.ts b/drizzle-orm/src/pg-core/columns/varchar.ts index 31d66aade..84283d40e 100644 --- a/drizzle-orm/src/pg-core/columns/varchar.ts +++ b/drizzle-orm/src/pg-core/columns/varchar.ts @@ -12,6 +12,7 @@ export type PgVarcharBuilderInitial; export class PgVarcharBuilder> extends PgColumnBuilder< From 862fd2f35ff74ee6994d3e0c346a93d6f69e6646 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 15:30:50 -0500 Subject: [PATCH 04/40] [SQLite] Added `generatedAlwaysAs` method to all SQLite column builders and fixed types --- drizzle-orm/src/sqlite-core/columns/blob.ts | 3 +++ drizzle-orm/src/sqlite-core/columns/common.ts | 15 +++++++++++++++ drizzle-orm/src/sqlite-core/columns/custom.ts | 1 + drizzle-orm/src/sqlite-core/columns/integer.ts | 3 +++ drizzle-orm/src/sqlite-core/columns/numeric.ts | 1 + drizzle-orm/src/sqlite-core/columns/real.ts | 1 + drizzle-orm/src/sqlite-core/columns/text.ts | 2 ++ 7 files changed, 26 insertions(+) diff --git a/drizzle-orm/src/sqlite-core/columns/blob.ts b/drizzle-orm/src/sqlite-core/columns/blob.ts index 50a94c068..7371eb299 100644 --- a/drizzle-orm/src/sqlite-core/columns/blob.ts +++ b/drizzle-orm/src/sqlite-core/columns/blob.ts @@ -14,6 +14,7 @@ export type SQLiteBigIntBuilderInitial = SQLiteBigIntBuild data: bigint; driverParam: Buffer; enumValues: undefined; + generated: undefined; }>; export class SQLiteBigIntBuilder> @@ -56,6 +57,7 @@ export type SQLiteBlobJsonBuilderInitial = SQLiteBlobJsonB data: unknown; driverParam: Buffer; enumValues: undefined; + generated: undefined; }>; export class SQLiteBlobJsonBuilder> @@ -101,6 +103,7 @@ export type SQLiteBlobBufferBuilderInitial = SQLiteBlobBuf data: Buffer; driverParam: Buffer; enumValues: undefined; + generated: undefined; }>; export class SQLiteBlobBufferBuilder> diff --git a/drizzle-orm/src/sqlite-core/columns/common.ts b/drizzle-orm/src/sqlite-core/columns/common.ts index 83a5e175d..0f2772d31 100644 --- a/drizzle-orm/src/sqlite-core/columns/common.ts +++ b/drizzle-orm/src/sqlite-core/columns/common.ts @@ -4,12 +4,14 @@ import type { ColumnBuilderExtraConfig, ColumnBuilderRuntimeConfig, ColumnDataType, + HasGenerated, MakeColumnConfig, } from '~/column-builder.ts'; import { ColumnBuilder } from '~/column-builder.ts'; import { Column } from '~/column.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; +import type { SQL } from '~/sql/sql.ts'; import type { ForeignKey, UpdateDeleteAction } from '~/sqlite-core/foreign-keys.ts'; import { ForeignKeyBuilder } from '~/sqlite-core/foreign-keys.ts'; import type { AnySQLiteTable, SQLiteTable } from '~/sqlite-core/table.ts'; @@ -29,6 +31,10 @@ export interface SQLiteColumnBuilderBase< TTypeConfig extends object = object, > extends ColumnBuilderBase {} +export interface SQLiteGeneratedColumnConfig { + mode?: 'virtual' | 'stored'; +} + export abstract class SQLiteColumnBuilder< T extends ColumnBuilderBaseConfig = ColumnBuilderBaseConfig, TRuntimeConfig extends object = object, @@ -57,6 +63,15 @@ export abstract class SQLiteColumnBuilder< return this; } + generatedAlwaysAs(as: SQL | T['data'], config?: SQLiteGeneratedColumnConfig): HasGenerated { + this.config.generated = { + as, + type: 'always', + mode: config?.mode ?? 'virtual', + }; + return this as any; + } + /** @internal */ buildForeignKeys(column: SQLiteColumn, table: SQLiteTable): ForeignKey[] { return this.foreignKeyConfigs.map(({ ref, actions }) => { diff --git a/drizzle-orm/src/sqlite-core/columns/custom.ts b/drizzle-orm/src/sqlite-core/columns/custom.ts index e8b765ffc..513f380e0 100644 --- a/drizzle-orm/src/sqlite-core/columns/custom.ts +++ b/drizzle-orm/src/sqlite-core/columns/custom.ts @@ -14,6 +14,7 @@ export type ConvertCustomConfig = SQLiteIntegerBui data: number; driverParam: number; enumValues: undefined; + generated: undefined; }>; export class SQLiteIntegerBuilder> @@ -101,6 +102,7 @@ export type SQLiteTimestampBuilderInitial = SQLiteTimestam data: Date; driverParam: number; enumValues: undefined; + generated: undefined; }>; export class SQLiteTimestampBuilder> @@ -162,6 +164,7 @@ export type SQLiteBooleanBuilderInitial = SQLiteBooleanBui data: boolean; driverParam: number; enumValues: undefined; + generated: undefined; }>; export class SQLiteBooleanBuilder> diff --git a/drizzle-orm/src/sqlite-core/columns/numeric.ts b/drizzle-orm/src/sqlite-core/columns/numeric.ts index 041e1390c..5cd29f78f 100644 --- a/drizzle-orm/src/sqlite-core/columns/numeric.ts +++ b/drizzle-orm/src/sqlite-core/columns/numeric.ts @@ -11,6 +11,7 @@ export type SQLiteNumericBuilderInitial = SQLiteNumericBui data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class SQLiteNumericBuilder> diff --git a/drizzle-orm/src/sqlite-core/columns/real.ts b/drizzle-orm/src/sqlite-core/columns/real.ts index 3186d4b8f..c04cb1be3 100644 --- a/drizzle-orm/src/sqlite-core/columns/real.ts +++ b/drizzle-orm/src/sqlite-core/columns/real.ts @@ -11,6 +11,7 @@ export type SQLiteRealBuilderInitial = SQLiteRealBuilder<{ data: number; driverParam: number; enumValues: undefined; + generated: undefined; }>; export class SQLiteRealBuilder> diff --git a/drizzle-orm/src/sqlite-core/columns/text.ts b/drizzle-orm/src/sqlite-core/columns/text.ts index 4b1285259..7eecf1d1f 100644 --- a/drizzle-orm/src/sqlite-core/columns/text.ts +++ b/drizzle-orm/src/sqlite-core/columns/text.ts @@ -12,6 +12,7 @@ export type SQLiteTextBuilderInitial; export class SQLiteTextBuilder> extends SQLiteColumnBuilder< @@ -62,6 +63,7 @@ export type SQLiteTextJsonBuilderInitial = SQLiteTextJsonB data: unknown; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class SQLiteTextJsonBuilder> From 148f1c0b6ab22243bbc5b6a7d5f930db35d607d9 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 15:31:15 -0500 Subject: [PATCH 05/40] [All] Fixed type tests --- drizzle-orm/type-tests/mysql/tables.ts | 13 ++++++++++++ drizzle-orm/type-tests/pg/array.ts | 1 + drizzle-orm/type-tests/pg/tables.ts | 28 +++++++++++++++++++++++++ drizzle-orm/type-tests/sqlite/tables.ts | 6 ++++++ 4 files changed, 48 insertions(+) diff --git a/drizzle-orm/type-tests/mysql/tables.ts b/drizzle-orm/type-tests/mysql/tables.ts index 6eac879da..deaf0ff40 100644 --- a/drizzle-orm/type-tests/mysql/tables.ts +++ b/drizzle-orm/type-tests/mysql/tables.ts @@ -135,6 +135,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: MySqlColumn<{ name: 'id'; @@ -147,6 +148,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -184,6 +186,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: MySqlColumn<{ name: 'id'; @@ -196,6 +199,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -231,6 +235,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -243,6 +248,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -278,6 +284,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -290,6 +297,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -317,6 +325,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -329,6 +338,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -356,6 +366,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -368,6 +379,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -398,6 +410,7 @@ Expect< enumValues: undefined; baseColumn: never; dialect: 'mysql'; + generated: undefined; }, Simplify['_']> > diff --git a/drizzle-orm/type-tests/pg/array.ts b/drizzle-orm/type-tests/pg/array.ts index 03ea190b4..87ba3e3d0 100644 --- a/drizzle-orm/type-tests/pg/array.ts +++ b/drizzle-orm/type-tests/pg/array.ts @@ -20,6 +20,7 @@ import { integer, pgTable } from '~/pg-core/index.ts'; hasDefault: false; enumValues: undefined; baseColumn: never; + generated: undefined; } >, typeof table['a']['_']['baseColumn'] diff --git a/drizzle-orm/type-tests/pg/tables.ts b/drizzle-orm/type-tests/pg/tables.ts index 4a940ebcb..84d2f95c8 100644 --- a/drizzle-orm/type-tests/pg/tables.ts +++ b/drizzle-orm/type-tests/pg/tables.ts @@ -179,6 +179,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -191,6 +192,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -230,6 +232,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -242,6 +245,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -279,6 +283,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -291,6 +296,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -328,6 +334,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -340,6 +347,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -367,6 +375,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -379,6 +388,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -406,6 +416,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -418,6 +429,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -460,6 +472,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -472,6 +485,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers2 @@ -514,6 +528,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -526,6 +541,7 @@ Expect< hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers2 @@ -566,6 +582,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -578,6 +595,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers2 @@ -618,6 +636,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -630,6 +649,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers2 @@ -657,6 +677,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -669,6 +690,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers2 @@ -696,6 +718,7 @@ Expect< notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -708,6 +731,7 @@ Expect< driverParam: string | number; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers2 @@ -810,6 +834,7 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); notNull: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; name: PgColumn<{ tableName: 'cities_table'; @@ -822,6 +847,7 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); enumValues: [string, ...string[]]; notNull: true; baseColumn: never; + generated: undefined; }>; role: PgColumn<{ tableName: 'cities_table'; @@ -834,6 +860,7 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); enumValues: ['admin', 'user']; notNull: true; baseColumn: never; + generated: undefined; }>; population: PgColumn<{ tableName: 'cities_table'; @@ -846,6 +873,7 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); hasDefault: true; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }; }>, diff --git a/drizzle-orm/type-tests/sqlite/tables.ts b/drizzle-orm/type-tests/sqlite/tables.ts index d56b5fe09..ac01719f3 100644 --- a/drizzle-orm/type-tests/sqlite/tables.ts +++ b/drizzle-orm/type-tests/sqlite/tables.ts @@ -166,6 +166,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: SQLiteColumn<{ name: 'id'; @@ -178,6 +179,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -209,6 +211,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: SQLiteColumn<{ name: 'city_id'; @@ -221,6 +224,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers @@ -248,6 +252,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; cityId: SQLiteColumn<{ name: 'city_id'; @@ -260,6 +265,7 @@ Expect< tableName: 'new_yorkers'; enumValues: undefined; baseColumn: never; + generated: undefined; }>; }>, typeof newYorkers From e20b29b07bd2db8db192eacf9b67abe4fd218a48 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 18:47:53 -0500 Subject: [PATCH 06/40] [All] fixed update set types and added type tests --- .../src/mysql-core/query-builders/update.ts | 3 +- .../src/pg-core/query-builders/update.ts | 2 +- .../src/sqlite-core/query-builders/update.ts | 2 +- .../type-tests/mysql/generated-columns.ts | 158 ++++++++++++++++++ drizzle-orm/type-tests/mysql/tables.ts | 35 ++++ drizzle-orm/type-tests/mysql/with.ts | 15 ++ .../type-tests/pg/generated-columns.ts | 158 ++++++++++++++++++ drizzle-orm/type-tests/pg/tables.ts | 31 ++++ drizzle-orm/type-tests/pg/with.ts | 15 ++ .../type-tests/sqlite/generated-columns.ts | 158 ++++++++++++++++++ drizzle-orm/type-tests/sqlite/tables.ts | 26 +++ drizzle-orm/type-tests/sqlite/with.ts | 15 ++ 12 files changed, 615 insertions(+), 3 deletions(-) create mode 100644 drizzle-orm/type-tests/mysql/generated-columns.ts create mode 100644 drizzle-orm/type-tests/pg/generated-columns.ts create mode 100644 drizzle-orm/type-tests/sqlite/generated-columns.ts diff --git a/drizzle-orm/src/mysql-core/query-builders/update.ts b/drizzle-orm/src/mysql-core/query-builders/update.ts index 94b884058..48f68f9e5 100644 --- a/drizzle-orm/src/mysql-core/query-builders/update.ts +++ b/drizzle-orm/src/mysql-core/query-builders/update.ts @@ -1,5 +1,6 @@ import type { GetColumnData } from '~/column.ts'; import { entityKind } from '~/entity.ts'; +// import type { NotGenerated } from '~/index.ts'; import type { MySqlDialect } from '~/mysql-core/dialect.ts'; import type { AnyQueryResultHKT, @@ -25,7 +26,7 @@ export interface MySqlUpdateConfig { export type MySqlUpdateSetSource = & { - [Key in keyof TTable['_']['columns']]?: + [Key in keyof TTable['$inferInsert']]?: | GetColumnData | SQL; } diff --git a/drizzle-orm/src/pg-core/query-builders/update.ts b/drizzle-orm/src/pg-core/query-builders/update.ts index 449f99149..83afefb9e 100644 --- a/drizzle-orm/src/pg-core/query-builders/update.ts +++ b/drizzle-orm/src/pg-core/query-builders/update.ts @@ -25,7 +25,7 @@ export interface PgUpdateConfig { export type PgUpdateSetSource = & { - [Key in keyof TTable['_']['columns']]?: + [Key in keyof TTable['$inferInsert']]?: | GetColumnData | SQL; } diff --git a/drizzle-orm/src/sqlite-core/query-builders/update.ts b/drizzle-orm/src/sqlite-core/query-builders/update.ts index 857a944d8..dcda1581e 100644 --- a/drizzle-orm/src/sqlite-core/query-builders/update.ts +++ b/drizzle-orm/src/sqlite-core/query-builders/update.ts @@ -19,7 +19,7 @@ export interface SQLiteUpdateConfig { export type SQLiteUpdateSetSource = & { - [Key in keyof TTable['_']['columns']]?: + [Key in keyof TTable['$inferInsert']]?: | GetColumnData | SQL; } diff --git a/drizzle-orm/type-tests/mysql/generated-columns.ts b/drizzle-orm/type-tests/mysql/generated-columns.ts new file mode 100644 index 000000000..ed9c36349 --- /dev/null +++ b/drizzle-orm/type-tests/mysql/generated-columns.ts @@ -0,0 +1,158 @@ +import { type Equal, Expect } from 'type-tests/utils'; +import { type InferInsertModel, type InferSelectModel, sql } from '~/index'; +import { mysqlTable, serial, text, varchar } from '~/mysql-core'; +import { drizzle } from '~/mysql2'; +import { db } from './db'; + +const users = mysqlTable( + 'users', + { + id: serial('id').primaryKey(), + firstName: varchar('first_name', { length: 255 }), + lastName: varchar('last_name', { length: 255 }), + email: text('email').notNull(), + fullName: text('full_name').generatedAlwaysAs(sql`concat_ws(first_name, ' ', last_name)`), + upperName: text('upper_name').generatedAlwaysAs( + sql` case when first_name is null then null else upper(first_name) end `, + ).$type(), // There is no way for drizzle to detect nullability in these cases. This is how the user can work around it + }, +); +{ + type User = typeof users.$inferSelect; + type NewUser = typeof users.$inferInsert; + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }, + User + > + >(); + + Expect< + Equal< + { + email: string; + id?: number | undefined; + firstName?: string | null | undefined; + lastName?: string | null | undefined; + }, + NewUser + > + >(); +} + +{ + type User = InferSelectModel; + type NewUser = InferInsertModel; + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }, + User + > + >(); + + Expect< + Equal< + { + email: string; + id?: number | undefined; + firstName?: string | null | undefined; + lastName?: string | null | undefined; + }, + NewUser + > + >(); +} + +{ + const dbUsers = await db.select().from(users); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }[], + typeof dbUsers + > + >(); +} + +{ + const db = drizzle({} as any, { schema: { users }, mode: 'default' }); + + const dbUser = await db.query.users.findFirst(); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + } | undefined, + typeof dbUser + > + >(); +} + +{ + const db = drizzle({} as any, { schema: { users }, mode: 'default' }); + + const dbUser = await db.query.users.findMany(); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }[], + typeof dbUser + > + >(); +} + +{ + // @ts-expect-error - Can't use the fullName because it's a generated column + await db.insert(users).values({ + firstName: 'test', + lastName: 'test', + email: 'test', + fullName: 'test', + }); +} + +{ + await db.update(users).set({ + firstName: 'test', + lastName: 'test', + email: 'test', + // @ts-expect-error - Can't use the fullName because it's a generated column + fullName: 'test', + }); +} diff --git a/drizzle-orm/type-tests/mysql/tables.ts b/drizzle-orm/type-tests/mysql/tables.ts index deaf0ff40..4cb09965f 100644 --- a/drizzle-orm/type-tests/mysql/tables.ts +++ b/drizzle-orm/type-tests/mysql/tables.ts @@ -477,6 +477,41 @@ Expect< Expect>; } +{ // All types with generated columns + const test = mysqlTable('test', { + test1: mysqlEnum('test', ['a', 'b', 'c'] as const).generatedAlwaysAs(sql``), + test2: mysqlEnum('test', ['a', 'b', 'c']).generatedAlwaysAs(sql``), + test3: varchar('test', { length: 255, enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test4: varchar('test', { length: 255, enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test5: text('test', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test6: text('test', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test7: tinytext('test', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test8: tinytext('test', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test9: mediumtext('test', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test10: mediumtext('test', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test11: longtext('test', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test12: longtext('test', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test13: char('test', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test14: char('test', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test15: text('test').generatedAlwaysAs(sql``), + }); + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; +} + { const getUsersTable = (schemaName: TSchema) => { return mysqlSchema(schemaName).table('users', { diff --git a/drizzle-orm/type-tests/mysql/with.ts b/drizzle-orm/type-tests/mysql/with.ts index 338f9c43b..4da75ef45 100644 --- a/drizzle-orm/type-tests/mysql/with.ts +++ b/drizzle-orm/type-tests/mysql/with.ts @@ -11,6 +11,7 @@ const orders = mysqlTable('orders', { product: text('product').notNull(), amount: int('amount').notNull(), quantity: int('quantity').notNull(), + generated: text('generatedText').generatedAlwaysAs(sql``), }); { @@ -62,4 +63,18 @@ const orders = mysqlTable('orders', { productSales: number; }[], typeof result> >; + + const allOrdersWith = db.$with('all_orders_with').as(db.select().from(orders)); + const allFromWith = await db.with(allOrdersWith).select().from(allOrdersWith); + + Expect< + Equal<{ + id: number; + region: string; + product: string; + amount: number; + quantity: number; + generated: string; + }[], typeof allFromWith> + >; } diff --git a/drizzle-orm/type-tests/pg/generated-columns.ts b/drizzle-orm/type-tests/pg/generated-columns.ts new file mode 100644 index 000000000..1b2e1b073 --- /dev/null +++ b/drizzle-orm/type-tests/pg/generated-columns.ts @@ -0,0 +1,158 @@ +import { type Equal, Expect } from 'type-tests/utils'; +import { type InferInsertModel, type InferSelectModel, sql } from '~/index'; +import { drizzle } from '~/node-postgres'; +import { pgTable, serial, text, varchar } from '~/pg-core'; +import { db } from './db'; + +const users = pgTable( + 'users', + { + id: serial('id').primaryKey(), + firstName: varchar('first_name', { length: 255 }), + lastName: varchar('last_name', { length: 255 }), + email: text('email').notNull(), + fullName: text('full_name').generatedAlwaysAs(sql`concat_ws(first_name, ' ', last_name)`), + upperName: text('upper_name').generatedAlwaysAs( + sql` case when first_name is null then null else upper(first_name) end `, + ).$type(), // There is no way for drizzle to detect nullability in these cases. This is how the user can work around it + }, +); +{ + type User = typeof users.$inferSelect; + type NewUser = typeof users.$inferInsert; + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }, + User + > + >(); + + Expect< + Equal< + { + email: string; + id?: number | undefined; + firstName?: string | null | undefined; + lastName?: string | null | undefined; + }, + NewUser + > + >(); +} + +{ + type User = InferSelectModel; + type NewUser = InferInsertModel; + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }, + User + > + >(); + + Expect< + Equal< + { + email: string; + id?: number | undefined; + firstName?: string | null | undefined; + lastName?: string | null | undefined; + }, + NewUser + > + >(); +} + +{ + const dbUsers = await db.select().from(users); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }[], + typeof dbUsers + > + >(); +} + +{ + const db = drizzle({} as any, { schema: { users } }); + + const dbUser = await db.query.users.findFirst(); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + } | undefined, + typeof dbUser + > + >(); +} + +{ + const db = drizzle({} as any, { schema: { users } }); + + const dbUser = await db.query.users.findMany(); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }[], + typeof dbUser + > + >(); +} + +{ + // @ts-expect-error - Can't use the fullName because it's a generated column + await db.insert(users).values({ + firstName: 'test', + lastName: 'test', + email: 'test', + fullName: 'test', + }); +} + +{ + await db.update(users).set({ + firstName: 'test', + lastName: 'test', + email: 'test', + // @ts-expect-error - Can't use the fullName because it's a generated column + fullName: 'test', + }); +} diff --git a/drizzle-orm/type-tests/pg/tables.ts b/drizzle-orm/type-tests/pg/tables.ts index 84d2f95c8..76d98f80b 100644 --- a/drizzle-orm/type-tests/pg/tables.ts +++ b/drizzle-orm/type-tests/pg/tables.ts @@ -1019,6 +1019,37 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); Expect>; } +{ + const e1 = pgEnum('test', ['a', 'b', 'c']); + const e2 = pgEnum('test', ['a', 'b', 'c'] as const); + + const test = pgTable('test', { + col1: char('col1', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + col2: char('col2', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + col3: char('col3').generatedAlwaysAs(sql``), + col4: e1('col4').generatedAlwaysAs(sql``), + col5: e2('col5').generatedAlwaysAs(sql``), + col6: text('col6', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + col7: text('col7', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + col8: text('col8').generatedAlwaysAs(sql``), + col9: varchar('col9', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + col10: varchar('col10', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + col11: varchar('col11').generatedAlwaysAs(sql``), + }); + + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; + Expect>; +} + { const test = pgTable('test', { id: text('id').$defaultFn(() => crypto.randomUUID()).primaryKey(), diff --git a/drizzle-orm/type-tests/pg/with.ts b/drizzle-orm/type-tests/pg/with.ts index 1e4dfda99..a9ceac31b 100644 --- a/drizzle-orm/type-tests/pg/with.ts +++ b/drizzle-orm/type-tests/pg/with.ts @@ -11,6 +11,7 @@ const orders = pgTable('orders', { product: text('product').notNull(), amount: integer('amount').notNull(), quantity: integer('quantity').notNull(), + generated: text('generatedText').generatedAlwaysAs(sql``), }); { @@ -62,4 +63,18 @@ const orders = pgTable('orders', { productSales: number; }[], typeof result> >; + + const allOrdersWith = db.$with('all_orders_with').as(db.select().from(orders)); + const allFromWith = await db.with(allOrdersWith).select().from(allOrdersWith); + + Expect< + Equal<{ + id: number; + region: string; + product: string; + amount: number; + quantity: number; + generated: string; + }[], typeof allFromWith> + >; } diff --git a/drizzle-orm/type-tests/sqlite/generated-columns.ts b/drizzle-orm/type-tests/sqlite/generated-columns.ts new file mode 100644 index 000000000..6c9fc54cd --- /dev/null +++ b/drizzle-orm/type-tests/sqlite/generated-columns.ts @@ -0,0 +1,158 @@ +import { type Equal, Expect } from 'type-tests/utils'; +import { type InferInsertModel, type InferSelectModel, sql } from '~/index'; +import { drizzle } from '~/libsql'; +import { int, sqliteTable, text } from '~/sqlite-core'; +import { db } from './db'; + +const users = sqliteTable( + 'users', + { + id: int('id').primaryKey(), + firstName: text('first_name', { length: 255 }), + lastName: text('last_name', { length: 255 }), + email: text('email').notNull(), + fullName: text('full_name').generatedAlwaysAs(sql`concat_ws(first_name, ' ', last_name)`), + upperName: text('upper_name').generatedAlwaysAs( + sql` case when first_name is null then null else upper(first_name) end `, + ).$type(), // There is no way for drizzle to detect nullability in these cases. This is how the user can work around it + }, +); +{ + type User = typeof users.$inferSelect; + type NewUser = typeof users.$inferInsert; + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }, + User + > + >(); + + Expect< + Equal< + { + email: string; + id?: number | undefined; + firstName?: string | null | undefined; + lastName?: string | null | undefined; + }, + NewUser + > + >(); +} + +{ + type User = InferSelectModel; + type NewUser = InferInsertModel; + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }, + User + > + >(); + + Expect< + Equal< + { + email: string; + id?: number | undefined; + firstName?: string | null | undefined; + lastName?: string | null | undefined; + }, + NewUser + > + >(); +} + +{ + const dbUsers = await db.select().from(users); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }[], + typeof dbUsers + > + >(); +} + +{ + const db = drizzle({} as any, { schema: { users } }); + + const dbUser = await db.query.users.findFirst(); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + } | undefined, + typeof dbUser + > + >(); +} + +{ + const db = drizzle({} as any, { schema: { users } }); + + const dbUser = await db.query.users.findMany(); + + Expect< + Equal< + { + id: number; + firstName: string | null; + lastName: string | null; + email: string; + fullName: string; + upperName: string | null; + }[], + typeof dbUser + > + >(); +} + +{ + // @ts-expect-error - Can't use the fullName because it's a generated column + await db.insert(users).values({ + firstName: 'test', + lastName: 'test', + email: 'test', + fullName: 'test', + }); +} + +{ + await db.update(users).set({ + firstName: 'test', + lastName: 'test', + email: 'test', + // @ts-expect-error - Can't use the fullName because it's a generated column + fullName: 'test', + }); +} diff --git a/drizzle-orm/type-tests/sqlite/tables.ts b/drizzle-orm/type-tests/sqlite/tables.ts index ac01719f3..bc0a07736 100644 --- a/drizzle-orm/type-tests/sqlite/tables.ts +++ b/drizzle-orm/type-tests/sqlite/tables.ts @@ -416,3 +416,29 @@ Expect< }, typeof table.$inferSelect> >; } + +{ + const test = sqliteTable('test', { + test1: text('test', { length: 255, enum: ['a', 'b', 'c'] as const }).notNull(), + test2: text('test', { length: 255, enum: ['a', 'b', 'c'] }).notNull(), + test3: text('test', { enum: ['a', 'b', 'c'] as const }).notNull(), + test4: text('test', { enum: ['a', 'b', 'c'] }).notNull(), + }); + Expect>; + Expect>; + Expect>; + Expect>; +} + +{ // All types with generated columns + const test = sqliteTable('test', { + test1: text('test', { length: 255, enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test2: text('test', { length: 255, enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + test3: text('test', { enum: ['a', 'b', 'c'] as const }).generatedAlwaysAs(sql``), + test4: text('test', { enum: ['a', 'b', 'c'] }).generatedAlwaysAs(sql``), + }); + Expect>; + Expect>; + Expect>; + Expect>; +} diff --git a/drizzle-orm/type-tests/sqlite/with.ts b/drizzle-orm/type-tests/sqlite/with.ts index 94a969dac..983c96b85 100644 --- a/drizzle-orm/type-tests/sqlite/with.ts +++ b/drizzle-orm/type-tests/sqlite/with.ts @@ -11,6 +11,7 @@ const orders = sqliteTable('orders', { product: text('product').notNull(), amount: integer('amount').notNull(), quantity: integer('quantity').notNull(), + generated: text('generatedText').generatedAlwaysAs(sql``), }); { @@ -63,4 +64,18 @@ const orders = sqliteTable('orders', { productSales: number; }[], typeof result> >; + + const allOrdersWith = db.$with('all_orders_with').as(db.select().from(orders)); + const allFromWith = await db.with(allOrdersWith).select().from(allOrdersWith); + + Expect< + Equal<{ + id: number; + region: string; + product: string; + amount: number; + quantity: number; + generated: string; + }[], typeof allFromWith> + >; } From 7499703edbd2cfb4d8b6340ec1a335c0009c08ee Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 5 Nov 2023 21:26:12 -0500 Subject: [PATCH 07/40] [All] fixed insert that now bypasses the generated columns and added intergration tests --- drizzle-orm/src/column.ts | 5 ++ drizzle-orm/src/mysql-core/dialect.ts | 4 +- drizzle-orm/src/pg-core/dialect.ts | 6 +- drizzle-orm/src/sqlite-core/dialect.ts | 6 +- integration-tests/tests/libsql.test.ts | 100 ++++++++++++++++++++++- integration-tests/tests/mysql.test.ts | 96 ++++++++++++++++++++++ integration-tests/tests/pg-proxy.test.ts | 2 +- integration-tests/tests/pg.test.ts | 98 +++++++++++++++++++++- 8 files changed, 306 insertions(+), 11 deletions(-) diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index 3f3bcbf80..8265f06cb 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -105,6 +105,11 @@ export abstract class Column< mapToDriverValue(value: unknown): unknown { return value; } + + // ** @internal */ + isGenerated(): boolean { + return this.generated !== undefined; + } } export type UpdateColConfig< diff --git a/drizzle-orm/src/mysql-core/dialect.ts b/drizzle-orm/src/mysql-core/dialect.ts index 34d5bf907..126903056 100644 --- a/drizzle-orm/src/mysql-core/dialect.ts +++ b/drizzle-orm/src/mysql-core/dialect.ts @@ -18,7 +18,7 @@ import { Param, type QueryWithTypings, SQL, sql, type SQLChunk, View } from '~/s import { Subquery, SubqueryConfig } from '~/subquery.ts'; import { getTableName, Table } from '~/table.ts'; import { orderSelectedFields, type UpdateSet } from '~/utils.ts'; -import { DrizzleError, type Name, ViewBaseConfig, and, eq } from '../index.ts'; +import { and, DrizzleError, eq, type Name, ViewBaseConfig } from '../index.ts'; import { MySqlColumn } from './columns/common.ts'; import type { MySqlDeleteConfig } from './query-builders/delete.ts'; import type { MySqlInsertConfig } from './query-builders/insert.ts'; @@ -398,7 +398,7 @@ export class MySqlDialect { // const isSingleValue = values.length === 1; const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; - const colEntries: [string, MySqlColumn][] = Object.entries(columns); + const colEntries: [string, MySqlColumn][] = Object.entries(columns).filter(([_, col]) => !col.isGenerated()); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); diff --git a/drizzle-orm/src/pg-core/dialect.ts b/drizzle-orm/src/pg-core/dialect.ts index 366436e29..9010b0016 100644 --- a/drizzle-orm/src/pg-core/dialect.ts +++ b/drizzle-orm/src/pg-core/dialect.ts @@ -24,6 +24,7 @@ import { type TableRelationalConfig, type TablesRelationalConfig, } from '~/relations.ts'; +import { and, eq, View } from '~/sql/index.ts'; import { type DriverValueEncoder, type Name, @@ -39,9 +40,8 @@ import { getTableName, Table } from '~/table.ts'; import { orderSelectedFields, type UpdateSet } from '~/utils.ts'; import { ViewBaseConfig } from '~/view-common.ts'; import type { PgSession } from './session.ts'; -import type { PgMaterializedView } from './view.ts'; -import { View, and, eq } from '~/sql/index.ts'; import { PgViewBase } from './view-base.ts'; +import type { PgMaterializedView } from './view.ts'; export class PgDialect { static readonly [entityKind]: string = 'PgDialect'; @@ -426,7 +426,7 @@ export class PgDialect { const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; - const colEntries: [string, PgColumn][] = Object.entries(columns); + const colEntries: [string, PgColumn][] = Object.entries(columns).filter(([_, col]) => !col.isGenerated()); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); diff --git a/drizzle-orm/src/sqlite-core/dialect.ts b/drizzle-orm/src/sqlite-core/dialect.ts index d58ef419e..1e425ce2c 100644 --- a/drizzle-orm/src/sqlite-core/dialect.ts +++ b/drizzle-orm/src/sqlite-core/dialect.ts @@ -16,9 +16,9 @@ import { type TableRelationalConfig, type TablesRelationalConfig, } from '~/relations.ts'; +import type { Name } from '~/sql/index.ts'; +import { and, eq } from '~/sql/index.ts'; import { Param, type QueryWithTypings, SQL, sql, type SQLChunk } from '~/sql/sql.ts'; -import type { Name} from '~/sql/index.ts'; -import { and, eq } from '~/sql/index.ts' import { SQLiteColumn } from '~/sqlite-core/columns/index.ts'; import type { SQLiteDeleteConfig, SQLiteInsertConfig, SQLiteUpdateConfig } from '~/sqlite-core/query-builders/index.ts'; import { SQLiteTable } from '~/sqlite-core/table.ts'; @@ -365,7 +365,7 @@ export abstract class SQLiteDialect { const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; - const colEntries: [string, SQLiteColumn][] = Object.entries(columns); + const colEntries: [string, SQLiteColumn][] = Object.entries(columns).filter(([_, col]) => !col.isGenerated()); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); for (const [valueIndex, value] of values.entries()) { diff --git a/integration-tests/tests/libsql.test.ts b/integration-tests/tests/libsql.test.ts index b8e224e1b..11f5ca2a6 100644 --- a/integration-tests/tests/libsql.test.ts +++ b/integration-tests/tests/libsql.test.ts @@ -1348,7 +1348,7 @@ test.serial('insert null timestamp', async (t) => { t: integer('t', { mode: 'timestamp' }), }); - await db.run(sql`create table ${test} (t timestamp)`); + await db.run(sql`create table if not exists ${test} (t timestamp)`); await db.insert(test).values({ t: null }).run(); const res = await db.select().from(test).all(); @@ -2423,3 +2423,101 @@ test.serial('set operations (mixed all) as function with subquery', async (t) => ).orderBy(asc(sql`id`)); }); }); + +test.serial('select from a table with generated columns', async (t) => { + const { db } = t.context; + + const usersTable = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + firstName: text('first_name'), + lastName: text('last_name'), + fullName: text('full_name').generatedAlwaysAs(sql`first_name || ' ' || last_name`, { mode: 'virtual' }), + fullName2: text('full_name2').generatedAlwaysAs(sql`first_name || ' ' || last_name`, { mode: 'stored' }), + upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`, { mode: 'virtual' }), + }); + // const lkj = await db.get(sql`select * from ${usersTable}`); + // console.log(lkj); + + await db.run(sql`drop table if exists ${usersTable}`); + await db.run(sql` + create table ${usersTable} ( + id integer primary key autoincrement, + first_name text, + last_name text, + full_name text generated always as (first_name || ' ' || last_name) virtual, + full_name2 text generated always as (first_name || ' ' || last_name) stored, + upper text generated always as (upper(full_name)) virtual + ) + `); + + await db.insert(usersTable).values([ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Jane', lastName: 'Doe' }, + ]); + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + firstName: string | null; + lastName: string | null; + fullName: string; + fullName2: string; + upper: string; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, firstName: 'John', lastName: 'Doe', fullName: 'John Doe', fullName2: 'John Doe', upper: 'JOHN DOE' }, + { id: 2, firstName: 'Jane', lastName: 'Doe', fullName: 'Jane Doe', fullName2: 'Jane Doe', upper: 'JANE DOE' }, + ]); +}); + +test.serial('select from a table with generated columns with null', async (t) => { + const { db } = t.context; + + const usersTable = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + firstName: text('first_name'), + lastName: text('last_name'), + fullName: text('full_name').generatedAlwaysAs(sql`first_name || ' ' || last_name`, { mode: 'virtual' }).$type< + string | null + >(), + fullName2: text('full_name2').generatedAlwaysAs(sql`first_name || ' ' || last_name`, { mode: 'stored' }).$type< + string | null + >(), + upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`, { mode: 'virtual' }).$type(), + }); + + await db.run(sql`drop table if exists ${usersTable}`); + await db.run(sql` + create table ${usersTable} ( + id integer primary key autoincrement, + first_name text, + last_name text, + full_name text generated always as (first_name || ' ' || last_name) virtual, + full_name2 text generated always as (first_name || ' ' || last_name) stored, + upper text generated always as (upper(full_name)) virtual + ) + `); + + await db.insert(usersTable).values({}); + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + firstName: string | null; + lastName: string | null; + fullName: string | null; + fullName2: string | null; + upper: string | null; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, firstName: null, lastName: null, fullName: null, fullName2: null, upper: null }, + ]); +}); diff --git a/integration-tests/tests/mysql.test.ts b/integration-tests/tests/mysql.test.ts index 3b545fcd8..23ca9c02c 100644 --- a/integration-tests/tests/mysql.test.ts +++ b/integration-tests/tests/mysql.test.ts @@ -2654,3 +2654,99 @@ test.serial('set operations (mixed all) as function with subquery', async (t) => ); }); }); + +test.serial('select from a table with generated columns', async (t) => { + const { db } = t.context; + + const usersTable = mysqlTable('users', { + id: serial('id'), + firstName: text('first_name'), + lastName: text('last_name'), + fullName: text('full_name').generatedAlwaysAs(sql`concat(first_name, ' ', last_name)`, { mode: 'virtual' }), + fullName2: text('full_name2').generatedAlwaysAs(sql`concat(first_name, ' ', last_name)`, { mode: 'stored' }), + upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`, { mode: 'virtual' }), + }); + + await db.execute(sql`drop table if exists ${usersTable}`); + await db.execute(sql` + create table ${usersTable} ( + id serial, + first_name text, + last_name text, + full_name text generated always as (concat(first_name, ' ', last_name)) virtual, + full_name2 text generated always as (concat(first_name, ' ', last_name)) stored, + upper text generated always as (upper(full_name)) virtual + ) + `); + + await db.insert(usersTable).values([ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Jane', lastName: 'Doe' }, + ]); + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + firstName: string | null; + lastName: string | null; + fullName: string; + fullName2: string; + upper: string; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, firstName: 'John', lastName: 'Doe', fullName: 'John Doe', fullName2: 'John Doe', upper: 'JOHN DOE' }, + { id: 2, firstName: 'Jane', lastName: 'Doe', fullName: 'Jane Doe', fullName2: 'Jane Doe', upper: 'JANE DOE' }, + ]); +}); + +test.serial('select from a table with generated columns with null', async (t) => { + const { db } = t.context; + + const usersTable = mysqlTable('users', { + id: serial('id'), + firstName: text('first_name'), + lastName: text('last_name'), + fullName: text('full_name').generatedAlwaysAs(sql`concat(first_name, ' ', last_name)`, { mode: 'virtual' }).$type< + string | null + >(), + fullName2: text('full_name2').generatedAlwaysAs(sql`concat(first_name, ' ', last_name)`, { mode: 'stored' }).$type< + string | null + >(), + upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`, { mode: 'virtual' }).$type(), + }); + + await db.execute(sql`drop table if exists ${usersTable}`); + await db.execute(sql` + create table ${usersTable} ( + id serial, + first_name text, + last_name text, + full_name text generated always as (concat(first_name, ' ', last_name)) virtual, + full_name2 text generated always as (concat(first_name, ' ', last_name)) stored, + upper text generated always as (upper(full_name)) virtual + ) + `); + + await db.insert(usersTable).values({}); + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + firstName: string | null; + lastName: string | null; + fullName: string | null; + fullName2: string | null; + upper: string | null; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, firstName: null, lastName: null, fullName: null, fullName2: null, upper: null }, + ]); +}); diff --git a/integration-tests/tests/pg-proxy.test.ts b/integration-tests/tests/pg-proxy.test.ts index 02c48cffc..2c142d7d0 100644 --- a/integration-tests/tests/pg-proxy.test.ts +++ b/integration-tests/tests/pg-proxy.test.ts @@ -258,7 +258,7 @@ test.after.always(async (t) => { test.beforeEach(async (t) => { const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); + await ctx.db.execute(sql`drop schema if exists public cascade`); await ctx.db.execute(sql`create schema public`); await ctx.db.execute( sql` diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 38fd1a8a3..b70e55ec6 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -42,6 +42,7 @@ import { macaddr, macaddr8, type PgColumn, + pgEnum, pgMaterializedView, pgTable, pgTableCreator, @@ -56,7 +57,6 @@ import { uniqueKeyName, uuid as pgUuid, varchar, - pgEnum, } from 'drizzle-orm/pg-core'; import getPort from 'get-port'; import pg from 'pg'; @@ -3151,3 +3151,99 @@ test.serial('set operations (mixed all) as function', async (t) => { ).orderBy(asc(sql`id`)); }); }); + +test.serial('select from a table with generated columns', async (t) => { + const { db } = t.context; + + const usersTable = pgTable('users', { + id: serial('id'), + firstName: text('first_name'), + lastName: text('last_name'), + fullName: text('full_name').generatedAlwaysAs(sql`first_name || ' ' || last_name`), + upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`), + }); + + await db.execute(sql`drop table if exists ${usersTable}`); + await db.execute(sql` + create table ${usersTable} ( + id serial, + first_name text, + last_name text, + full_name text generated always as (CASE WHEN first_name IS NULL THEN last_name + WHEN last_name IS NULL THEN first_name + ELSE first_name || ' ' || last_name END) stored, + upper text generated always as (upper(CASE WHEN first_name IS NULL THEN last_name + WHEN last_name IS NULL THEN first_name + ELSE first_name || ' ' || last_name END)) stored + ) + `); + + await db.insert(usersTable).values([ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Jane', lastName: 'Doe' }, + ]); + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + firstName: string | null; + lastName: string | null; + fullName: string; + upper: string; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, firstName: 'John', lastName: 'Doe', fullName: 'John Doe', upper: 'JOHN DOE' }, + { id: 2, firstName: 'Jane', lastName: 'Doe', fullName: 'Jane Doe', upper: 'JANE DOE' }, + ]); +}); + +test.serial('select from a table with generated columns with null', async (t) => { + const { db } = t.context; + + const usersTable = pgTable('users', { + id: serial('id'), + firstName: text('first_name'), + lastName: text('last_name'), + fullName: text('full_name').generatedAlwaysAs(sql`first_name || ' ' || last_name`).$type< + string | null + >(), + upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`).$type(), + }); + + await db.execute(sql`drop table if exists ${usersTable}`); + await db.execute(sql` + create table ${usersTable} ( + id serial, + first_name text, + last_name text, + full_name text generated always as (CASE WHEN first_name IS NULL THEN last_name + WHEN last_name IS NULL THEN first_name + ELSE first_name || ' ' || last_name END) stored, + upper text generated always as (upper(CASE WHEN first_name IS NULL THEN last_name + WHEN last_name IS NULL THEN first_name + ELSE first_name || ' ' || last_name END)) stored + ) + `); + + await db.insert(usersTable).values({}); + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + firstName: string | null; + lastName: string | null; + fullName: string | null; + upper: string | null; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, firstName: null, lastName: null, fullName: null, upper: null }, + ]); +}); From 3cffb9823200b433c4e5f0105b69f1efb61bec6c Mon Sep 17 00:00:00 2001 From: Angelelz Date: Mon, 6 Nov 2023 01:40:36 -0500 Subject: [PATCH 08/40] [Pg] Completed implementation of generated as identity --- drizzle-orm/src/column-builder.ts | 13 ++++++- drizzle-orm/src/column.ts | 4 +- drizzle-orm/src/mysql-core/dialect.ts | 4 +- drizzle-orm/src/operations.ts | 5 ++- drizzle-orm/src/pg-core/columns/bigint.ts | 20 +++++++++- drizzle-orm/src/pg-core/columns/common.ts | 16 ++------ drizzle-orm/src/pg-core/columns/integer.ts | 20 +++++++++- drizzle-orm/src/pg-core/columns/smallint.ts | 20 +++++++++- drizzle-orm/src/pg-core/dialect.ts | 2 +- drizzle-orm/src/sqlite-core/dialect.ts | 4 +- .../type-tests/pg/generated-columns.ts | 34 ++++++++++++++++- integration-tests/tests/pg.test.ts | 37 +++++++++++++++++++ 12 files changed, 153 insertions(+), 26 deletions(-) diff --git a/drizzle-orm/src/column-builder.ts b/drizzle-orm/src/column-builder.ts index 3a56cfc67..6ad5357cb 100644 --- a/drizzle-orm/src/column-builder.ts +++ b/drizzle-orm/src/column-builder.ts @@ -55,7 +55,7 @@ export type MakeColumnConfig< enumValues: T['enumValues']; baseColumn: T extends { baseBuilder: infer U extends ColumnBuilderBase } ? BuildColumn : never; - generated: T['generated'] extends object ? GeneratedColumnConfig : undefined; + generated: T['generated'] extends object ? T['generated'] : undefined; } & {}; export type ColumnBuilderTypeConfig< @@ -123,6 +123,17 @@ export type HasGenerated = T & { + _: { + notNull: true; + hasDefault: true; + generated: { as: any; type: TType }; + }; +}; + export interface ColumnBuilderBase< T extends ColumnBuilderBaseConfig = ColumnBuilderBaseConfig, TTypeConfig extends object = object, diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index 8265f06cb..3dc20768b 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -107,8 +107,8 @@ export abstract class Column< } // ** @internal */ - isGenerated(): boolean { - return this.generated !== undefined; + shouldDisableInsert(): boolean { + return this.config.generated !== undefined && this.config.generated.type !== 'byDefault'; } } diff --git a/drizzle-orm/src/mysql-core/dialect.ts b/drizzle-orm/src/mysql-core/dialect.ts index 126903056..befb68bf7 100644 --- a/drizzle-orm/src/mysql-core/dialect.ts +++ b/drizzle-orm/src/mysql-core/dialect.ts @@ -398,7 +398,9 @@ export class MySqlDialect { // const isSingleValue = values.length === 1; const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; - const colEntries: [string, MySqlColumn][] = Object.entries(columns).filter(([_, col]) => !col.isGenerated()); + const colEntries: [string, MySqlColumn][] = Object.entries(columns).filter(([_, col]) => + !col.shouldDisableInsert() + ); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); diff --git a/drizzle-orm/src/operations.ts b/drizzle-orm/src/operations.ts index a79215836..492bb3f2a 100644 --- a/drizzle-orm/src/operations.ts +++ b/drizzle-orm/src/operations.ts @@ -16,7 +16,10 @@ export type NotGenerated = T extends AnyC export type OptionalKeyOnly< TKey extends string, T extends Column, -> = TKey extends RequiredKeyOnly ? never : TKey extends NotGenerated ? TKey : never; +> = TKey extends RequiredKeyOnly ? never + : TKey extends NotGenerated ? TKey + : T['_']['generated'] extends object ? T['_']['generated']['type'] extends 'byDefault' ? TKey : never + : never; export type SelectedFieldsFlat = Record< string, diff --git a/drizzle-orm/src/pg-core/columns/bigint.ts b/drizzle-orm/src/pg-core/columns/bigint.ts index fc3438dd5..f7cec269d 100644 --- a/drizzle-orm/src/pg-core/columns/bigint.ts +++ b/drizzle-orm/src/pg-core/columns/bigint.ts @@ -1,9 +1,15 @@ -import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts'; +import type { + ColumnBuilderBaseConfig, + ColumnBuilderRuntimeConfig, + IsIdentityByDefault, + MakeColumnConfig, +} from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; import type { AnyPgTable } from '~/pg-core/table.ts'; -import { PgColumn, PgColumnBuilder } from './common.ts'; +import { type SQL, sql } from '~/sql/sql.ts'; +import { PgColumn, PgColumnBuilder, type PgGeneratedColumnConfig } from './common.ts'; export type PgBigInt53BuilderInitial = PgBigInt53Builder<{ name: TName; @@ -22,6 +28,16 @@ export class PgBigInt53Builder( + config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }, + ): IsIdentityByDefault { + this.config.generated = { + as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, + type: config?.type ?? 'always', + }; + return this as any; + } + /** @internal */ override build( table: AnyPgTable<{ name: TTableName }>, diff --git a/drizzle-orm/src/pg-core/columns/common.ts b/drizzle-orm/src/pg-core/columns/common.ts index c11dfc81a..64dafc552 100644 --- a/drizzle-orm/src/pg-core/columns/common.ts +++ b/drizzle-orm/src/pg-core/columns/common.ts @@ -14,13 +14,13 @@ import { Column } from '~/column.ts'; import { entityKind, is } from '~/entity.ts'; import type { Update } from '~/utils.ts'; -import { type SQL, sql } from '~/index.ts'; +import type { SQL } from '~/index.ts'; import type { ForeignKey, UpdateDeleteAction } from '~/pg-core/foreign-keys.ts'; import { ForeignKeyBuilder } from '~/pg-core/foreign-keys.ts'; import type { AnyPgTable, PgTable } from '~/pg-core/table.ts'; +import { iife } from '~/tracing-utils.ts'; import { uniqueKeyName } from '../unique-constraint.ts'; import { makePgArray, parsePgArray } from '../utils/array.ts'; -import { iife } from '~/tracing-utils.ts'; export interface ReferenceConfig { ref: () => PgColumn; @@ -35,8 +35,8 @@ export interface PgColumnBuilderBase< TTypeConfig extends object = object, > extends ColumnBuilderBase {} -export interface PgGeneratedColumnConfig { - type?: 'always' | 'byDefault'; +export interface PgGeneratedColumnConfig { + type?: TType; } export abstract class PgColumnBuilder< @@ -95,14 +95,6 @@ export abstract class PgColumnBuilder< return this as any; } - generatedAsIdentity(config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }): HasGenerated { - this.config.generated = { - as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, - type: config?.type ?? 'always', - }; - return this as any; - } - /** @internal */ buildForeignKeys(column: PgColumn, table: PgTable): ForeignKey[] { return this.foreignKeyConfigs.map(({ ref, actions }) => { diff --git a/drizzle-orm/src/pg-core/columns/integer.ts b/drizzle-orm/src/pg-core/columns/integer.ts index e83369760..a3df5c81d 100644 --- a/drizzle-orm/src/pg-core/columns/integer.ts +++ b/drizzle-orm/src/pg-core/columns/integer.ts @@ -1,8 +1,14 @@ -import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts'; +import type { + ColumnBuilderBaseConfig, + ColumnBuilderRuntimeConfig, + IsIdentityByDefault, + MakeColumnConfig, +} from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; +import { type SQL, sql } from '~/sql/sql.ts'; import type { AnyPgTable } from '../table.ts'; -import { PgColumn, PgColumnBuilder } from './common.ts'; +import { PgColumn, PgColumnBuilder, type PgGeneratedColumnConfig } from './common.ts'; type PgIntegerBuilderInitial = PgIntegerBuilder<{ name: TName; @@ -21,6 +27,16 @@ export class PgIntegerBuilder( + config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }, + ): IsIdentityByDefault { + this.config.generated = { + as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, + type: config?.type ?? 'always', + }; + return this as any; + } + /** @internal */ override build( table: AnyPgTable<{ name: TTableName }>, diff --git a/drizzle-orm/src/pg-core/columns/smallint.ts b/drizzle-orm/src/pg-core/columns/smallint.ts index 2ae80b5d9..3cd29246e 100644 --- a/drizzle-orm/src/pg-core/columns/smallint.ts +++ b/drizzle-orm/src/pg-core/columns/smallint.ts @@ -1,8 +1,14 @@ -import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts'; +import type { + ColumnBuilderBaseConfig, + ColumnBuilderRuntimeConfig, + IsIdentityByDefault, + MakeColumnConfig, +} from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; import type { AnyPgTable } from '~/pg-core/table.ts'; -import { PgColumn, PgColumnBuilder } from './common.ts'; +import { type SQL, sql } from '~/sql/sql.ts'; +import { PgColumn, PgColumnBuilder, type PgGeneratedColumnConfig } from './common.ts'; export type PgSmallIntBuilderInitial = PgSmallIntBuilder<{ name: TName; @@ -21,6 +27,16 @@ export class PgSmallIntBuilder( + config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }, + ): IsIdentityByDefault { + this.config.generated = { + as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, + type: config?.type ?? 'always', + }; + return this as any; + } + /** @internal */ override build( table: AnyPgTable<{ name: TTableName }>, diff --git a/drizzle-orm/src/pg-core/dialect.ts b/drizzle-orm/src/pg-core/dialect.ts index 9010b0016..af5263006 100644 --- a/drizzle-orm/src/pg-core/dialect.ts +++ b/drizzle-orm/src/pg-core/dialect.ts @@ -426,7 +426,7 @@ export class PgDialect { const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; - const colEntries: [string, PgColumn][] = Object.entries(columns).filter(([_, col]) => !col.isGenerated()); + const colEntries: [string, PgColumn][] = Object.entries(columns).filter(([_, col]) => !col.shouldDisableInsert()); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); diff --git a/drizzle-orm/src/sqlite-core/dialect.ts b/drizzle-orm/src/sqlite-core/dialect.ts index 1e425ce2c..4781707f9 100644 --- a/drizzle-orm/src/sqlite-core/dialect.ts +++ b/drizzle-orm/src/sqlite-core/dialect.ts @@ -365,7 +365,9 @@ export abstract class SQLiteDialect { const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; - const colEntries: [string, SQLiteColumn][] = Object.entries(columns).filter(([_, col]) => !col.isGenerated()); + const colEntries: [string, SQLiteColumn][] = Object.entries(columns).filter(([_, col]) => + !col.shouldDisableInsert() + ); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); for (const [valueIndex, value] of values.entries()) { diff --git a/drizzle-orm/type-tests/pg/generated-columns.ts b/drizzle-orm/type-tests/pg/generated-columns.ts index 1b2e1b073..cad0203b1 100644 --- a/drizzle-orm/type-tests/pg/generated-columns.ts +++ b/drizzle-orm/type-tests/pg/generated-columns.ts @@ -1,7 +1,7 @@ import { type Equal, Expect } from 'type-tests/utils'; import { type InferInsertModel, type InferSelectModel, sql } from '~/index'; import { drizzle } from '~/node-postgres'; -import { pgTable, serial, text, varchar } from '~/pg-core'; +import { integer, pgTable, serial, text, varchar } from '~/pg-core'; import { db } from './db'; const users = pgTable( @@ -156,3 +156,35 @@ const users = pgTable( fullName: 'test', }); } + +const users2 = pgTable( + 'users', + { + id: integer('id').generatedAsIdentity({ type: 'byDefault' }), + id2: integer('id').generatedAsIdentity({ type: 'always' }), + }, +); + +{ + type User = typeof users2.$inferSelect; + type NewUser = typeof users2.$inferInsert; + + Expect< + Equal< + { + id: number; + id2: number; + }, + User + > + >(); + + Expect< + Equal< + { + id?: number | undefined; + }, + NewUser + > + >(); +} diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index b70e55ec6..2af8724ed 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -3247,3 +3247,40 @@ test.serial('select from a table with generated columns with null', async (t) => { id: 1, firstName: null, lastName: null, fullName: null, upper: null }, ]); }); + +test.serial('select from a table with generated columns as identity', async (t) => { + const { db } = t.context; + + const usersTable = pgTable('users', { + id: integer('id').generatedAsIdentity(), + id2: integer('id2').generatedAsIdentity({ type: 'byDefault' }), + id3: integer('id3').generatedAsIdentity({ type: 'always' }), + }); + + await db.execute(sql`drop table if exists ${usersTable}`); + await db.execute(sql` + create table ${usersTable} ( + id integer generated always as identity, + id2 integer generated by default as identity, + id3 integer generated always as identity + ) + `); + + const query = db.insert(usersTable).values({}); + console.log(query.toSQL()); + await query; + + const result = await db.select().from(usersTable); + + Expect< + Equal<{ + id: number; + id2: number; + id3: number; + }[], typeof result> + >; + + t.deepEqual(result, [ + { id: 1, id2: 1, id3: 1 }, + ]); +}); From 6061a63d3f5fb5ba6a872a289651557739188e89 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Mon, 6 Nov 2023 01:46:55 -0500 Subject: [PATCH 09/40] [Pg] Deleted config param not needed for generated always as --- drizzle-orm/src/pg-core/columns/common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/common.ts b/drizzle-orm/src/pg-core/columns/common.ts index 64dafc552..e1bc7833d 100644 --- a/drizzle-orm/src/pg-core/columns/common.ts +++ b/drizzle-orm/src/pg-core/columns/common.ts @@ -86,10 +86,10 @@ export abstract class PgColumnBuilder< return this; } - generatedAlwaysAs(as: SQL | T['data'], config?: PgGeneratedColumnConfig): HasGenerated { + generatedAlwaysAs(as: SQL | T['data']): HasGenerated { this.config.generated = { as, - type: config?.type ?? 'always', + type: 'always', mode: 'stored', }; return this as any; From 21b48e15275e8dfc48c4050e5f36536ddd6ddbe9 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sun, 19 Nov 2023 09:53:15 -0500 Subject: [PATCH 10/40] [All] Deleted old debug coments --- drizzle-orm/src/mysql-core/query-builders/update.ts | 1 - integration-tests/tests/libsql.test.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/drizzle-orm/src/mysql-core/query-builders/update.ts b/drizzle-orm/src/mysql-core/query-builders/update.ts index 48f68f9e5..913cb61ca 100644 --- a/drizzle-orm/src/mysql-core/query-builders/update.ts +++ b/drizzle-orm/src/mysql-core/query-builders/update.ts @@ -1,6 +1,5 @@ import type { GetColumnData } from '~/column.ts'; import { entityKind } from '~/entity.ts'; -// import type { NotGenerated } from '~/index.ts'; import type { MySqlDialect } from '~/mysql-core/dialect.ts'; import type { AnyQueryResultHKT, diff --git a/integration-tests/tests/libsql.test.ts b/integration-tests/tests/libsql.test.ts index 11f5ca2a6..4b09c1f91 100644 --- a/integration-tests/tests/libsql.test.ts +++ b/integration-tests/tests/libsql.test.ts @@ -2435,8 +2435,6 @@ test.serial('select from a table with generated columns', async (t) => { fullName2: text('full_name2').generatedAlwaysAs(sql`first_name || ' ' || last_name`, { mode: 'stored' }), upper: text('upper').generatedAlwaysAs(sql`upper(full_name)`, { mode: 'virtual' }), }); - // const lkj = await db.get(sql`select * from ${usersTable}`); - // console.log(lkj); await db.run(sql`drop table if exists ${usersTable}`); await db.run(sql` From dbb1fe038d5bd4efb74cf2554cacc5fa7fd2aa29 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Wed, 27 Mar 2024 12:16:10 +0200 Subject: [PATCH 11/40] Add pgSequence, move identity to common, fix type-tests TODO: add tests for pgSequence(getConfig) --- drizzle-orm/src/column-builder.ts | 9 +--- drizzle-orm/src/pg-core/columns/bigint.ts | 29 ++++------- drizzle-orm/src/pg-core/columns/common.ts | 6 +-- drizzle-orm/src/pg-core/columns/int.common.ts | 33 ++++++++++++ drizzle-orm/src/pg-core/columns/integer.ts | 25 +++------ drizzle-orm/src/pg-core/columns/smallint.ts | 25 +++------ drizzle-orm/src/pg-core/index.ts | 1 + drizzle-orm/src/pg-core/schema.ts | 5 ++ drizzle-orm/src/pg-core/sequence.ts | 43 +++++++++++++++ .../type-tests/pg/generated-columns.ts | 52 +++++++++++++++++-- 10 files changed, 153 insertions(+), 75 deletions(-) create mode 100644 drizzle-orm/src/pg-core/columns/int.common.ts create mode 100644 drizzle-orm/src/pg-core/sequence.ts diff --git a/drizzle-orm/src/column-builder.ts b/drizzle-orm/src/column-builder.ts index 6ad5357cb..2a8ce3385 100644 --- a/drizzle-orm/src/column-builder.ts +++ b/drizzle-orm/src/column-builder.ts @@ -24,7 +24,7 @@ export type GeneratedStorageMode = 'virtual' | 'stored'; export type GeneratedType = 'always' | 'byDefault'; export type GeneratedColumnConfig = { - as: TDataType | SQL; + as: TDataType | SQL | (() => SQL); type?: GeneratedType; mode?: GeneratedStorageMode; }; @@ -117,7 +117,6 @@ export type $Type = T & { export type HasGenerated = T & { _: { - notNull: true; hasDefault: true; generated: TGenerated; }; @@ -139,10 +138,6 @@ export interface ColumnBuilderBase< TTypeConfig extends object = object, > { _: ColumnBuilderTypeConfig; - generatedAlwaysAs( - as: SQL | T['data'], - config?: Partial>, - ): HasGenerated; } // To understand how to use `ColumnBuilder` and `AnyColumnBuilder`, see `Column` and `AnyColumn` documentation. @@ -243,7 +238,7 @@ export abstract class ColumnBuilder< } abstract generatedAlwaysAs( - as: SQL | T['data'], + as: SQL | T['data'] | (() => SQL), config?: Partial>, ): HasGenerated; } diff --git a/drizzle-orm/src/pg-core/columns/bigint.ts b/drizzle-orm/src/pg-core/columns/bigint.ts index f7cec269d..81f40d4e9 100644 --- a/drizzle-orm/src/pg-core/columns/bigint.ts +++ b/drizzle-orm/src/pg-core/columns/bigint.ts @@ -1,15 +1,10 @@ -import type { - ColumnBuilderBaseConfig, - ColumnBuilderRuntimeConfig, - IsIdentityByDefault, - MakeColumnConfig, -} from '~/column-builder.ts'; +import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; import type { AnyPgTable } from '~/pg-core/table.ts'; -import { type SQL, sql } from '~/sql/sql.ts'; -import { PgColumn, PgColumnBuilder, type PgGeneratedColumnConfig } from './common.ts'; +import { PgColumn } from './common.ts'; +import { PgIntColumnBaseBuilder } from './int.common.ts'; export type PgBigInt53BuilderInitial = PgBigInt53Builder<{ name: TName; @@ -21,23 +16,15 @@ export type PgBigInt53BuilderInitial = PgBigInt53Builder<{ generated: undefined; }>; -export class PgBigInt53Builder> extends PgColumnBuilder { +export class PgBigInt53Builder> + extends PgIntColumnBaseBuilder +{ static readonly [entityKind]: string = 'PgBigInt53Builder'; constructor(name: T['name']) { super(name, 'number', 'PgBigInt53'); } - generatedAsIdentity( - config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }, - ): IsIdentityByDefault { - this.config.generated = { - as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, - type: config?.type ?? 'always', - }; - return this as any; - } - /** @internal */ override build( table: AnyPgTable<{ name: TTableName }>, @@ -71,7 +58,9 @@ export type PgBigInt64BuilderInitial = PgBigInt64Builder<{ generated: undefined; }>; -export class PgBigInt64Builder> extends PgColumnBuilder { +export class PgBigInt64Builder> + extends PgIntColumnBaseBuilder +{ static readonly [entityKind]: string = 'PgBigInt64Builder'; constructor(name: T['name']) { diff --git a/drizzle-orm/src/pg-core/columns/common.ts b/drizzle-orm/src/pg-core/columns/common.ts index e1bc7833d..42f700306 100644 --- a/drizzle-orm/src/pg-core/columns/common.ts +++ b/drizzle-orm/src/pg-core/columns/common.ts @@ -35,10 +35,6 @@ export interface PgColumnBuilderBase< TTypeConfig extends object = object, > extends ColumnBuilderBase {} -export interface PgGeneratedColumnConfig { - type?: TType; -} - export abstract class PgColumnBuilder< T extends ColumnBuilderBaseConfig = ColumnBuilderBaseConfig, TRuntimeConfig extends object = object, @@ -86,7 +82,7 @@ export abstract class PgColumnBuilder< return this; } - generatedAlwaysAs(as: SQL | T['data']): HasGenerated { + generatedAlwaysAs(as: SQL | T['data'] | (() => SQL)): HasGenerated { this.config.generated = { as, type: 'always', diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts new file mode 100644 index 000000000..014a1e8c7 --- /dev/null +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -0,0 +1,33 @@ +import type { ColumnBuilderBaseConfig, ColumnDataType, IsIdentityByDefault } from '~/column-builder.ts'; +import { entityKind } from '~/entity.ts'; +import type { PgSequence } from '../sequence.ts'; +import { PgColumnBuilder } from './common.ts'; + +export abstract class PgIntColumnBaseBuilder< + T extends ColumnBuilderBaseConfig, +> extends PgColumnBuilder< + T, + { generatedIdentity: { sequence?: PgSequence; type: 'always' | 'byDefault' } } +> { + static readonly [entityKind]: string = 'PgIntColumnBaseBuilder'; + + generatedAlwaysAsIdentity( + sequence?: PgSequence, + ): IsIdentityByDefault { + this.config.generatedIdentity = { + type: 'always', + sequence, + }; + return this as any; + } + + generatedByDefaultAsIdentity( + sequence?: PgSequence, + ): IsIdentityByDefault { + this.config.generatedIdentity = { + type: 'byDefault', + sequence, + }; + return this as any; + } +} diff --git a/drizzle-orm/src/pg-core/columns/integer.ts b/drizzle-orm/src/pg-core/columns/integer.ts index a3df5c81d..2c35c1e29 100644 --- a/drizzle-orm/src/pg-core/columns/integer.ts +++ b/drizzle-orm/src/pg-core/columns/integer.ts @@ -1,14 +1,9 @@ -import type { - ColumnBuilderBaseConfig, - ColumnBuilderRuntimeConfig, - IsIdentityByDefault, - MakeColumnConfig, -} from '~/column-builder.ts'; +import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; -import { type SQL, sql } from '~/sql/sql.ts'; import type { AnyPgTable } from '../table.ts'; -import { PgColumn, PgColumnBuilder, type PgGeneratedColumnConfig } from './common.ts'; +import { PgColumn } from './common.ts'; +import { PgIntColumnBaseBuilder } from './int.common.ts'; type PgIntegerBuilderInitial = PgIntegerBuilder<{ name: TName; @@ -20,23 +15,15 @@ type PgIntegerBuilderInitial = PgIntegerBuilder<{ generated: undefined; }>; -export class PgIntegerBuilder> extends PgColumnBuilder { +export class PgIntegerBuilder> + extends PgIntColumnBaseBuilder +{ static readonly [entityKind]: string = 'PgIntegerBuilder'; constructor(name: T['name']) { super(name, 'number', 'PgInteger'); } - generatedAsIdentity( - config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }, - ): IsIdentityByDefault { - this.config.generated = { - as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, - type: config?.type ?? 'always', - }; - return this as any; - } - /** @internal */ override build( table: AnyPgTable<{ name: TTableName }>, diff --git a/drizzle-orm/src/pg-core/columns/smallint.ts b/drizzle-orm/src/pg-core/columns/smallint.ts index 3cd29246e..4a500bf5c 100644 --- a/drizzle-orm/src/pg-core/columns/smallint.ts +++ b/drizzle-orm/src/pg-core/columns/smallint.ts @@ -1,14 +1,9 @@ -import type { - ColumnBuilderBaseConfig, - ColumnBuilderRuntimeConfig, - IsIdentityByDefault, - MakeColumnConfig, -} from '~/column-builder.ts'; +import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; import { entityKind } from '~/entity.ts'; import type { AnyPgTable } from '~/pg-core/table.ts'; -import { type SQL, sql } from '~/sql/sql.ts'; -import { PgColumn, PgColumnBuilder, type PgGeneratedColumnConfig } from './common.ts'; +import { PgColumn } from './common.ts'; +import { PgIntColumnBaseBuilder } from './int.common.ts'; export type PgSmallIntBuilderInitial = PgSmallIntBuilder<{ name: TName; @@ -20,23 +15,15 @@ export type PgSmallIntBuilderInitial = PgSmallIntBuilder<{ generated: undefined; }>; -export class PgSmallIntBuilder> extends PgColumnBuilder { +export class PgSmallIntBuilder> + extends PgIntColumnBaseBuilder +{ static readonly [entityKind]: string = 'PgSmallIntBuilder'; constructor(name: T['name']) { super(name, 'number', 'PgSmallInt'); } - generatedAsIdentity( - config?: PgGeneratedColumnConfig & { sequenceOpts?: SQL }, - ): IsIdentityByDefault { - this.config.generated = { - as: sql`identity${config?.sequenceOpts ? ` ${config.sequenceOpts}` : ''}`, - type: config?.type ?? 'always', - }; - return this as any; - } - /** @internal */ override build( table: AnyPgTable<{ name: TTableName }>, diff --git a/drizzle-orm/src/pg-core/index.ts b/drizzle-orm/src/pg-core/index.ts index 1a80ff7ad..084633c4a 100644 --- a/drizzle-orm/src/pg-core/index.ts +++ b/drizzle-orm/src/pg-core/index.ts @@ -8,6 +8,7 @@ export * from './indexes.ts'; export * from './primary-keys.ts'; export * from './query-builders/index.ts'; export * from './schema.ts'; +export * from './sequence.ts'; export * from './session.ts'; export * from './subquery.ts'; export * from './table.ts'; diff --git a/drizzle-orm/src/pg-core/schema.ts b/drizzle-orm/src/pg-core/schema.ts index 35f674729..a588e3e7a 100644 --- a/drizzle-orm/src/pg-core/schema.ts +++ b/drizzle-orm/src/pg-core/schema.ts @@ -1,6 +1,7 @@ import { entityKind, is } from '~/entity.ts'; import type { pgEnum } from './columns/enum.ts'; import { pgEnumWithSchema } from './columns/enum.ts'; +import { type pgSequence, pgSequenceWithSchema } from './sequence.ts'; import { type PgTableFn, pgTableWithSchema } from './table.ts'; import { type pgMaterializedView, pgMaterializedViewWithSchema, type pgView, pgViewWithSchema } from './view.ts'; @@ -25,6 +26,10 @@ export class PgSchema { enum: typeof pgEnum = ((name, values) => { return pgEnumWithSchema(name, values, this.schemaName); }); + + sequence: typeof pgSequence = ((name, options) => { + return pgSequenceWithSchema(name, options, this.schemaName); + }); } export function isPgSchema(obj: unknown): obj is PgSchema { diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts new file mode 100644 index 000000000..db8161e01 --- /dev/null +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -0,0 +1,43 @@ +export type PgSequenceOptions = { + increment?: number; + minValue?: number; + maxValue?: number; + startWith?: number; + cache?: number; + cycle?: boolean; +}; + +const isPgSeqSym = Symbol.for('drizzle:isPgSequence'); + +export interface PgSequence { + readonly seqName: string; + readonly seqOptions: PgSequenceOptions; + readonly schema: string | undefined; + /** @internal */ + [isPgSeqSym]: true; +} + +export function pgSequence( + name: string, + options: PgSequenceOptions, +): PgSequence { + return pgSequenceWithSchema(name, options, undefined); +} + +/** @internal */ +export function pgSequenceWithSchema( + name: string, + options: PgSequenceOptions, + schema?: string, +): PgSequence { + const sequenceInstance: PgSequence = Object.assign( + { + name, + seqOptions: options, + schema, + [isPgSeqSym]: true, + } as const, + ); + + return sequenceInstance; +} diff --git a/drizzle-orm/type-tests/pg/generated-columns.ts b/drizzle-orm/type-tests/pg/generated-columns.ts index cad0203b1..6f3213e29 100644 --- a/drizzle-orm/type-tests/pg/generated-columns.ts +++ b/drizzle-orm/type-tests/pg/generated-columns.ts @@ -1,7 +1,7 @@ import { type Equal, Expect } from 'type-tests/utils'; import { type InferInsertModel, type InferSelectModel, sql } from '~/index'; import { drizzle } from '~/node-postgres'; -import { integer, pgTable, serial, text, varchar } from '~/pg-core'; +import { integer, pgSchema, pgSequence, pgTable, serial, text, varchar } from '~/pg-core'; import { db } from './db'; const users = pgTable( @@ -11,10 +11,10 @@ const users = pgTable( firstName: varchar('first_name', { length: 255 }), lastName: varchar('last_name', { length: 255 }), email: text('email').notNull(), - fullName: text('full_name').generatedAlwaysAs(sql`concat_ws(first_name, ' ', last_name)`), + fullName: text('full_name').generatedAlwaysAs(sql`concat_ws(first_name, ' ', last_name)`).notNull(), upperName: text('upper_name').generatedAlwaysAs( sql` case when first_name is null then null else upper(first_name) end `, - ).$type(), // There is no way for drizzle to detect nullability in these cases. This is how the user can work around it + ), }, ); { @@ -160,8 +160,8 @@ const users = pgTable( const users2 = pgTable( 'users', { - id: integer('id').generatedAsIdentity({ type: 'byDefault' }), - id2: integer('id').generatedAsIdentity({ type: 'always' }), + id: integer('id').generatedByDefaultAsIdentity(), + id2: integer('id').generatedAlwaysAsIdentity(), }, ); @@ -188,3 +188,45 @@ const users2 = pgTable( > >(); } + +const customSequence = pgSequence('custom_seq', { + minValue: 100000, + increment: 1, +}); + +const customSequenceSchema = pgSchema('test').sequence('custom_seq', { + minValue: 100000, + increment: 1, +}); + +const usersSeq = pgTable( + 'users', + { + id: integer('id').generatedByDefaultAsIdentity(customSequence), + id2: integer('id').generatedAlwaysAsIdentity(customSequenceSchema), + }, +); + +{ + type User = typeof usersSeq.$inferSelect; + type NewUser = typeof usersSeq.$inferInsert; + + Expect< + Equal< + { + id: number; + id2: number; + }, + User + > + >(); + + Expect< + Equal< + { + id?: number | undefined; + }, + NewUser + > + >(); +} From 43a5ab9bae07a8989f6bc0accc6288b2a0168f09 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 28 Mar 2024 16:36:46 +0200 Subject: [PATCH 12/40] Move to class for using is with sequences --- drizzle-orm/src/pg-core/columns/int.common.ts | 52 ++++++++++++++----- drizzle-orm/src/pg-core/sequence.ts | 13 +++-- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index 014a1e8c7..e47858409 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -1,33 +1,59 @@ import type { ColumnBuilderBaseConfig, ColumnDataType, IsIdentityByDefault } from '~/column-builder.ts'; -import { entityKind } from '~/entity.ts'; -import type { PgSequence } from '../sequence.ts'; +import { entityKind, is } from '~/entity.ts'; +import { PgSequence, type PgSequenceOptions } from '../sequence.ts'; import { PgColumnBuilder } from './common.ts'; export abstract class PgIntColumnBaseBuilder< T extends ColumnBuilderBaseConfig, > extends PgColumnBuilder< T, - { generatedIdentity: { sequence?: PgSequence; type: 'always' | 'byDefault' } } + { generatedIdentity: { sequenceName?: string; sequenceOptions?: PgSequenceOptions; type: 'always' | 'byDefault' } } > { static readonly [entityKind]: string = 'PgIntColumnBaseBuilder'; generatedAlwaysAsIdentity( - sequence?: PgSequence, + sequence?: PgSequenceOptions & { name?: string } | PgSequence, ): IsIdentityByDefault { - this.config.generatedIdentity = { - type: 'always', - sequence, - }; + if (sequence) { + if (is(sequence, PgSequence)) { + this.config.generatedIdentity = { + type: 'always', + sequenceName: sequence.seqName, + sequenceOptions: sequence.seqOptions, + }; + } else { + const { name, ...options } = sequence; + this.config.generatedIdentity = { + type: 'always', + sequenceName: name, + sequenceOptions: options, + }; + } + } + return this as any; } generatedByDefaultAsIdentity( - sequence?: PgSequence, + sequence?: PgSequenceOptions & { name?: string } | PgSequence, ): IsIdentityByDefault { - this.config.generatedIdentity = { - type: 'byDefault', - sequence, - }; + if (sequence) { + if (is(sequence, PgSequence)) { + this.config.generatedIdentity = { + type: 'byDefault', + sequenceName: sequence.seqName, + sequenceOptions: sequence.seqOptions, + }; + } else { + const { name, ...options } = sequence; + this.config.generatedIdentity = { + type: 'byDefault', + sequenceName: name, + sequenceOptions: options, + }; + } + } + return this as any; } } diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index db8161e01..2e12a995c 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -1,3 +1,5 @@ +import { entityKind } from '~/entity'; + export type PgSequenceOptions = { increment?: number; minValue?: number; @@ -7,14 +9,12 @@ export type PgSequenceOptions = { cycle?: boolean; }; -const isPgSeqSym = Symbol.for('drizzle:isPgSequence'); +export class PgSequence { + static readonly [entityKind]: string = 'PgSequence'; -export interface PgSequence { - readonly seqName: string; - readonly seqOptions: PgSequenceOptions; + readonly seqName: string | undefined; + readonly seqOptions: PgSequenceOptions | undefined; readonly schema: string | undefined; - /** @internal */ - [isPgSeqSym]: true; } export function pgSequence( @@ -35,7 +35,6 @@ export function pgSequenceWithSchema( name, seqOptions: options, schema, - [isPgSeqSym]: true, } as const, ); From 23c870ca6c117732cb4f871fcfa9d31997a3be75 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 28 Mar 2024 16:44:40 +0200 Subject: [PATCH 13/40] Fix test setups for new API --- drizzle-orm/type-tests/mysql/generated-columns.ts | 10 +++++----- drizzle-orm/type-tests/mysql/with.ts | 2 +- drizzle-orm/type-tests/pg/with.ts | 2 +- drizzle-orm/type-tests/sqlite/generated-columns.ts | 10 +++++----- drizzle-orm/type-tests/sqlite/with.ts | 2 +- integration-tests/tests/libsql.test.ts | 6 +++--- integration-tests/tests/mysql.test.ts | 6 +++--- integration-tests/tests/pg.test.ts | 10 +++++----- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drizzle-orm/type-tests/mysql/generated-columns.ts b/drizzle-orm/type-tests/mysql/generated-columns.ts index ed9c36349..d045fe1b3 100644 --- a/drizzle-orm/type-tests/mysql/generated-columns.ts +++ b/drizzle-orm/type-tests/mysql/generated-columns.ts @@ -28,7 +28,7 @@ const users = mysqlTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }, User @@ -59,7 +59,7 @@ const users = mysqlTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }, User @@ -89,7 +89,7 @@ const users = mysqlTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }[], typeof dbUsers @@ -109,7 +109,7 @@ const users = mysqlTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; } | undefined, typeof dbUser @@ -129,7 +129,7 @@ const users = mysqlTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }[], typeof dbUser diff --git a/drizzle-orm/type-tests/mysql/with.ts b/drizzle-orm/type-tests/mysql/with.ts index 4da75ef45..b4e528191 100644 --- a/drizzle-orm/type-tests/mysql/with.ts +++ b/drizzle-orm/type-tests/mysql/with.ts @@ -74,7 +74,7 @@ const orders = mysqlTable('orders', { product: string; amount: number; quantity: number; - generated: string; + generated: string | null; }[], typeof allFromWith> >; } diff --git a/drizzle-orm/type-tests/pg/with.ts b/drizzle-orm/type-tests/pg/with.ts index a9ceac31b..d5fcc96ed 100644 --- a/drizzle-orm/type-tests/pg/with.ts +++ b/drizzle-orm/type-tests/pg/with.ts @@ -74,7 +74,7 @@ const orders = pgTable('orders', { product: string; amount: number; quantity: number; - generated: string; + generated: string | null; }[], typeof allFromWith> >; } diff --git a/drizzle-orm/type-tests/sqlite/generated-columns.ts b/drizzle-orm/type-tests/sqlite/generated-columns.ts index 6c9fc54cd..57ffea989 100644 --- a/drizzle-orm/type-tests/sqlite/generated-columns.ts +++ b/drizzle-orm/type-tests/sqlite/generated-columns.ts @@ -28,7 +28,7 @@ const users = sqliteTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }, User @@ -59,7 +59,7 @@ const users = sqliteTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }, User @@ -89,7 +89,7 @@ const users = sqliteTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }[], typeof dbUsers @@ -109,7 +109,7 @@ const users = sqliteTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; } | undefined, typeof dbUser @@ -129,7 +129,7 @@ const users = sqliteTable( firstName: string | null; lastName: string | null; email: string; - fullName: string; + fullName: string | null; upperName: string | null; }[], typeof dbUser diff --git a/drizzle-orm/type-tests/sqlite/with.ts b/drizzle-orm/type-tests/sqlite/with.ts index 983c96b85..8b5963eb6 100644 --- a/drizzle-orm/type-tests/sqlite/with.ts +++ b/drizzle-orm/type-tests/sqlite/with.ts @@ -75,7 +75,7 @@ const orders = sqliteTable('orders', { product: string; amount: number; quantity: number; - generated: string; + generated: string | null; }[], typeof allFromWith> >; } diff --git a/integration-tests/tests/libsql.test.ts b/integration-tests/tests/libsql.test.ts index d0b545945..55197fff4 100644 --- a/integration-tests/tests/libsql.test.ts +++ b/integration-tests/tests/libsql.test.ts @@ -2670,9 +2670,9 @@ test.serial('select from a table with generated columns', async (t) => { id: number; firstName: string | null; lastName: string | null; - fullName: string; - fullName2: string; - upper: string; + fullName: string | null; + fullName2: string | null; + upper: string | null; }[], typeof result> >; diff --git a/integration-tests/tests/mysql.test.ts b/integration-tests/tests/mysql.test.ts index 4670052a1..d9b89a450 100644 --- a/integration-tests/tests/mysql.test.ts +++ b/integration-tests/tests/mysql.test.ts @@ -2869,9 +2869,9 @@ test.serial('select from a table with generated columns', async (t) => { id: number; firstName: string | null; lastName: string | null; - fullName: string; - fullName2: string; - upper: string; + fullName: string | null; + fullName2: string | null; + upper: string | null; }[], typeof result> >; diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 60491d3a7..a0b3bf19c 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -3958,8 +3958,8 @@ test.serial('select from a table with generated columns', async (t) => { id: number; firstName: string | null; lastName: string | null; - fullName: string; - upper: string; + fullName: string | null; + upper: string | null; }[], typeof result> >; @@ -4020,9 +4020,9 @@ test.serial('select from a table with generated columns as identity', async (t) const { db } = t.context; const usersTable = pgTable('users', { - id: integer('id').generatedAsIdentity(), - id2: integer('id2').generatedAsIdentity({ type: 'byDefault' }), - id3: integer('id3').generatedAsIdentity({ type: 'always' }), + id: integer('id').generatedAlwaysAsIdentity(), + id2: integer('id2').generatedByDefaultAsIdentity(), + id3: integer('id3').generatedAlwaysAsIdentity(), }); await db.execute(sql`drop table if exists ${usersTable}`); From 25f8d883070d109926c54bbccb0cad0ec4bf9034 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 28 Mar 2024 16:55:49 +0200 Subject: [PATCH 14/40] Fix import --- drizzle-orm/src/pg-core/sequence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index 2e12a995c..c251d176c 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -1,4 +1,4 @@ -import { entityKind } from '~/entity'; +import { entityKind } from '~/entity.ts'; export type PgSequenceOptions = { increment?: number; From 18854f0d8cb1e90c23c08833667b126fdd1f6a3d Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Mon, 1 Apr 2024 14:27:39 +0300 Subject: [PATCH 15/40] expose generated identity --- drizzle-orm/src/column-builder.ts | 9 ++++++++- drizzle-orm/src/column.ts | 3 +++ drizzle-orm/src/pg-core/columns/int.common.ts | 9 +++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drizzle-orm/src/column-builder.ts b/drizzle-orm/src/column-builder.ts index 5bb7204cf..75aea3eb6 100644 --- a/drizzle-orm/src/column-builder.ts +++ b/drizzle-orm/src/column-builder.ts @@ -1,7 +1,7 @@ import { entityKind } from '~/entity.ts'; import type { Column } from './column.ts'; import type { MySqlColumn } from './mysql-core/index.ts'; -import type { PgColumn } from './pg-core/index.ts'; +import type { PgColumn, PgSequenceOptions } from './pg-core/index.ts'; import type { SQL } from './sql/sql.ts'; import type { SQLiteColumn } from './sqlite-core/index.ts'; import type { Simplify } from './utils.ts'; @@ -29,6 +29,12 @@ export type GeneratedColumnConfig = { mode?: GeneratedStorageMode; }; +export type GeneratedIdentityConfig = { + sequenceName?: string; + sequenceOptions?: PgSequenceOptions; + type: 'always' | 'byDefault'; +}; + export interface ColumnBuilderBaseConfig { name: string; dataType: TDataType; @@ -92,6 +98,7 @@ export type ColumnBuilderRuntimeConfig | undefined; + generatedIdentity: GeneratedIdentityConfig | undefined; } & TRuntimeConfig; export interface ColumnBuilderExtraConfig { diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index 8533c28f9..1f9c9e5c1 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -3,6 +3,7 @@ import type { ColumnBuilderRuntimeConfig, ColumnDataType, GeneratedColumnConfig, + GeneratedIdentityConfig, } from './column-builder.ts'; import { entityKind } from './entity.ts'; import type { DriverValueMapper, SQL, SQLWrapper } from './sql/sql.ts'; @@ -75,6 +76,7 @@ export abstract class Column< readonly columnType: T['columnType']; readonly enumValues: T['enumValues'] = undefined; readonly generated: GeneratedColumnConfig | undefined = undefined; + readonly generatedIdentity: GeneratedIdentityConfig | undefined = undefined; protected config: ColumnRuntimeConfig; @@ -96,6 +98,7 @@ export abstract class Column< this.dataType = config.dataType as T['dataType']; this.columnType = config.columnType; this.generated = config.generated; + this.generatedIdentity = config.generatedIdentity; } abstract getSQLType(): string; diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index e47858409..9ab7f482e 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -1,4 +1,9 @@ -import type { ColumnBuilderBaseConfig, ColumnDataType, IsIdentityByDefault } from '~/column-builder.ts'; +import type { + ColumnBuilderBaseConfig, + ColumnDataType, + GeneratedIdentityConfig, + IsIdentityByDefault, +} from '~/column-builder.ts'; import { entityKind, is } from '~/entity.ts'; import { PgSequence, type PgSequenceOptions } from '../sequence.ts'; import { PgColumnBuilder } from './common.ts'; @@ -7,7 +12,7 @@ export abstract class PgIntColumnBaseBuilder< T extends ColumnBuilderBaseConfig, > extends PgColumnBuilder< T, - { generatedIdentity: { sequenceName?: string; sequenceOptions?: PgSequenceOptions; type: 'always' | 'byDefault' } } + { generatedIdentity: GeneratedIdentityConfig } > { static readonly [entityKind]: string = 'PgIntColumnBaseBuilder'; From 5f598e286666d1af38d837a154d57dfb4740478e Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Wed, 3 Apr 2024 17:21:03 +0300 Subject: [PATCH 16/40] Fix shouldDisableInsert part --- drizzle-orm/src/column.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index 1f9c9e5c1..1dfe21793 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -113,7 +113,7 @@ export abstract class Column< // ** @internal */ shouldDisableInsert(): boolean { - return this.config.generated !== undefined && this.config.generated.type !== 'byDefault'; + return this.config.generatedIdentity !== undefined && this.config.generatedIdentity.type !== 'byDefault'; } } From 0d48b649607881de898e7c23515a11690404f855 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Fri, 7 Jun 2024 13:18:22 +0300 Subject: [PATCH 17/40] Fixed new typed configs --- drizzle-orm/src/pg-core/columns/line.ts | 2 ++ drizzle-orm/src/pg-core/columns/point.ts | 2 ++ drizzle-orm/src/pg-core/columns/postgis_extension/geometry.ts | 2 ++ drizzle-orm/src/pg-core/columns/vector_extension/bit.ts | 1 + drizzle-orm/src/pg-core/columns/vector_extension/halfvec.ts | 1 + drizzle-orm/src/pg-core/columns/vector_extension/sparsevec.ts | 1 + drizzle-orm/src/pg-core/columns/vector_extension/vector.ts | 1 + 7 files changed, 10 insertions(+) diff --git a/drizzle-orm/src/pg-core/columns/line.ts b/drizzle-orm/src/pg-core/columns/line.ts index 8ff705481..bf4e653ad 100644 --- a/drizzle-orm/src/pg-core/columns/line.ts +++ b/drizzle-orm/src/pg-core/columns/line.ts @@ -13,6 +13,7 @@ export type PgLineBuilderInitial = PgLineBuilder<{ data: [number, number, number]; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class PgLineBuilder> extends PgColumnBuilder { @@ -57,6 +58,7 @@ export type PgLineABCBuilderInitial = PgLineABCBuilder<{ data: { a: number; b: number; c: number }; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgLineABCBuilder> extends PgColumnBuilder { diff --git a/drizzle-orm/src/pg-core/columns/point.ts b/drizzle-orm/src/pg-core/columns/point.ts index 1e3ae1098..7bff25e55 100644 --- a/drizzle-orm/src/pg-core/columns/point.ts +++ b/drizzle-orm/src/pg-core/columns/point.ts @@ -13,6 +13,7 @@ export type PgPointTupleBuilderInitial = PgPointTupleBuild data: [number, number]; driverParam: number | string; enumValues: undefined; + generated: undefined; }>; export class PgPointTupleBuilder> @@ -62,6 +63,7 @@ export type PgPointObjectBuilderInitial = PgPointObjectBui data: { x: number; y: number }; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgPointObjectBuilder> diff --git a/drizzle-orm/src/pg-core/columns/postgis_extension/geometry.ts b/drizzle-orm/src/pg-core/columns/postgis_extension/geometry.ts index ef84e20b5..5dc2b8955 100644 --- a/drizzle-orm/src/pg-core/columns/postgis_extension/geometry.ts +++ b/drizzle-orm/src/pg-core/columns/postgis_extension/geometry.ts @@ -14,6 +14,7 @@ export type PgGeometryBuilderInitial = PgGeometryBuilder<{ data: [number, number]; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgGeometryBuilder> extends PgColumnBuilder { @@ -57,6 +58,7 @@ export type PgGeometryObjectBuilderInitial = PgGeometryObj data: { x: number; y: number }; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgGeometryObjectBuilder> diff --git a/drizzle-orm/src/pg-core/columns/vector_extension/bit.ts b/drizzle-orm/src/pg-core/columns/vector_extension/bit.ts index a0e23188e..95d60e7d8 100644 --- a/drizzle-orm/src/pg-core/columns/vector_extension/bit.ts +++ b/drizzle-orm/src/pg-core/columns/vector_extension/bit.ts @@ -11,6 +11,7 @@ export type PgBinaryVectorBuilderInitial = PgBinaryVectorB data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgBinaryVectorBuilder> diff --git a/drizzle-orm/src/pg-core/columns/vector_extension/halfvec.ts b/drizzle-orm/src/pg-core/columns/vector_extension/halfvec.ts index 8278f2b69..182beda7e 100644 --- a/drizzle-orm/src/pg-core/columns/vector_extension/halfvec.ts +++ b/drizzle-orm/src/pg-core/columns/vector_extension/halfvec.ts @@ -11,6 +11,7 @@ export type PgHalfVectorBuilderInitial = PgHalfVectorBuild data: number[]; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgHalfVectorBuilder> extends PgColumnBuilder< diff --git a/drizzle-orm/src/pg-core/columns/vector_extension/sparsevec.ts b/drizzle-orm/src/pg-core/columns/vector_extension/sparsevec.ts index af98517c1..060003bc6 100644 --- a/drizzle-orm/src/pg-core/columns/vector_extension/sparsevec.ts +++ b/drizzle-orm/src/pg-core/columns/vector_extension/sparsevec.ts @@ -11,6 +11,7 @@ export type PgSparseVectorBuilderInitial = PgSparseVectorB data: string; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgSparseVectorBuilder> diff --git a/drizzle-orm/src/pg-core/columns/vector_extension/vector.ts b/drizzle-orm/src/pg-core/columns/vector_extension/vector.ts index a551d36e6..c7099b5dc 100644 --- a/drizzle-orm/src/pg-core/columns/vector_extension/vector.ts +++ b/drizzle-orm/src/pg-core/columns/vector_extension/vector.ts @@ -11,6 +11,7 @@ export type PgVectorBuilderInitial = PgVectorBuilder<{ data: number[]; driverParam: string; enumValues: undefined; + generated: undefined; }>; export class PgVectorBuilder> extends PgColumnBuilder< From 3739dfc2d776080fe666e80930e8caa9d0012342 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 11 Jun 2024 17:36:55 +0300 Subject: [PATCH 18/40] Add isPgSequence --- drizzle-orm/src/pg-core/sequence.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index c251d176c..d6b850c4e 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -1,5 +1,3 @@ -import { entityKind } from '~/entity.ts'; - export type PgSequenceOptions = { increment?: number; minValue?: number; @@ -9,12 +7,14 @@ export type PgSequenceOptions = { cycle?: boolean; }; -export class PgSequence { - static readonly [entityKind]: string = 'PgSequence'; - +const isPgSequenceSym = Symbol.for('drizzle:isPgSequence'); +export interface PgSequence { readonly seqName: string | undefined; readonly seqOptions: PgSequenceOptions | undefined; readonly schema: string | undefined; + + /** @internal */ + [isPgSequenceSym]: true; } export function pgSequence( @@ -40,3 +40,7 @@ export function pgSequenceWithSchema( return sequenceInstance; } + +export function isPgSequence(obj: unknown): obj is PgSequence { + return !!obj && typeof obj === 'function' && isPgSequenceSym in obj && obj[isPgSequenceSym] === true; +} From 857ba5491118cd680e715ba89ab623d04566e8f0 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 11 Jun 2024 17:46:15 +0300 Subject: [PATCH 19/40] Update int.common types --- drizzle-orm/src/pg-core/columns/int.common.ts | 9 +- pnpm-lock.yaml | 664 +++++++----------- 2 files changed, 259 insertions(+), 414 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index 9ab7f482e..a23f5d6a4 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -4,8 +4,9 @@ import type { GeneratedIdentityConfig, IsIdentityByDefault, } from '~/column-builder.ts'; -import { entityKind, is } from '~/entity.ts'; -import { PgSequence, type PgSequenceOptions } from '../sequence.ts'; +import { entityKind } from '~/entity.ts'; +import { isPgSequence } from '../sequence.ts'; +import type { PgSequence, PgSequenceOptions } from '../sequence.ts'; import { PgColumnBuilder } from './common.ts'; export abstract class PgIntColumnBaseBuilder< @@ -20,7 +21,7 @@ export abstract class PgIntColumnBaseBuilder< sequence?: PgSequenceOptions & { name?: string } | PgSequence, ): IsIdentityByDefault { if (sequence) { - if (is(sequence, PgSequence)) { + if (isPgSequence(sequence)) { this.config.generatedIdentity = { type: 'always', sequenceName: sequence.seqName, @@ -43,7 +44,7 @@ export abstract class PgIntColumnBaseBuilder< sequence?: PgSequenceOptions & { name?: string } | PgSequence, ): IsIdentityByDefault { if (sequence) { - if (is(sequence, PgSequence)) { + if (isPgSequence(sequence)) { this.config.generatedIdentity = { type: 'byDefault', sequenceName: sequence.seqName, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f06a6ae2a..50905be4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,19 +15,19 @@ importers: devDependencies: '@arethetypeswrong/cli': specifier: ^0.12.1 - version: 0.12.1(encoding@0.1.13) + version: 0.12.1 '@trivago/prettier-plugin-sort-imports': specifier: ^4.2.0 version: 4.2.0(prettier@3.0.3) '@typescript-eslint/eslint-plugin': specifier: ^6.7.3 - version: 6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) '@typescript-eslint/experimental-utils': specifier: ^5.62.0 - version: 5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 5.62.0(eslint@8.50.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: ^6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) bun-types: specifier: ^1.0.3 version: 1.0.3 @@ -45,7 +45,7 @@ importers: version: link:drizzle-orm/dist drizzle-orm-old: specifier: npm:drizzle-orm@^0.27.2 - version: drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.549.0)(@cloudflare/workers-types@4.20230904.0)(@libsql/client@0.5.6(encoding@0.1.13))(@neondatabase/serverless@0.9.0)(@opentelemetry/api@1.4.1)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.4)(@types/pg@8.10.1)(@types/sql.js@1.4.4)(@vercel/postgres@0.8.0)(better-sqlite3@8.4.0)(bun-types@1.0.3)(knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)))(kysely@0.25.0)(mysql2@3.3.3)(pg@8.11.0)(postgres@3.3.5)(sql.js@1.8.0)(sqlite3@5.1.6(encoding@0.1.13)) + version: drizzle-orm@0.27.2(bun-types@1.0.3) eslint: specifier: ^8.50.0 version: 8.50.0 @@ -54,7 +54,7 @@ importers: version: link:eslint/eslint-plugin-drizzle eslint-plugin-import: specifier: ^2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0) eslint-plugin-no-instanceof: specifier: ^1.0.1 version: 1.0.1 @@ -63,7 +63,7 @@ importers: version: 48.0.1(eslint@8.50.0) eslint-plugin-unused-imports: specifier: ^3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0) + version: 3.0.0(@typescript-eslint/eslint-plugin@6.7.3)(eslint@8.50.0) glob: specifier: ^10.3.10 version: 10.3.10 @@ -75,10 +75,10 @@ importers: version: 0.23.4 resolve-tspaths: specifier: ^0.8.16 - version: 0.8.16(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 0.8.16(typescript@5.2.2) tsup: specifier: ^7.2.0 - version: 7.2.0(postcss@8.4.38)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 7.2.0(typescript@5.2.2) turbo: specifier: ^1.10.14 version: 1.10.14 @@ -99,13 +99,13 @@ importers: version: 0.1.1 '@libsql/client': specifier: ^0.5.6 - version: 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + version: 0.5.6 '@neondatabase/serverless': specifier: ^0.9.0 version: 0.9.0 '@op-engineering/op-sqlite': specifier: ^2.0.16 - version: 2.0.16(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3))(react@18.2.0) + version: 2.0.16(react-native@0.73.6)(react@18.2.0) '@opentelemetry/api': specifier: ^1.4.1 version: 1.4.1 @@ -138,7 +138,7 @@ importers: version: 0.8.0 '@xata.io/client': specifier: ^0.29.3 - version: 0.29.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 0.29.3(typescript@5.2.2) better-sqlite3: specifier: ^8.4.0 version: 8.4.0 @@ -150,10 +150,10 @@ importers: version: 10.1.0 expo-sqlite: specifier: ^13.2.0 - version: 13.2.0(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + version: 13.2.0(expo@50.0.14) knex: specifier: ^2.4.2 - version: 2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)) + version: 2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6) kysely: specifier: ^0.25.0 version: 0.25.0 @@ -174,7 +174,7 @@ importers: version: 1.8.0 sqlite3: specifier: ^5.1.2 - version: 5.1.6(encoding@0.1.13) + version: 5.1.6 tslib: specifier: ^2.5.2 version: 2.5.2 @@ -183,10 +183,10 @@ importers: version: 3.12.7 vite-tsconfig-paths: specifier: ^4.2.0 - version: 4.2.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))(vite@4.3.9(@types/node@20.2.5)(terser@5.30.3)) + version: 4.2.0(typescript@5.2.2)(vite@4.3.9) vitest: specifier: ^0.31.4 - version: 0.31.4(@vitest/ui@0.31.4)(terser@5.30.3) + version: 0.31.4(@vitest/ui@0.31.4) zod: specifier: ^3.20.2 version: 3.21.4 @@ -201,7 +201,7 @@ importers: version: 0.4.1(rollup@3.27.2) '@rollup/plugin-typescript': specifier: ^11.1.0 - version: 11.1.1(rollup@3.27.2)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 11.1.1(rollup@3.27.2)(typescript@5.2.2) '@sinclair/typebox': specifier: ^0.29.6 version: 0.29.6 @@ -237,7 +237,7 @@ importers: version: 0.4.1(rollup@3.27.2) '@rollup/plugin-typescript': specifier: ^11.1.0 - version: 11.1.1(rollup@3.27.2)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 11.1.1(rollup@3.27.2)(typescript@5.2.2) '@types/node': specifier: ^18.15.10 version: 18.15.10 @@ -273,7 +273,7 @@ importers: version: 0.4.1(rollup@3.20.7) '@rollup/plugin-typescript': specifier: ^11.1.0 - version: 11.1.0(rollup@3.20.7)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 11.1.0(rollup@3.20.7)(typescript@5.2.2) '@types/node': specifier: ^18.15.10 version: 18.15.10 @@ -309,13 +309,13 @@ importers: version: 20.10.1 '@typescript-eslint/parser': specifier: ^6.10.0 - version: 6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/rule-tester': specifier: ^6.10.0 - version: 6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/utils': specifier: ^6.10.0 - version: 6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) cpy-cli: specifier: ^5.0.0 version: 5.0.0 @@ -327,7 +327,7 @@ importers: version: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) vitest: specifier: ^0.34.6 - version: 0.34.6(@vitest/ui@0.31.4)(terser@5.30.3) + version: 0.34.6 integration-tests: dependencies: @@ -342,7 +342,7 @@ importers: version: 0.1.1 '@libsql/client': specifier: ^0.5.6 - version: 0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3) + version: 0.5.6 '@miniflare/d1': specifier: ^2.14.2 version: 2.14.2 @@ -363,7 +363,7 @@ importers: version: 0.3.0 '@xata.io/client': specifier: ^0.29.3 - version: 0.29.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + version: 0.29.3(typescript@5.2.2) better-sqlite3: specifier: ^8.4.0 version: 8.4.0 @@ -405,7 +405,7 @@ importers: version: 1.8.0 sqlite3: specifier: ^5.1.4 - version: 5.1.6(encoding@0.1.13) + version: 5.1.6 sst: specifier: ^3.0.4 version: 3.0.4 @@ -417,7 +417,7 @@ importers: version: 0.5.6 vitest: specifier: ^0.31.4 - version: 0.31.4(@vitest/ui@0.31.4)(terser@5.30.3) + version: 0.31.4(@vitest/ui@0.31.4) zod: specifier: ^3.20.2 version: 3.21.4 @@ -466,10 +466,10 @@ importers: version: 3.12.7 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@20.2.5)(terser@5.30.3) + version: 4.3.9(@types/node@20.2.5) vite-tsconfig-paths: specifier: ^4.2.0 - version: 4.2.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))(vite@4.3.9(@types/node@20.2.5)(terser@5.30.3)) + version: 4.2.0(typescript@5.2.2)(vite@4.3.9) zx: specifier: ^7.2.2 version: 7.2.2 @@ -5401,6 +5401,7 @@ packages: libsql@0.3.10: resolution: {integrity: sha512-/8YMTbwWFPmrDWY+YFK3kYqVPFkMgQre0DGmBaOmjogMdSe+7GHm1/q9AZ61AWkEub/vHmi+bA4tqIzVhKnqzg==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lighthouse-logger@1.4.2: @@ -7038,6 +7039,9 @@ packages: sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} + peerDependenciesMeta: + node-gyp: + optional: true sqlstring@2.3.3: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} @@ -7941,18 +7945,6 @@ packages: utf-8-validate: optional: true - ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xcode@3.0.1: resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} engines: {node: '>=10.0.0'} @@ -8057,23 +8049,23 @@ snapshots: '@andrewbranch/untar.js@1.0.2': {} - '@arethetypeswrong/cli@0.12.1(encoding@0.1.13)': + '@arethetypeswrong/cli@0.12.1': dependencies: - '@arethetypeswrong/core': 0.12.1(encoding@0.1.13) + '@arethetypeswrong/core': 0.12.1 chalk: 4.1.2 cli-table3: 0.6.3 commander: 10.0.1 marked: 5.1.2 marked-terminal: 5.2.0(marked@5.1.2) - node-fetch: 2.6.11(encoding@0.1.13) + node-fetch: 2.6.11 semver: 7.5.4 transitivePeerDependencies: - encoding - '@arethetypeswrong/core@0.12.1(encoding@0.1.13)': + '@arethetypeswrong/core@0.12.1': dependencies: '@andrewbranch/untar.js': 1.0.2 - fetch-ponyfill: 7.1.0(encoding@0.1.13) + fetch-ponyfill: 7.1.0 fflate: 0.7.4 semver: 7.5.4 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) @@ -10106,7 +10098,7 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 - '@expo/cli@0.17.8(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.10.3)(utf-8-validate@6.0.3)': + '@expo/cli@0.17.8(@react-native/babel-preset@0.73.21)(expo-modules-autolinking@1.10.3)': dependencies: '@babel/runtime': 7.24.4 '@expo/code-signing-certificates': 0.0.5 @@ -10114,17 +10106,17 @@ snapshots: '@expo/config-plugins': 7.8.4 '@expo/devcert': 1.1.0 '@expo/env': 0.2.2 - '@expo/image-utils': 0.4.1(encoding@0.1.13) + '@expo/image-utils': 0.4.1 '@expo/json-file': 8.3.0 - '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))) + '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21) '@expo/osascript': 2.1.0 '@expo/package-manager': 1.4.2 '@expo/plist': 0.1.0 - '@expo/prebuild-config': 6.7.4(encoding@0.1.13)(expo-modules-autolinking@1.10.3) - '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) + '@expo/prebuild-config': 6.7.4(expo-modules-autolinking@1.10.3) + '@expo/rudder-sdk-node': 1.1.1 '@expo/spawn-async': 1.5.0 '@expo/xcpretty': 4.3.1 - '@react-native/dev-middleware': 0.73.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native/dev-middleware': 0.73.8 '@urql/core': 2.3.6(graphql@15.8.0) '@urql/exchange-retry': 0.3.0(graphql@15.8.0) accepts: 1.3.8 @@ -10155,7 +10147,7 @@ snapshots: md5hex: 1.0.0 minimatch: 3.1.2 minipass: 3.3.6 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 node-forge: 1.3.1 npm-package-arg: 7.0.0 open: 8.4.2 @@ -10183,7 +10175,7 @@ snapshots: text-table: 0.2.0 url-join: 4.0.0 wrap-ansi: 7.0.0 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - '@react-native/babel-preset' - bluebird @@ -10278,14 +10270,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/image-utils@0.4.1(encoding@0.1.13)': + '@expo/image-utils@0.4.1': dependencies: '@expo/spawn-async': 1.5.0 chalk: 4.1.2 fs-extra: 9.0.0 getenv: 1.0.0 jimp-compact: 0.16.1 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 parse-png: 2.1.0 resolve-from: 5.0.0 semver: 7.3.2 @@ -10299,7 +10291,7 @@ snapshots: json5: 2.2.3 write-file-atomic: 2.4.3 - '@expo/metro-config@0.17.6(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))': + '@expo/metro-config@0.17.6(@react-native/babel-preset@0.73.21)': dependencies: '@babel/core': 7.24.4 '@babel/generator': 7.24.4 @@ -10309,7 +10301,7 @@ snapshots: '@expo/env': 0.2.2 '@expo/json-file': 8.3.0 '@expo/spawn-async': 1.7.2 - '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4) babel-preset-fbjs: 3.4.0(@babel/core@7.24.4) chalk: 4.1.2 debug: 4.3.4 @@ -10351,12 +10343,12 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 14.0.0 - '@expo/prebuild-config@6.7.4(encoding@0.1.13)(expo-modules-autolinking@1.10.3)': + '@expo/prebuild-config@6.7.4(expo-modules-autolinking@1.10.3)': dependencies: '@expo/config': 8.5.4 '@expo/config-plugins': 7.8.4 '@expo/config-types': 50.0.0 - '@expo/image-utils': 0.4.1(encoding@0.1.13) + '@expo/image-utils': 0.4.1 '@expo/json-file': 8.3.0 debug: 4.3.4 expo-modules-autolinking: 1.10.3 @@ -10368,13 +10360,13 @@ snapshots: - encoding - supports-color - '@expo/rudder-sdk-node@1.1.1(encoding@0.1.13)': + '@expo/rudder-sdk-node@1.1.1': dependencies: '@expo/bunyan': 4.0.0 '@segment/loosely-validate-event': 2.0.0 fetch-retry: 4.1.1 md5: 2.3.0 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 remove-trailing-slash: 0.1.1 uuid: 8.3.2 transitivePeerDependencies: @@ -10541,39 +10533,16 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@libsql/client@0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3)': - dependencies: - '@libsql/core': 0.5.6 - '@libsql/hrana-client': 0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3) - js-base64: 3.7.5 - libsql: 0.3.10 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - - '@libsql/client@0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': - dependencies: - '@libsql/core': 0.5.6 - '@libsql/hrana-client': 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - js-base64: 3.7.5 - libsql: 0.3.10 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - - '@libsql/client@0.5.6(encoding@0.1.13)': + '@libsql/client@0.5.6': dependencies: '@libsql/core': 0.5.6 - '@libsql/hrana-client': 0.5.6(encoding@0.1.13) + '@libsql/hrana-client': 0.5.6 js-base64: 3.7.5 libsql: 0.3.10 transitivePeerDependencies: - bufferutil - encoding - utf-8-validate - optional: true '@libsql/core@0.5.6': dependencies: @@ -10585,31 +10554,9 @@ snapshots: '@libsql/darwin-x64@0.3.10': optional: true - '@libsql/hrana-client@0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3)': - dependencies: - '@libsql/isomorphic-fetch': 0.1.12(encoding@0.1.13) - '@libsql/isomorphic-ws': 0.1.5(bufferutil@4.0.7)(utf-8-validate@6.0.3) - js-base64: 3.7.5 - node-fetch: 3.3.2 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - - '@libsql/hrana-client@0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': - dependencies: - '@libsql/isomorphic-fetch': 0.1.12(encoding@0.1.13) - '@libsql/isomorphic-ws': 0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3) - js-base64: 3.7.5 - node-fetch: 3.3.2 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - - '@libsql/hrana-client@0.5.6(encoding@0.1.13)': + '@libsql/hrana-client@0.5.6': dependencies: - '@libsql/isomorphic-fetch': 0.1.12(encoding@0.1.13) + '@libsql/isomorphic-fetch': 0.1.12 '@libsql/isomorphic-ws': 0.1.5 js-base64: 3.7.5 node-fetch: 3.3.2 @@ -10617,33 +10564,15 @@ snapshots: - bufferutil - encoding - utf-8-validate - optional: true - '@libsql/isomorphic-fetch@0.1.12(encoding@0.1.13)': + '@libsql/isomorphic-fetch@0.1.12': dependencies: '@types/node-fetch': 2.6.11 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 transitivePeerDependencies: - encoding '@libsql/isomorphic-ws@0.1.5': - dependencies: - '@types/ws': 8.5.4 - ws: 8.14.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - optional: true - - '@libsql/isomorphic-ws@0.1.5(bufferutil@4.0.7)(utf-8-validate@6.0.3)': - dependencies: - '@types/ws': 8.5.4 - ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@libsql/isomorphic-ws@0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: '@types/ws': 8.5.4 ws: 8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) @@ -10666,12 +10595,12 @@ snapshots: '@libsql/win32-x64-msvc@0.3.10': optional: true - '@mapbox/node-pre-gyp@1.0.10(encoding@0.1.13)': + '@mapbox/node-pre-gyp@1.0.10': dependencies: detect-libc: 2.0.1 https-proxy-agent: 5.0.1 make-dir: 3.1.0 - node-fetch: 2.6.9(encoding@0.1.13) + node-fetch: 2.6.9 nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 @@ -10750,10 +10679,10 @@ snapshots: mkdirp: 1.0.4 rimraf: 3.0.2 - '@op-engineering/op-sqlite@2.0.16(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3))(react@18.2.0)': + '@op-engineering/op-sqlite@2.0.16(react-native@0.73.6)(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) '@opentelemetry/api@1.4.1': {} @@ -10768,17 +10697,17 @@ snapshots: '@polka/url@1.0.0-next.21': {} - '@react-native-community/cli-clean@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-clean@12.3.6': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6 chalk: 4.1.2 execa: 5.1.1 transitivePeerDependencies: - encoding - '@react-native-community/cli-config@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-config@12.3.6': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6 chalk: 4.1.2 cosmiconfig: 5.2.1 deepmerge: 4.3.1 @@ -10793,12 +10722,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native-community/cli-doctor@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-doctor@12.3.6': dependencies: - '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-config': 12.3.6 + '@react-native-community/cli-platform-android': 12.3.6 + '@react-native-community/cli-platform-ios': 12.3.6 + '@react-native-community/cli-tools': 12.3.6 chalk: 4.1.2 command-exists: 1.2.9 deepmerge: 4.3.1 @@ -10814,18 +10743,18 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-hermes@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-hermes@12.3.6': dependencies: - '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.6 + '@react-native-community/cli-tools': 12.3.6 chalk: 4.1.2 hermes-profile-transformer: 0.0.6 transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-android@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-platform-android@12.3.6': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6 chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.6 @@ -10834,9 +10763,9 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-ios@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-platform-ios@12.3.6': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6 chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.6 @@ -10847,30 +10776,30 @@ snapshots: '@react-native-community/cli-plugin-metro@12.3.6': {} - '@react-native-community/cli-server-api@12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native-community/cli-server-api@12.3.6': dependencies: '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6 compression: 1.7.4 connect: 3.7.0 errorhandler: 1.5.1 nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 7.5.9 transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - '@react-native-community/cli-tools@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-tools@12.3.6': dependencies: appdirsjs: 1.2.7 chalk: 4.1.2 find-up: 5.0.0 mime: 2.6.0 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 open: 6.4.0 ora: 5.4.1 semver: 7.6.0 @@ -10883,16 +10812,16 @@ snapshots: dependencies: joi: 17.12.3 - '@react-native-community/cli@12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native-community/cli@12.3.6': dependencies: - '@react-native-community/cli-clean': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-clean': 12.3.6 + '@react-native-community/cli-config': 12.3.6 '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-doctor': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-hermes': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-doctor': 12.3.6 + '@react-native-community/cli-hermes': 12.3.6 '@react-native-community/cli-plugin-metro': 12.3.6 - '@react-native-community/cli-server-api': 12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.6 + '@react-native-community/cli-tools': 12.3.6 '@react-native-community/cli-types': 12.3.6 chalk: 4.1.2 commander: 9.5.0 @@ -10911,14 +10840,14 @@ snapshots: '@react-native/assets-registry@0.73.1': {} - '@react-native/babel-plugin-codegen@0.73.4(@babel/preset-env@7.24.4(@babel/core@7.24.4))': + '@react-native/babel-plugin-codegen@0.73.4(@babel/preset-env@7.24.4)': dependencies: - '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4) transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))': + '@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.24.4) @@ -10959,38 +10888,38 @@ snapshots: '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.4) '@babel/plugin-transform-unicode-regex': 7.24.1(@babel/core@7.24.4) '@babel/template': 7.24.0 - '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.24.4) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.24.4) react-refresh: 0.14.0 transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/codegen@0.73.3(@babel/preset-env@7.24.4(@babel/core@7.24.4))': + '@react-native/codegen@0.73.3(@babel/preset-env@7.24.4)': dependencies: '@babel/parser': 7.24.4 '@babel/preset-env': 7.24.4(@babel/core@7.24.4) flow-parser: 0.206.0 glob: 7.2.3 invariant: 2.2.4 - jscodeshift: 0.14.0(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + jscodeshift: 0.14.0(@babel/preset-env@7.24.4) mkdirp: 0.5.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native/community-cli-plugin@0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4)': dependencies: - '@react-native-community/cli-server-api': 12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) - '@react-native/dev-middleware': 0.73.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native-community/cli-server-api': 12.3.6 + '@react-native-community/cli-tools': 12.3.6 + '@react-native/dev-middleware': 0.73.8 + '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - metro-config: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro: 0.80.8 + metro-config: 0.80.8 metro-core: 0.80.8 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 readline: 1.3.0 transitivePeerDependencies: - '@babel/core' @@ -11002,7 +10931,7 @@ snapshots: '@react-native/debugger-frontend@0.73.3': {} - '@react-native/dev-middleware@0.73.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native/dev-middleware@0.73.8': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.73.3 @@ -11010,11 +10939,11 @@ snapshots: chromium-edge-launcher: 1.0.0 connect: 3.7.0 debug: 2.6.9 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 open: 7.4.2 serve-static: 1.15.0 temp-dir: 2.0.0 - ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 6.2.2 transitivePeerDependencies: - bufferutil - encoding @@ -11025,10 +10954,10 @@ snapshots: '@react-native/js-polyfills@0.73.1': {} - '@react-native/metro-babel-transformer@0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))': + '@react-native/metro-babel-transformer@0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4)': dependencies: '@babel/core': 7.24.4 - '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4) hermes-parser: 0.15.0 nullthrows: 1.1.1 transitivePeerDependencies: @@ -11039,52 +10968,45 @@ snapshots: '@react-native/normalize-colors@0.73.2': {} - '@react-native/virtualized-lists@0.73.4(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3))': + '@react-native/virtualized-lists@0.73.4(react-native@0.73.6)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) '@rollup/plugin-terser@0.4.1(rollup@3.20.7)': dependencies: + rollup: 3.20.7 serialize-javascript: 6.0.1 smob: 0.0.6 terser: 5.17.1 - optionalDependencies: - rollup: 3.20.7 '@rollup/plugin-terser@0.4.1(rollup@3.27.2)': dependencies: + rollup: 3.27.2 serialize-javascript: 6.0.1 smob: 0.0.6 terser: 5.17.1 - optionalDependencies: - rollup: 3.27.2 - '@rollup/plugin-typescript@11.1.0(rollup@3.20.7)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@rollup/plugin-typescript@11.1.0(rollup@3.20.7)(typescript@5.2.2)': dependencies: '@rollup/pluginutils': 5.0.2(rollup@3.20.7) resolve: 1.22.1 - typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) - optionalDependencies: rollup: 3.20.7 - tslib: 2.6.2 + typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) - '@rollup/plugin-typescript@11.1.1(rollup@3.27.2)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@rollup/plugin-typescript@11.1.1(rollup@3.27.2)(typescript@5.2.2)': dependencies: '@rollup/pluginutils': 5.0.2(rollup@3.27.2) resolve: 1.22.2 - typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) - optionalDependencies: rollup: 3.27.2 - tslib: 2.6.2 + typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) '@rollup/pluginutils@5.0.2(rollup@3.20.7)': dependencies: '@types/estree': 1.0.1 estree-walker: 2.0.2 picomatch: 2.3.1 - optionalDependencies: rollup: 3.20.7 '@rollup/pluginutils@5.0.2(rollup@3.27.2)': @@ -11092,7 +11014,6 @@ snapshots: '@types/estree': 1.0.1 estree-walker: 2.0.2 picomatch: 2.3.1 - optionalDependencies: rollup: 3.27.2 '@segment/loosely-validate-event@2.0.0': @@ -11591,13 +11512,13 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2)': dependencies: '@eslint-community/regexpp': 4.9.0 - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 6.7.3 - '@typescript-eslint/type-utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/type-utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.7.3 debug: 4.3.4 eslint: 8.50.0 @@ -11605,51 +11526,48 @@ snapshots: ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - optionalDependencies: + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/experimental-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2)': dependencies: - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) eslint: 8.50.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2)': dependencies: '@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.10.0 debug: 4.3.4 eslint: 8.53.0 - optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2)': dependencies: '@typescript-eslint/scope-manager': 6.7.3 '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.7.3 debug: 4.3.4 eslint: 8.50.0 - optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/rule-tester@6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/rule-tester@6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2)': dependencies: '@eslint/eslintrc': 3.0.2 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) ajv: 6.12.6 eslint: 8.53.0 lodash.merge: 4.6.2 @@ -11673,14 +11591,13 @@ snapshots: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 - '@typescript-eslint/type-utils@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/type-utils@6.7.3(eslint@8.50.0)(typescript@5.2.2)': dependencies: - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) debug: 4.3.4 eslint: 8.50.0 - ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - optionalDependencies: + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color @@ -11691,7 +11608,7 @@ snapshots: '@typescript-eslint/types@6.7.3': {} - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 @@ -11699,13 +11616,12 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - optionalDependencies: + tsutils: 3.21.0(typescript@5.2.2) typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2)': dependencies: '@typescript-eslint/types': 6.10.0 '@typescript-eslint/visitor-keys': 6.10.0 @@ -11713,13 +11629,12 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - optionalDependencies: + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/typescript-estree@6.7.3(typescript@5.2.2)': dependencies: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 @@ -11727,20 +11642,19 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - optionalDependencies: + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) eslint: 8.50.0 eslint-scope: 5.1.1 semver: 7.5.4 @@ -11748,28 +11662,28 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) eslint: 8.53.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@typescript-eslint/utils@6.7.3(eslint@8.50.0)(typescript@5.2.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.7.3 '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) eslint: 8.50.0 semver: 7.5.4 transitivePeerDependencies: @@ -11884,19 +11798,7 @@ snapshots: pathe: 1.1.1 picocolors: 1.0.0 sirv: 2.0.3 - vitest: 0.31.4(@vitest/ui@0.31.4)(terser@5.30.3) - - '@vitest/ui@0.31.4(vitest@0.34.6)': - dependencies: - '@vitest/utils': 0.31.4 - fast-glob: 3.2.12 - fflate: 0.7.4 - flatted: 3.2.7 - pathe: 1.1.1 - picocolors: 1.0.0 - sirv: 2.0.3 - vitest: 0.34.6(@vitest/ui@0.31.4)(terser@5.30.3) - optional: true + vitest: 0.31.4(@vitest/ui@0.31.4) '@vitest/utils@0.31.4': dependencies: @@ -11910,7 +11812,7 @@ snapshots: loupe: 2.3.6 pretty-format: 29.7.0 - '@xata.io/client@0.29.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': + '@xata.io/client@0.29.3(typescript@5.2.2)': dependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) @@ -12289,7 +12191,7 @@ snapshots: '@babel/plugin-transform-parameters': 7.24.1(@babel/core@7.24.4) '@babel/preset-env': 7.24.4(@babel/core@7.24.4) '@babel/preset-react': 7.24.1(@babel/core@7.24.4) - '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4) babel-plugin-react-native-web: 0.18.12 react-refresh: 0.14.0 transitivePeerDependencies: @@ -12841,9 +12743,9 @@ snapshots: p-filter: 3.0.0 p-map: 6.0.0 - cross-fetch@3.1.8(encoding@0.1.13): + cross-fetch@3.1.8: dependencies: - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 transitivePeerDependencies: - encoding @@ -13060,27 +12962,9 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.549.0)(@cloudflare/workers-types@4.20230904.0)(@libsql/client@0.5.6(encoding@0.1.13))(@neondatabase/serverless@0.9.0)(@opentelemetry/api@1.4.1)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.4)(@types/pg@8.10.1)(@types/sql.js@1.4.4)(@vercel/postgres@0.8.0)(better-sqlite3@8.4.0)(bun-types@1.0.3)(knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)))(kysely@0.25.0)(mysql2@3.3.3)(pg@8.11.0)(postgres@3.3.5)(sql.js@1.8.0)(sqlite3@5.1.6(encoding@0.1.13)): - optionalDependencies: - '@aws-sdk/client-rds-data': 3.549.0 - '@cloudflare/workers-types': 4.20230904.0 - '@libsql/client': 0.5.6(encoding@0.1.13) - '@neondatabase/serverless': 0.9.0 - '@opentelemetry/api': 1.4.1 - '@planetscale/database': 1.16.0 - '@types/better-sqlite3': 7.6.4 - '@types/pg': 8.10.1 - '@types/sql.js': 1.4.4 - '@vercel/postgres': 0.8.0 - better-sqlite3: 8.4.0 + drizzle-orm@0.27.2(bun-types@1.0.3): + dependencies: bun-types: 1.0.3 - knex: 2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)) - kysely: 0.25.0 - mysql2: 3.3.3 - pg: 8.11.0 - postgres: 3.3.5 - sql.js: 1.8.0 - sqlite3: 5.1.6(encoding@0.1.13) duplexer@0.1.2: {} @@ -13377,18 +13261,18 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint-import-resolver-node@0.3.9)(eslint@8.50.0): + eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0): dependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0): + eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0): dependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) array-includes: 3.1.6 array.prototype.findlastindex: 1.2.2 array.prototype.flat: 1.3.1 @@ -13397,7 +13281,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint-import-resolver-node@0.3.9)(eslint@8.50.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -13407,8 +13291,6 @@ snapshots: object.values: 1.1.6 semver: 6.3.1 tsconfig-paths: 3.14.2 - optionalDependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -13435,12 +13317,11 @@ snapshots: semver: 7.5.4 strip-indent: 3.0.0 - eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0): + eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.7.3)(eslint@8.50.0): dependencies: + '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) eslint: 8.50.0 eslint-rule-composer: 0.3.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint-rule-composer@0.3.0: {} @@ -13634,37 +13515,37 @@ snapshots: expand-template@2.0.3: {} - expo-asset@9.0.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-asset@9.0.2(expo@50.0.14): dependencies: '@react-native/assets-registry': 0.73.1 blueimp-md5: 2.19.0 - expo-constants: 15.4.5(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-file-system: 16.0.8(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-constants: 15.4.5(expo@50.0.14) + expo-file-system: 16.0.8(expo@50.0.14) invariant: 2.2.4 md5-file: 3.2.3 transitivePeerDependencies: - expo - supports-color - expo-constants@15.4.5(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-constants@15.4.5(expo@50.0.14): dependencies: '@expo/config': 8.5.4 - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) transitivePeerDependencies: - supports-color - expo-file-system@16.0.8(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-file-system@16.0.8(expo@50.0.14): dependencies: - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) - expo-font@11.10.3(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-font@11.10.3(expo@50.0.14): dependencies: - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) fontfaceobserver: 2.3.0 - expo-keep-awake@12.8.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-keep-awake@12.8.2(expo@50.0.14): dependencies: - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) expo-modules-autolinking@1.10.3: dependencies: @@ -13681,27 +13562,27 @@ snapshots: dependencies: invariant: 2.2.4 - expo-sqlite@13.2.0(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-sqlite@13.2.0(expo@50.0.14): dependencies: '@expo/websql': 1.0.1 - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) - expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21): dependencies: '@babel/runtime': 7.24.4 - '@expo/cli': 0.17.8(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.10.3)(utf-8-validate@6.0.3) + '@expo/cli': 0.17.8(@react-native/babel-preset@0.73.21)(expo-modules-autolinking@1.10.3) '@expo/config': 8.5.4 '@expo/config-plugins': 7.8.4 - '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))) + '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21) '@expo/vector-icons': 14.0.0 babel-preset-expo: 10.0.1(@babel/core@7.24.4) - expo-asset: 9.0.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-file-system: 16.0.8(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-font: 11.10.3(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-keep-awake: 12.8.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-asset: 9.0.2(expo@50.0.14) + expo-file-system: 16.0.8(expo@50.0.14) + expo-font: 11.10.3(expo@50.0.14) + expo-keep-awake: 12.8.2(expo@50.0.14) expo-modules-autolinking: 1.10.3 expo-modules-core: 1.11.12 - fbemitter: 3.0.0(encoding@0.1.13) + fbemitter: 3.0.0 whatwg-url-without-unicode: 8.0.0-3 transitivePeerDependencies: - '@babel/core' @@ -13800,17 +13681,17 @@ snapshots: dependencies: bser: 2.1.1 - fbemitter@3.0.0(encoding@0.1.13): + fbemitter@3.0.0: dependencies: - fbjs: 3.0.5(encoding@0.1.13) + fbjs: 3.0.5 transitivePeerDependencies: - encoding fbjs-css-vars@1.0.2: {} - fbjs@3.0.5(encoding@0.1.13): + fbjs@3.0.5: dependencies: - cross-fetch: 3.1.8(encoding@0.1.13) + cross-fetch: 3.1.8 fbjs-css-vars: 1.0.2 loose-envify: 1.4.0 object-assign: 4.1.1 @@ -13825,9 +13706,9 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.2.1 - fetch-ponyfill@7.1.0(encoding@0.1.13): + fetch-ponyfill@7.1.0: dependencies: - node-fetch: 2.6.11(encoding@0.1.13) + node-fetch: 2.6.11 transitivePeerDependencies: - encoding @@ -14639,7 +14520,7 @@ snapshots: jsc-safe-url@0.2.4: {} - jscodeshift@0.14.0(@babel/preset-env@7.24.4(@babel/core@7.24.4)): + jscodeshift@0.14.0(@babel/preset-env@7.24.4): dependencies: '@babel/core': 7.24.4 '@babel/parser': 7.24.4 @@ -14734,8 +14615,9 @@ snapshots: kleur@4.1.5: {} - knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)): + knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6): dependencies: + better-sqlite3: 8.4.0 colorette: 2.0.19 commander: 9.5.0 debug: 4.3.4 @@ -14745,16 +14627,14 @@ snapshots: getopts: 2.3.0 interpret: 2.2.0 lodash: 4.17.21 + mysql2: 3.3.3 + pg: 8.11.0 pg-connection-string: 2.5.0 rechoir: 0.8.0 resolve-from: 5.0.0 + sqlite3: 5.1.6 tarn: 3.0.2 tildify: 2.0.0 - optionalDependencies: - better-sqlite3: 8.4.0 - mysql2: 3.3.3 - pg: 8.11.0 - sqlite3: 5.1.6(encoding@0.1.13) transitivePeerDependencies: - supports-color @@ -15041,12 +14921,12 @@ snapshots: metro-core: 0.80.8 rimraf: 3.0.2 - metro-config@0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + metro-config@0.80.8: dependencies: connect: 3.7.0 cosmiconfig: 5.2.1 jest-validate: 29.7.0 - metro: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro: 0.80.8 metro-cache: 0.80.8 metro-core: 0.80.8 metro-runtime: 0.80.8 @@ -15122,13 +15002,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + metro-transform-worker@0.80.8: dependencies: '@babel/core': 7.24.4 '@babel/generator': 7.24.4 '@babel/parser': 7.24.4 '@babel/types': 7.24.0 - metro: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro: 0.80.8 metro-babel-transformer: 0.80.8 metro-cache: 0.80.8 metro-cache-key: 0.80.8 @@ -15142,7 +15022,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + metro@0.80.8: dependencies: '@babel/code-frame': 7.24.2 '@babel/core': 7.24.4 @@ -15168,7 +15048,7 @@ snapshots: metro-babel-transformer: 0.80.8 metro-cache: 0.80.8 metro-cache-key: 0.80.8 - metro-config: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-config: 0.80.8 metro-core: 0.80.8 metro-file-map: 0.80.8 metro-resolver: 0.80.8 @@ -15176,16 +15056,16 @@ snapshots: metro-source-map: 0.80.8 metro-symbolicate: 0.80.8 metro-transform-plugins: 0.80.8 - metro-transform-worker: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-transform-worker: 0.80.8 mime-types: 2.1.35 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 nullthrows: 1.1.1 rimraf: 3.0.2 serialize-error: 2.1.0 source-map: 0.5.7 strip-ansi: 6.0.1 throat: 5.0.0 - ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 7.5.9 yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -15381,23 +15261,17 @@ snapshots: dependencies: lodash: 4.17.21 - node-fetch@2.6.11(encoding@0.1.13): + node-fetch@2.6.11: dependencies: whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 - node-fetch@2.6.9(encoding@0.1.13): + node-fetch@2.6.9: dependencies: whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 - node-fetch@2.7.0(encoding@0.1.13): + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 node-fetch@3.3.1: dependencies: @@ -15845,12 +15719,10 @@ snapshots: pngjs@3.4.0: {} - postcss-load-config@4.0.1(postcss@8.4.38): + postcss-load-config@4.0.1: dependencies: lilconfig: 2.1.0 yaml: 2.3.1 - optionalDependencies: - postcss: 8.4.38 postcss@8.4.24: dependencies: @@ -16017,10 +15889,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-devtools-core@4.28.5(bufferutil@4.0.8)(utf-8-validate@6.0.3): + react-devtools-core@4.28.5: dependencies: shell-quote: 1.8.1 - ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 7.5.9 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -16031,19 +15903,19 @@ snapshots: react-is@18.2.0: {} - react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3): + react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) + '@react-native-community/cli': 12.3.6 + '@react-native-community/cli-platform-android': 12.3.6 + '@react-native-community/cli-platform-ios': 12.3.6 '@react-native/assets-registry': 0.73.1 - '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4(@babel/core@7.24.4)) - '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4) + '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 - '@react-native/virtualized-lists': 0.73.4(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3)) + '@react-native/virtualized-lists': 0.73.4(react-native@0.73.6) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -16063,14 +15935,14 @@ snapshots: pretty-format: 26.6.2 promise: 8.3.0 react: 18.2.0 - react-devtools-core: 4.28.5(bufferutil@4.0.8)(utf-8-validate@6.0.3) + react-devtools-core: 4.28.5 react-refresh: 0.14.0 react-shallow-renderer: 16.15.0(react@18.2.0) regenerator-runtime: 0.13.11 scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 whatwg-fetch: 3.6.20 - ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 6.2.2 yargs: 17.7.2 transitivePeerDependencies: - '@babel/core' @@ -16215,7 +16087,7 @@ snapshots: resolve-from@5.0.0: {} - resolve-tspaths@0.8.16(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): + resolve-tspaths@0.8.16(typescript@5.2.2): dependencies: ansi-colors: 4.1.3 commander: 11.0.0 @@ -16549,9 +16421,9 @@ snapshots: sql.js@1.8.0: {} - sqlite3@5.1.6(encoding@0.1.13): + sqlite3@5.1.6: dependencies: - '@mapbox/node-pre-gyp': 1.0.10(encoding@0.1.13) + '@mapbox/node-pre-gyp': 1.0.10 node-addon-api: 4.3.0 tar: 6.1.13 optionalDependencies: @@ -16885,14 +16757,14 @@ snapshots: treeify@1.1.0: {} - ts-api-utils@1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): + ts-api-utils@1.0.3(typescript@5.2.2): dependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) ts-interface-checker@0.1.13: {} - tsconfck@2.1.1(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): - optionalDependencies: + tsconfck@2.1.1(typescript@5.2.2): + dependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) tsconfig-paths@3.14.2: @@ -16910,7 +16782,7 @@ snapshots: tslib@2.6.2: {} - tsup@7.2.0(postcss@8.4.38)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): + tsup@7.2.0(typescript@5.2.2): dependencies: bundle-require: 4.0.2(esbuild@0.18.20) cac: 6.7.14 @@ -16920,20 +16792,18 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.1 resolve-from: 5.0.0 rollup: 3.27.2 source-map: 0.8.0-beta.0 sucrase: 3.34.0 tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.4.38 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - ts-node - tsutils@3.21.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): + tsutils@3.21.0(typescript@5.2.2): dependencies: tslib: 1.14.1 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) @@ -17175,14 +17045,14 @@ snapshots: vary@1.1.2: {} - vite-node@0.31.4(@types/node@20.8.7)(terser@5.30.3): + vite-node@0.31.4(@types/node@20.8.7): dependencies: cac: 6.7.14 debug: 4.3.4 mlly: 1.3.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.3.9(@types/node@20.8.7)(terser@5.30.3) + vite: 4.3.9(@types/node@20.8.7) transitivePeerDependencies: - '@types/node' - less @@ -17192,14 +17062,14 @@ snapshots: - supports-color - terser - vite-node@0.34.6(@types/node@20.10.1)(terser@5.30.3): + vite-node@0.34.6(@types/node@20.10.1): dependencies: cac: 6.7.14 debug: 4.3.4 mlly: 1.4.2 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.3.9(@types/node@20.10.1)(terser@5.30.3) + vite: 4.3.9(@types/node@20.10.1) transitivePeerDependencies: - '@types/node' - less @@ -17209,48 +17079,44 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.2.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))(vite@4.3.9(@types/node@20.2.5)(terser@5.30.3)): + vite-tsconfig-paths@4.2.0(typescript@5.2.2)(vite@4.3.9): dependencies: debug: 4.3.4 globrex: 0.1.2 - tsconfck: 2.1.1(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) - optionalDependencies: - vite: 4.3.9(@types/node@20.2.5)(terser@5.30.3) + tsconfck: 2.1.1(typescript@5.2.2) + vite: 4.3.9(@types/node@20.2.5) transitivePeerDependencies: - supports-color - typescript - vite@4.3.9(@types/node@20.10.1)(terser@5.30.3): + vite@4.3.9(@types/node@20.10.1): dependencies: + '@types/node': 20.10.1 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.27.2 optionalDependencies: - '@types/node': 20.10.1 fsevents: 2.3.3 - terser: 5.30.3 - vite@4.3.9(@types/node@20.2.5)(terser@5.30.3): + vite@4.3.9(@types/node@20.2.5): dependencies: + '@types/node': 20.2.5 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.27.2 optionalDependencies: - '@types/node': 20.2.5 fsevents: 2.3.3 - terser: 5.30.3 - vite@4.3.9(@types/node@20.8.7)(terser@5.30.3): + vite@4.3.9(@types/node@20.8.7): dependencies: + '@types/node': 20.8.7 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.27.2 optionalDependencies: - '@types/node': 20.8.7 fsevents: 2.3.3 - terser: 5.30.3 - vitest@0.31.4(@vitest/ui@0.31.4)(terser@5.30.3): + vitest@0.31.4(@vitest/ui@0.31.4): dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 @@ -17259,6 +17125,7 @@ snapshots: '@vitest/runner': 0.31.4 '@vitest/snapshot': 0.31.4 '@vitest/spy': 0.31.4 + '@vitest/ui': 0.31.4(vitest@0.31.4) '@vitest/utils': 0.31.4 acorn: 8.8.2 acorn-walk: 8.2.0 @@ -17274,11 +17141,9 @@ snapshots: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.5.0 - vite: 4.3.9(@types/node@20.8.7)(terser@5.30.3) - vite-node: 0.31.4(@types/node@20.8.7)(terser@5.30.3) + vite: 4.3.9(@types/node@20.8.7) + vite-node: 0.31.4(@types/node@20.8.7) why-is-node-running: 2.2.2 - optionalDependencies: - '@vitest/ui': 0.31.4(vitest@0.31.4) transitivePeerDependencies: - less - sass @@ -17287,7 +17152,7 @@ snapshots: - supports-color - terser - vitest@0.34.6(@vitest/ui@0.31.4)(terser@5.30.3): + vitest@0.34.6: dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 @@ -17310,11 +17175,9 @@ snapshots: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.3.9(@types/node@20.10.1)(terser@5.30.3) - vite-node: 0.34.6(@types/node@20.10.1)(terser@5.30.3) + vite: 4.3.9(@types/node@20.10.1) + vite-node: 0.34.6(@types/node@20.10.1) why-is-node-running: 2.2.2 - optionalDependencies: - '@vitest/ui': 0.31.4(vitest@0.34.6) transitivePeerDependencies: - less - sass @@ -17443,38 +17306,19 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.0.2 - ws@6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): + ws@6.2.2: dependencies: async-limiter: 1.0.1 - optionalDependencies: - bufferutil: 4.0.8 - utf-8-validate: 6.0.3 - ws@7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3): - optionalDependencies: - bufferutil: 4.0.8 - utf-8-validate: 6.0.3 + ws@7.5.9: {} ws@8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3): - optionalDependencies: - bufferutil: 4.0.7 - utf-8-validate: 6.0.3 - - ws@8.14.2: - optional: true - - ws@8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3): - optionalDependencies: + dependencies: bufferutil: 4.0.7 utf-8-validate: 6.0.3 ws@8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): - optionalDependencies: - bufferutil: 4.0.8 - utf-8-validate: 6.0.3 - - ws@8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): - optionalDependencies: + dependencies: bufferutil: 4.0.8 utf-8-validate: 6.0.3 From f4b4750503aa5c5a12a3826ad51e0bcbd265e161 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Wed, 12 Jun 2024 12:54:35 +0300 Subject: [PATCH 20/40] Update isPgSequence --- drizzle-orm/src/pg-core/columns/int.common.ts | 10 +++++----- drizzle-orm/src/pg-core/sequence.ts | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index a23f5d6a4..372c36023 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -4,9 +4,9 @@ import type { GeneratedIdentityConfig, IsIdentityByDefault, } from '~/column-builder.ts'; -import { entityKind } from '~/entity.ts'; -import { isPgSequence } from '../sequence.ts'; -import type { PgSequence, PgSequenceOptions } from '../sequence.ts'; +import { entityKind, is } from '~/entity.ts'; +import { PgSequence } from '../sequence.ts'; +import type { PgSequenceOptions } from '../sequence.ts'; import { PgColumnBuilder } from './common.ts'; export abstract class PgIntColumnBaseBuilder< @@ -21,7 +21,7 @@ export abstract class PgIntColumnBaseBuilder< sequence?: PgSequenceOptions & { name?: string } | PgSequence, ): IsIdentityByDefault { if (sequence) { - if (isPgSequence(sequence)) { + if (is(sequence, PgSequence)) { this.config.generatedIdentity = { type: 'always', sequenceName: sequence.seqName, @@ -44,7 +44,7 @@ export abstract class PgIntColumnBaseBuilder< sequence?: PgSequenceOptions & { name?: string } | PgSequence, ): IsIdentityByDefault { if (sequence) { - if (isPgSequence(sequence)) { + if (is(sequence, PgSequence)) { this.config.generatedIdentity = { type: 'byDefault', sequenceName: sequence.seqName, diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index d6b850c4e..1252362cd 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -1,3 +1,5 @@ +import { entityKind, is } from '~/entity'; + export type PgSequenceOptions = { increment?: number; minValue?: number; @@ -7,14 +9,12 @@ export type PgSequenceOptions = { cycle?: boolean; }; -const isPgSequenceSym = Symbol.for('drizzle:isPgSequence'); -export interface PgSequence { +export class PgSequence { + static readonly [entityKind]: string = 'PgSequence'; + readonly seqName: string | undefined; readonly seqOptions: PgSequenceOptions | undefined; readonly schema: string | undefined; - - /** @internal */ - [isPgSequenceSym]: true; } export function pgSequence( @@ -42,5 +42,5 @@ export function pgSequenceWithSchema( } export function isPgSequence(obj: unknown): obj is PgSequence { - return !!obj && typeof obj === 'function' && isPgSequenceSym in obj && obj[isPgSequenceSym] === true; + return is(obj, PgSequence); } From 4ed01aaf75236eadf665b3ca8251f2f85d987332 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Wed, 12 Jun 2024 13:02:42 +0300 Subject: [PATCH 21/40] Fix imports in sequences --- drizzle-orm/src/pg-core/sequence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index 1252362cd..fc35fca23 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -1,4 +1,4 @@ -import { entityKind, is } from '~/entity'; +import { entityKind, is } from '~/entity.ts'; export type PgSequenceOptions = { increment?: number; From 0055471ebe183a1c4abe1ee25155370830d0a774 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Wed, 12 Jun 2024 13:45:06 +0300 Subject: [PATCH 22/40] Update PgSequence creation --- drizzle-orm/src/pg-core/sequence.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index fc35fca23..01f7eb8c5 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -12,9 +12,12 @@ export type PgSequenceOptions = { export class PgSequence { static readonly [entityKind]: string = 'PgSequence'; - readonly seqName: string | undefined; - readonly seqOptions: PgSequenceOptions | undefined; - readonly schema: string | undefined; + constructor( + public readonly seqName: string | undefined, + public readonly seqOptions: PgSequenceOptions | undefined, + public readonly schema: string | undefined, + ) { + } } export function pgSequence( @@ -30,15 +33,7 @@ export function pgSequenceWithSchema( options: PgSequenceOptions, schema?: string, ): PgSequence { - const sequenceInstance: PgSequence = Object.assign( - { - name, - seqOptions: options, - schema, - } as const, - ); - - return sequenceInstance; + return new PgSequence(name, options, schema); } export function isPgSequence(obj: unknown): obj is PgSequence { From 0a6885dc9577836aba6756ad9d7adaaf3af2fe8c Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 13 Jun 2024 17:34:18 +0300 Subject: [PATCH 23/40] Allow numbers ans strings for sequences properties --- drizzle-orm/src/pg-core/sequence.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index 01f7eb8c5..6817f4db4 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -1,11 +1,11 @@ import { entityKind, is } from '~/entity.ts'; export type PgSequenceOptions = { - increment?: number; - minValue?: number; - maxValue?: number; - startWith?: number; - cache?: number; + increment?: number | string; + minValue?: number | string; + maxValue?: number | string; + startWith?: number | string; + cache?: number | string; cycle?: boolean; }; From af7ce997bccfa50b7913d7b6e69eb28e1f6ab7ab Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Mon, 17 Jun 2024 18:03:03 +0300 Subject: [PATCH 24/40] Add callback option to mysql and sqlite generated --- drizzle-orm/src/mysql-core/columns/common.ts | 2 +- drizzle-orm/src/sqlite-core/columns/common.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drizzle-orm/src/mysql-core/columns/common.ts b/drizzle-orm/src/mysql-core/columns/common.ts index 85dca59ff..a91560e74 100644 --- a/drizzle-orm/src/mysql-core/columns/common.ts +++ b/drizzle-orm/src/mysql-core/columns/common.ts @@ -61,7 +61,7 @@ export abstract class MySqlColumnBuilder< return this; } - generatedAlwaysAs(as: SQL | T['data'], config?: MySqlGeneratedColumnConfig): HasGenerated { + generatedAlwaysAs(as: SQL | T['data'] | (() => SQL), config?: MySqlGeneratedColumnConfig): HasGenerated { this.config.generated = { as, type: 'always', diff --git a/drizzle-orm/src/sqlite-core/columns/common.ts b/drizzle-orm/src/sqlite-core/columns/common.ts index 0f2772d31..a0cdd755d 100644 --- a/drizzle-orm/src/sqlite-core/columns/common.ts +++ b/drizzle-orm/src/sqlite-core/columns/common.ts @@ -63,7 +63,7 @@ export abstract class SQLiteColumnBuilder< return this; } - generatedAlwaysAs(as: SQL | T['data'], config?: SQLiteGeneratedColumnConfig): HasGenerated { + generatedAlwaysAs(as: SQL | T['data'] | (() => SQL), config?: SQLiteGeneratedColumnConfig): HasGenerated { this.config.generated = { as, type: 'always', From 81cb79452058ce1903e81117dde21cbbcd0c15e9 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Sat, 22 Jun 2024 13:59:46 +0300 Subject: [PATCH 25/40] Update identity behavior --- drizzle-orm/src/pg-core/columns/int.common.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index 372c36023..1842a37b8 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -35,6 +35,10 @@ export abstract class PgIntColumnBaseBuilder< sequenceOptions: options, }; } + } else { + this.config.generatedIdentity = { + type: 'always', + }; } return this as any; @@ -58,6 +62,10 @@ export abstract class PgIntColumnBaseBuilder< sequenceOptions: options, }; } + } else { + this.config.generatedIdentity = { + type: 'byDefault', + }; } return this as any; From 7721c7c289fab1c7a40925fbbc425e797aadffdc Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Sat, 22 Jun 2024 20:41:25 +0300 Subject: [PATCH 26/40] Remove seq as an option for identity column --- drizzle-orm/src/pg-core/columns/int.common.ts | 47 ++++++------------- .../type-tests/pg/generated-columns.ts | 16 ++----- 2 files changed, 18 insertions(+), 45 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index 1842a37b8..6816c6e32 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -4,8 +4,7 @@ import type { GeneratedIdentityConfig, IsIdentityByDefault, } from '~/column-builder.ts'; -import { entityKind, is } from '~/entity.ts'; -import { PgSequence } from '../sequence.ts'; +import { entityKind } from '~/entity.ts'; import type { PgSequenceOptions } from '../sequence.ts'; import { PgColumnBuilder } from './common.ts'; @@ -18,23 +17,15 @@ export abstract class PgIntColumnBaseBuilder< static readonly [entityKind]: string = 'PgIntColumnBaseBuilder'; generatedAlwaysAsIdentity( - sequence?: PgSequenceOptions & { name?: string } | PgSequence, + sequence?: PgSequenceOptions & { name?: string }, ): IsIdentityByDefault { if (sequence) { - if (is(sequence, PgSequence)) { - this.config.generatedIdentity = { - type: 'always', - sequenceName: sequence.seqName, - sequenceOptions: sequence.seqOptions, - }; - } else { - const { name, ...options } = sequence; - this.config.generatedIdentity = { - type: 'always', - sequenceName: name, - sequenceOptions: options, - }; - } + const { name, ...options } = sequence; + this.config.generatedIdentity = { + type: 'always', + sequenceName: name, + sequenceOptions: options, + }; } else { this.config.generatedIdentity = { type: 'always', @@ -45,23 +36,15 @@ export abstract class PgIntColumnBaseBuilder< } generatedByDefaultAsIdentity( - sequence?: PgSequenceOptions & { name?: string } | PgSequence, + sequence?: PgSequenceOptions & { name?: string }, ): IsIdentityByDefault { if (sequence) { - if (is(sequence, PgSequence)) { - this.config.generatedIdentity = { - type: 'byDefault', - sequenceName: sequence.seqName, - sequenceOptions: sequence.seqOptions, - }; - } else { - const { name, ...options } = sequence; - this.config.generatedIdentity = { - type: 'byDefault', - sequenceName: name, - sequenceOptions: options, - }; - } + const { name, ...options } = sequence; + this.config.generatedIdentity = { + type: 'byDefault', + sequenceName: name, + sequenceOptions: options, + }; } else { this.config.generatedIdentity = { type: 'byDefault', diff --git a/drizzle-orm/type-tests/pg/generated-columns.ts b/drizzle-orm/type-tests/pg/generated-columns.ts index 6f3213e29..afe84def6 100644 --- a/drizzle-orm/type-tests/pg/generated-columns.ts +++ b/drizzle-orm/type-tests/pg/generated-columns.ts @@ -1,7 +1,7 @@ import { type Equal, Expect } from 'type-tests/utils'; import { type InferInsertModel, type InferSelectModel, sql } from '~/index'; import { drizzle } from '~/node-postgres'; -import { integer, pgSchema, pgSequence, pgTable, serial, text, varchar } from '~/pg-core'; +import { integer, pgTable, serial, text, varchar } from '~/pg-core'; import { db } from './db'; const users = pgTable( @@ -189,21 +189,11 @@ const users2 = pgTable( >(); } -const customSequence = pgSequence('custom_seq', { - minValue: 100000, - increment: 1, -}); - -const customSequenceSchema = pgSchema('test').sequence('custom_seq', { - minValue: 100000, - increment: 1, -}); - const usersSeq = pgTable( 'users', { - id: integer('id').generatedByDefaultAsIdentity(customSequence), - id2: integer('id').generatedAlwaysAsIdentity(customSequenceSchema), + id: integer('id').generatedByDefaultAsIdentity(), + id2: integer('id').generatedAlwaysAsIdentity(), }, ); From b3c3f7c6a5c297353572a4f4799fed495ff2334b Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Mon, 24 Jun 2024 13:16:28 +0300 Subject: [PATCH 27/40] identity columns has default by default --- drizzle-orm/src/pg-core/columns/int.common.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index 6816c6e32..eec86ffb5 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -32,6 +32,8 @@ export abstract class PgIntColumnBaseBuilder< }; } + this.config.hasDefault = true; + return this as any; } @@ -51,6 +53,8 @@ export abstract class PgIntColumnBaseBuilder< }; } + this.config.hasDefault = true; + return this as any; } } From 85c8008682386630395dc99d1ff6bcd3afcee91a Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Mon, 24 Jun 2024 13:19:00 +0300 Subject: [PATCH 28/40] Make not null for identity --- drizzle-orm/src/pg-core/columns/int.common.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drizzle-orm/src/pg-core/columns/int.common.ts b/drizzle-orm/src/pg-core/columns/int.common.ts index eec86ffb5..07c26ba9e 100644 --- a/drizzle-orm/src/pg-core/columns/int.common.ts +++ b/drizzle-orm/src/pg-core/columns/int.common.ts @@ -33,6 +33,7 @@ export abstract class PgIntColumnBaseBuilder< } this.config.hasDefault = true; + this.config.notNull = true; return this as any; } @@ -54,6 +55,7 @@ export abstract class PgIntColumnBaseBuilder< } this.config.hasDefault = true; + this.config.notNull = true; return this as any; } From 0fdaa9e3bb1f54a36d43dde26670fdbd140e9a99 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 25 Jun 2024 19:00:08 +0300 Subject: [PATCH 29/40] Update compatibilityVersion to 8 --- drizzle-orm/src/version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/version.ts b/drizzle-orm/src/version.ts index 0c11937c8..d670a0575 100644 --- a/drizzle-orm/src/version.ts +++ b/drizzle-orm/src/version.ts @@ -1,4 +1,4 @@ // @ts-ignore - imported using Rollup json plugin export { version as npmVersion } from '../package.json'; // In version 7, we changed the PostgreSQL indexes API -export const compatibilityVersion = 7; +export const compatibilityVersion = 8; From e7cf33810834fc6d5413193a32eb5ce0487168b3 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 25 Jun 2024 20:41:54 +0300 Subject: [PATCH 30/40] Get version 7 back --- drizzle-orm/src/version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/version.ts b/drizzle-orm/src/version.ts index d670a0575..0c11937c8 100644 --- a/drizzle-orm/src/version.ts +++ b/drizzle-orm/src/version.ts @@ -1,4 +1,4 @@ // @ts-ignore - imported using Rollup json plugin export { version as npmVersion } from '../package.json'; // In version 7, we changed the PostgreSQL indexes API -export const compatibilityVersion = 8; +export const compatibilityVersion = 7; From d0d6436d1a16c9a8e8853d2d8b7aae56fe0994f9 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 27 Jun 2024 12:44:34 +0300 Subject: [PATCH 31/40] Fix column default for sqlite --- drizzle-orm/src/column.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index 1dfe21793..1f9c9e5c1 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -113,7 +113,7 @@ export abstract class Column< // ** @internal */ shouldDisableInsert(): boolean { - return this.config.generatedIdentity !== undefined && this.config.generatedIdentity.type !== 'byDefault'; + return this.config.generated !== undefined && this.config.generated.type !== 'byDefault'; } } From 5cc2ae0f3f39b91ff534839209cd3010d8411223 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 27 Jun 2024 13:33:54 +0300 Subject: [PATCH 32/40] Make sequence options optional --- drizzle-orm/src/pg-core/sequence.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drizzle-orm/src/pg-core/sequence.ts b/drizzle-orm/src/pg-core/sequence.ts index 6817f4db4..a437ba5d3 100644 --- a/drizzle-orm/src/pg-core/sequence.ts +++ b/drizzle-orm/src/pg-core/sequence.ts @@ -22,7 +22,7 @@ export class PgSequence { export function pgSequence( name: string, - options: PgSequenceOptions, + options?: PgSequenceOptions, ): PgSequence { return pgSequenceWithSchema(name, options, undefined); } @@ -30,7 +30,7 @@ export function pgSequence( /** @internal */ export function pgSequenceWithSchema( name: string, - options: PgSequenceOptions, + options?: PgSequenceOptions, schema?: string, ): PgSequence { return new PgSequence(name, options, schema); From e6a212bf550f5b7a71dd7367148888e001d0b6bd Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 2 Jul 2024 11:25:51 +0300 Subject: [PATCH 33/40] Add beta release notes --- changelogs/drizzle-orm/0.32.0-beta.md | 184 +++++++++++++++++++++++++ drizzle-orm/type-tests/mysql/tables.ts | 8 ++ drizzle-orm/type-tests/pg/tables.ts | 8 ++ 3 files changed, 200 insertions(+) create mode 100644 changelogs/drizzle-orm/0.32.0-beta.md diff --git a/changelogs/drizzle-orm/0.32.0-beta.md b/changelogs/drizzle-orm/0.32.0-beta.md new file mode 100644 index 000000000..bbf7859e7 --- /dev/null +++ b/changelogs/drizzle-orm/0.32.0-beta.md @@ -0,0 +1,184 @@ +# Preview release for `drizzle-orm@0.32.0` and `drizzle-kit@0.23.0` + +> It's not mandatory to upgrade both packages, but if you want to use the new features in both queries and migrations, you will need to upgrade both packages + +## New Features + +### 🎉 PostgreSQL Sequences + +You can now specify sequences in Postgres within any schema you need and define all the available properties + +##### **Example** + +```ts +import { pgSchema, pgSequence } from "drizzle-orm/pg-core"; + +// No params specified +export const customSequence = pgSequence("name"); + +// Sequence with params +export const customSequence = pgSequence("name", { + startWith: 100, + maxValue: 10000, + minValue: 100, + cycle: true, + cache: 10, + increment: 2 +}); + +// Sequence in custom schema +export const customSchema = pgSchema('custom_schema'); + +export const customSequence = customSchema.sequence("name"); +``` + +### 🎉 PostgreSQL Identity Columns + +[Source](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_serial): As mentioned, the `serial` type in Postgres is outdated and should be deprecated. Ideally, you should not use it. `Identity columns` are the recommended way to specify sequences in your schema, which is why we are introducing the `identity columns` feature + +##### **Example** + +```ts +import { pgTable, integer, text } from 'drizzle-orm/pg-core' + +export const ingredients = pgTable("ingredients", { + id: integer("id").primaryKey().generatedAlwaysAsIdentity({ startWith: 1000 }), + name: text("name").notNull(), + description: text("description"), +}); +``` + +You can specify all properties available for sequences in the `.generatedAlwaysAsIdentity()` function. Additionally, you can specify custom names for these sequences + +PostgreSQL docs [reference](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-IDENTITY). + +### 🎉 PostgreSQL Generated Columns + +You can now specify generated columns on any column supported by PostgreSQL to use with generated columns + +##### **Example** with generated column for `tsvector` + +> Note: we will add `tsVector` column type before latest release + +```ts +import { SQL, sql } from "drizzle-orm"; +import { customType, index, integer, pgTable, text } from "drizzle-orm/pg-core"; + +const tsVector = customType<{ data: string }>({ + dataType() { + return "tsvector"; + }, +}); + +export const test = pgTable( + "test", + { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + content: text("content"), + contentSearch: tsVector("content_search", { + dimensions: 3, + }).generatedAlwaysAs( + (): SQL => sql`to_tsvector('english', ${test.content})` + ), + }, + (t) => ({ + idx: index("idx_content_search").using("gin", t.contentSearch), + }) +); +``` + +In case you don't need to reference any columns from your table, you can use just `sql` template or a `string` + +```ts +export const users = pgTable("users", { + id: integer("id"), + name: text("name"), + generatedName: text("gen_name").generatedAlwaysAs(sql`hello world!`), + generatedName1: text("gen_name1").generatedAlwaysAs("hello world!"), +}), +``` + +### 🎉 MySQL Generated Columns + +You can now specify generated columns on any column supported by MySQL to use with generated columns + +You can specify both `stored` and `virtual` options, for more info you can check [MySQL docs](https://dev.mysql.com/doc/refman/8.4/en/create-table-generated-columns.html) + +Also MySQL has a few limitation for such columns usage, which is described [here](https://dev.mysql.com/doc/refman/8.4/en/alter-table-generated-columns.html) + +Drizzle Kit will also have limitations for `push` command: + +1. You can't change the generated constraint expression and type using `push`. Drizzle-kit will ignore this change. To make it work, you would need to `drop the column`, `push`, and then `add a column with a new expression`. This was done due to the complex mapping from the database side, where the schema expression will be modified on the database side and, on introspection, we will get a different string. We can't be sure if you changed this expression or if it was changed and formatted by the database. As long as these are generated columns and `push` is mostly used for prototyping on a local database, it should be fast to `drop` and `create` generated columns. Since these columns are `generated`, all the data will be restored + +2. `generate` should have no limitations + +##### **Example** + +```ts +export const users = mysqlTable("users", { + id: int("id"), + id2: int("id2"), + name: text("name"), + generatedName: text("gen_name").generatedAlwaysAs( + (): SQL => sql`${schema2.users.name} || 'hello'`, + { mode: "stored" } + ), + generatedName1: text("gen_name1").generatedAlwaysAs( + (): SQL => sql`${schema2.users.name} || 'hello'`, + { mode: "virtual" } + ), +}), +``` + +In case you don't need to reference any columns from your table, you can use just `sql` template or a `string` in `.generatedAlwaysAs()` + +### 🎉 SQLite Generated Columns + +You can now specify generated columns on any column supported by SQLite to use with generated columns + +You can specify both `stored` and `virtual` options, for more info you can check [SQLite docs](https://www.sqlite.org/gencol.html) + +Also SQLite has a few limitation for such columns usage, which is described [here](https://www.sqlite.org/gencol.html) + +Drizzle Kit will also have limitations for `push` and `generate` command: + +1. You can't change the generated constraint expression with the stored type in an existing table. You would need to delete this table and create it again. This is due to SQLite limitations for such actions. We will handle this case in future releases (it will involve the creation of a new table with data migration). + +2. You can't add a `stored` generated expression to an existing column for the same reason as above. However, you can add a `virtual` expression to an existing column. + +3. You can't change a `stored` generated expression in an existing column for the same reason as above. However, you can change a `virtual` expression. + +4. You can't change the generated constraint type from `virtual` to `stored` for the same reason as above. However, you can change from `stored` to `virtual`. + +## New Drizzle Kit features + +### 🎉 Migrations support for all the new orm features + +PostgreSQL sequences, identity columns and generated columns for all dialects + +### 🎉 New flag `--force` for `drizzle-kit push` + +You can auto-accept all data-loss statements using the push command. It's only available in CLI parameters. Make sure you always use it if you are fine with running data-loss statements on your database + +### 🎉 New `migrations` flag `prefix` + +You can now customize migration file prefixes to make the format suitable for your migration tools: + +- `index` is the default type and will result in `0001_name.sql` file names; +- `supabase` and `timestamp` are equal and will result in `20240627123900_name.sql` file names; +- `unix` will result in unix seconds prefixes `1719481298_name.sql` file names; +- `none` will omit the prefix completely; + + +##### **Example**: Supabase migrations format +```ts +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "postgresql", + migrations: { + prefix: 'supabase' + } +}); + +``` diff --git a/drizzle-orm/type-tests/mysql/tables.ts b/drizzle-orm/type-tests/mysql/tables.ts index 4cb09965f..4b5b703b5 100644 --- a/drizzle-orm/type-tests/mysql/tables.ts +++ b/drizzle-orm/type-tests/mysql/tables.ts @@ -81,6 +81,14 @@ Expect< }, InferSelectModel> >; +Expect< + Equal<{ + id?: number; + name: string; + population?: number | null; + }, typeof cities.$inferInsert> +>; + export const customSchema = mysqlSchema('custom_schema'); export const citiesCustom = customSchema.table('cities_table', { diff --git a/drizzle-orm/type-tests/pg/tables.ts b/drizzle-orm/type-tests/pg/tables.ts index 6113a91bf..267af3672 100644 --- a/drizzle-orm/type-tests/pg/tables.ts +++ b/drizzle-orm/type-tests/pg/tables.ts @@ -125,6 +125,14 @@ export const classes = pgTable('classes_table', { subClass: text('sub_class', { enum: ['B', 'D'] }).notNull(), }); +Expect< + Equal<{ + id?: number; + class?: 'A' | 'C' | null; + subClass: 'B' | 'D'; + }, typeof classes.$inferInsert> +>; + export const network = pgTable('network_table', { inet: inet('inet').notNull(), cidr: cidr('cidr').notNull(), From 4d0047df4f8d8cc9a885a57422aa48147506479f Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Thu, 4 Jul 2024 14:17:20 +0300 Subject: [PATCH 34/40] Add returning ids to mysql insert --- drizzle-orm/src/column-builder.ts | 32 +- drizzle-orm/src/column.ts | 6 + drizzle-orm/src/mysql-core/columns/common.ts | 5 +- drizzle-orm/src/mysql-core/columns/serial.ts | 28 +- drizzle-orm/src/mysql-core/dialect.ts | 18 +- .../src/mysql-core/query-builders/insert.ts | 94 ++- drizzle-orm/src/mysql-core/session.ts | 2 + drizzle-orm/src/mysql2/session.ts | 47 +- .../src/sqlite-core/columns/integer.ts | 5 +- drizzle-orm/type-tests/mysql/insert.ts | 14 +- drizzle-orm/type-tests/mysql/tables.ts | 95 +++ drizzle-orm/type-tests/pg/array.ts | 3 + drizzle-orm/type-tests/pg/tables.ts | 84 +++ drizzle-orm/type-tests/sqlite/tables.ts | 18 + integration-tests/package.json | 4 +- .../tests/mysql-returning.test.ts | 205 ++++++ pnpm-lock.yaml | 661 +++++++++++------- 17 files changed, 1028 insertions(+), 293 deletions(-) create mode 100644 integration-tests/tests/mysql-returning.test.ts diff --git a/drizzle-orm/src/column-builder.ts b/drizzle-orm/src/column-builder.ts index e2cc8bd1e..4a19a79a9 100644 --- a/drizzle-orm/src/column-builder.ts +++ b/drizzle-orm/src/column-builder.ts @@ -58,6 +58,9 @@ export type MakeColumnConfig< driverParam: T['driverParam']; notNull: T extends { notNull: true } ? true : false; hasDefault: T extends { hasDefault: true } ? true : false; + isPrimaryKey: T extends { isPrimaryKey: true } ? true : false; + isAutoincrement: T extends { isAutoincrement: true } ? true : false; + hasRuntimeDefault: T extends { hasRuntimeDefault: true } ? true : false; enumValues: T['enumValues']; baseColumn: T extends { baseBuilder: infer U extends ColumnBuilderBase } ? BuildColumn : never; @@ -117,6 +120,24 @@ export type HasDefault = T & { }; }; +export type IsPrimaryKey = T & { + _: { + isPrimaryKey: true; + }; +}; + +export type IsAutoincrement = T & { + _: { + isAutoincrement: true; + }; +}; + +export type HasRuntimeDefault = T & { + _: { + hasRuntimeDefault: true; + }; +}; + export type $Type = T & { _: { $type: TType; @@ -223,10 +244,10 @@ export abstract class ColumnBuilder< */ $defaultFn( fn: () => (this['_'] extends { $type: infer U } ? U : this['_']['data']) | SQL, - ): HasDefault { + ): HasRuntimeDefault> { this.config.defaultFn = fn; this.config.hasDefault = true; - return this as HasDefault; + return this as HasRuntimeDefault>; } /** @@ -259,10 +280,13 @@ export abstract class ColumnBuilder< * * In SQLite, `integer primary key` implicitly makes the column auto-incrementing. */ - primaryKey(): TExtraConfig['primaryKeyHasDefault'] extends true ? HasDefault> : NotNull { + primaryKey(): TExtraConfig['primaryKeyHasDefault'] extends true ? IsPrimaryKey>> + : IsPrimaryKey> + { this.config.primaryKey = true; this.config.notNull = true; - return this as TExtraConfig['primaryKeyHasDefault'] extends true ? HasDefault> : NotNull; + return this as TExtraConfig['primaryKeyHasDefault'] extends true ? IsPrimaryKey>> + : IsPrimaryKey>; } abstract generatedAlwaysAs( diff --git a/drizzle-orm/src/column.ts b/drizzle-orm/src/column.ts index 1f9c9e5c1..e740acaa0 100644 --- a/drizzle-orm/src/column.ts +++ b/drizzle-orm/src/column.ts @@ -17,6 +17,9 @@ export interface ColumnBaseConfig< tableName: string; notNull: boolean; hasDefault: boolean; + isPrimaryKey: boolean; + isAutoincrement: boolean; + hasRuntimeDefault: boolean; } export type ColumnTypeConfig, TTypeConfig extends object> = T & { @@ -29,6 +32,9 @@ export type ColumnTypeConfig, driverParam: T['driverParam']; notNull: T['notNull']; hasDefault: T['hasDefault']; + isPrimaryKey: T['isPrimaryKey']; + isAutoincrement: T['isAutoincrement']; + hasRuntimeDefault: T['hasRuntimeDefault']; enumValues: T['enumValues']; baseColumn: T extends { baseColumn: infer U } ? U : unknown; generated: GeneratedColumnConfig | undefined; diff --git a/drizzle-orm/src/mysql-core/columns/common.ts b/drizzle-orm/src/mysql-core/columns/common.ts index a91560e74..a0a192477 100644 --- a/drizzle-orm/src/mysql-core/columns/common.ts +++ b/drizzle-orm/src/mysql-core/columns/common.ts @@ -7,6 +7,7 @@ import type { ColumnDataType, HasDefault, HasGenerated, + IsAutoincrement, MakeColumnConfig, } from '~/column-builder.ts'; import type { ColumnBaseConfig } from '~/column.ts'; @@ -133,10 +134,10 @@ export abstract class MySqlColumnBuilderWithAutoIncrement< this.config.autoIncrement = false; } - autoincrement(): HasDefault { + autoincrement(): IsAutoincrement> { this.config.autoIncrement = true; this.config.hasDefault = true; - return this as HasDefault; + return this as IsAutoincrement>; } } diff --git a/drizzle-orm/src/mysql-core/columns/serial.ts b/drizzle-orm/src/mysql-core/columns/serial.ts index da3f5d29c..0f87f0bf5 100644 --- a/drizzle-orm/src/mysql-core/columns/serial.ts +++ b/drizzle-orm/src/mysql-core/columns/serial.ts @@ -2,6 +2,8 @@ import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, HasDefault, + IsAutoincrement, + IsPrimaryKey, MakeColumnConfig, NotNull, } from '~/column-builder.ts'; @@ -10,17 +12,21 @@ import { entityKind } from '~/entity.ts'; import type { AnyMySqlTable } from '~/mysql-core/table.ts'; import { MySqlColumnBuilderWithAutoIncrement, MySqlColumnWithAutoIncrement } from './common.ts'; -export type MySqlSerialBuilderInitial = NotNull< - HasDefault< - MySqlSerialBuilder<{ - name: TName; - dataType: 'number'; - columnType: 'MySqlSerial'; - data: number; - driverParam: number; - enumValues: undefined; - generated: undefined; - }> +export type MySqlSerialBuilderInitial = IsAutoincrement< + IsPrimaryKey< + NotNull< + HasDefault< + MySqlSerialBuilder<{ + name: TName; + dataType: 'number'; + columnType: 'MySqlSerial'; + data: number; + driverParam: number; + enumValues: undefined; + generated: undefined; + }> + > + > > >; diff --git a/drizzle-orm/src/mysql-core/dialect.ts b/drizzle-orm/src/mysql-core/dialect.ts index e2e3c982e..a7cdaef54 100644 --- a/drizzle-orm/src/mysql-core/dialect.ts +++ b/drizzle-orm/src/mysql-core/dialect.ts @@ -412,7 +412,9 @@ export class MySqlDialect { return sql`${leftChunk}${operatorChunk}${rightChunk}${orderBySql}${limitSql}${offsetSql}`; } - buildInsertQuery({ table, values, ignore, onConflict }: MySqlInsertConfig): SQL { + buildInsertQuery( + { table, values, ignore, onConflict }: MySqlInsertConfig, + ): { sql: SQL; generatedIds: Record[] } { // const isSingleValue = values.length === 1; const valuesSqlList: ((SQLChunk | SQL)[] | SQL)[] = []; const columns: Record = table[Table.Symbol.Columns]; @@ -421,8 +423,11 @@ export class MySqlDialect { ); const insertOrder = colEntries.map(([, column]) => sql.identifier(column.name)); + const generatedIdsResponse: Record[] = []; for (const [valueIndex, value] of values.entries()) { + const generatedIds: Record = {}; + const valueList: (SQLChunk | SQL)[] = []; for (const [fieldName, col] of colEntries) { const colValue = value[fieldName]; @@ -430,6 +435,7 @@ export class MySqlDialect { // eslint-disable-next-line unicorn/no-negated-condition if (col.defaultFn !== undefined) { const defaultFnResult = col.defaultFn(); + generatedIds[fieldName] = defaultFnResult; const defaultValue = is(defaultFnResult, SQL) ? defaultFnResult : sql.param(defaultFnResult, col); valueList.push(defaultValue); // eslint-disable-next-line unicorn/no-negated-condition @@ -441,9 +447,14 @@ export class MySqlDialect { valueList.push(sql`default`); } } else { + if (col.defaultFn && is(colValue, Param)) { + generatedIds[fieldName] = colValue.value; + } valueList.push(colValue); } } + + generatedIdsResponse.push(generatedIds); valuesSqlList.push(valueList); if (valueIndex < values.length - 1) { valuesSqlList.push(sql`, `); @@ -456,7 +467,10 @@ export class MySqlDialect { const onConflictSql = onConflict ? sql` on duplicate key ${onConflict}` : undefined; - return sql`insert${ignoreSql} into ${table} ${insertOrder} values ${valuesSql}${onConflictSql}`; + return { + sql: sql`insert${ignoreSql} into ${table} ${insertOrder} values ${valuesSql}${onConflictSql}`, + generatedIds: generatedIdsResponse, + }; } sqlToQuery(sql: SQL, invokeSource?: 'indexes' | undefined): QueryWithTypings { diff --git a/drizzle-orm/src/mysql-core/query-builders/insert.ts b/drizzle-orm/src/mysql-core/query-builders/insert.ts index 3aa51329f..a4d5b8766 100644 --- a/drizzle-orm/src/mysql-core/query-builders/insert.ts +++ b/drizzle-orm/src/mysql-core/query-builders/insert.ts @@ -11,10 +11,14 @@ import type { } from '~/mysql-core/session.ts'; import type { MySqlTable } from '~/mysql-core/table.ts'; import { QueryPromise } from '~/query-promise.ts'; +import type { RunnableQuery } from '~/runnable-query.ts'; import type { Placeholder, Query, SQLWrapper } from '~/sql/sql.ts'; import { Param, SQL, sql } from '~/sql/sql.ts'; +import type { InferModelFromColumns } from '~/table.ts'; import { Table } from '~/table.ts'; -import { mapUpdateSet } from '~/utils.ts'; +import { mapUpdateSet, orderSelectedFields } from '~/utils.ts'; +import type { AnyMySqlColumn, MySqlColumn } from '../columns/common.ts'; +import type { SelectedFieldsOrdered } from './select.types.ts'; import type { MySqlUpdateSetSource } from './update.ts'; export interface MySqlInsertConfig { @@ -22,6 +26,7 @@ export interface MySqlInsertConfig { values: Record[]; ignore: boolean; onConflict?: SQL; + returning?: SelectedFieldsOrdered; } export type AnyMySqlInsertConfig = MySqlInsertConfig; @@ -82,8 +87,9 @@ export type MySqlInsertWithout, T['_']['excludedMethods'] | K >; @@ -91,13 +97,17 @@ export type MySqlInsertWithout = MySqlInsert< T['_']['table'], T['_']['queryResult'], - T['_']['preparedQueryHKT'] + T['_']['preparedQueryHKT'], + T['_']['returning'] >; -export type MySqlInsertPrepare = PreparedQueryKind< +export type MySqlInsertPrepare< + T extends AnyMySqlInsert, + TReturning extends Record | undefined = undefined, +> = PreparedQueryKind< T['_']['preparedQueryHKT'], PreparedQueryConfig & { - execute: QueryResultKind; + execute: TReturning extends undefined ? QueryResultKind : TReturning[]; iterator: never; }, true @@ -111,36 +121,75 @@ export type MySqlInsert< TTable extends MySqlTable = MySqlTable, TQueryResult extends QueryResultHKT = AnyQueryResultHKT, TPreparedQueryHKT extends PreparedQueryHKTBase = PreparedQueryHKTBase, -> = MySqlInsertBase; + TReturning extends Record | undefined = Record | undefined, +> = MySqlInsertBase; + +export type MySqlInsertReturning< + T extends AnyMySqlInsert, + TDynamic extends boolean, +> = MySqlInsertBase< + T['_']['table'], + T['_']['queryResult'], + T['_']['preparedQueryHKT'], + InferModelFromColumns>, + TDynamic, + T['_']['excludedMethods'] | '$returning' +>; -export type AnyMySqlInsert = MySqlInsertBase; +export type AnyMySqlInsert = MySqlInsertBase; export interface MySqlInsertBase< TTable extends MySqlTable, TQueryResult extends QueryResultHKT, TPreparedQueryHKT extends PreparedQueryHKTBase, + TReturning extends Record | undefined = undefined, TDynamic extends boolean = false, TExcludedMethods extends string = never, -> extends QueryPromise>, SQLWrapper { +> extends + QueryPromise : TReturning[]>, + RunnableQuery : TReturning[], 'mysql'>, + SQLWrapper +{ readonly _: { + readonly dialect: 'mysql'; readonly table: TTable; readonly queryResult: TQueryResult; readonly preparedQueryHKT: TPreparedQueryHKT; readonly dynamic: TDynamic; readonly excludedMethods: TExcludedMethods; + readonly returning: TReturning; + readonly result: TReturning extends undefined ? QueryResultKind : TReturning[]; }; } +export type PrimaryKeyKeys> = { + [K in keyof T]: T[K]['_']['isPrimaryKey'] extends true ? T[K]['_']['isAutoincrement'] extends true ? K + : T[K]['_']['hasRuntimeDefault'] extends true ? T[K]['_']['isPrimaryKey'] extends true ? K : never + : never + : T[K]['_']['hasRuntimeDefault'] extends true ? T[K]['_']['isPrimaryKey'] extends true ? K : never + : never; +}[keyof T]; + +export type GetPrimarySerialOrDefaultKeys> = { + [K in PrimaryKeyKeys]: T[K]; +}; + export class MySqlInsertBase< TTable extends MySqlTable, TQueryResult extends QueryResultHKT, // eslint-disable-next-line @typescript-eslint/no-unused-vars TPreparedQueryHKT extends PreparedQueryHKTBase, // eslint-disable-next-line @typescript-eslint/no-unused-vars + TReturning extends Record | undefined = undefined, + // eslint-disable-next-line @typescript-eslint/no-unused-vars TDynamic extends boolean = false, // eslint-disable-next-line @typescript-eslint/no-unused-vars TExcludedMethods extends string = never, -> extends QueryPromise> implements SQLWrapper { +> extends QueryPromise : TReturning[]> + implements + RunnableQuery : TReturning[], 'mysql'>, + SQLWrapper +{ static readonly [entityKind]: string = 'MySqlInsert'; declare protected $table: TTable; @@ -192,9 +241,24 @@ export class MySqlInsertBase< return this as any; } + $returningId(): MySqlInsertWithout< + MySqlInsertReturning, + TDynamic, + '$returningId' + > { + const returning: SelectedFieldsOrdered = []; + for (const [key, value] of Object.entries(this.config.table[Table.Symbol.Columns])) { + if (value.primary) { + returning.push({ field: value, path: [key] }); + } + } + this.config.returning = orderSelectedFields(this.config.table[Table.Symbol.Columns]); + return this as any; + } + /** @internal */ getSQL(): SQL { - return this.dialect.buildInsertQuery(this.config); + return this.dialect.buildInsertQuery(this.config).sql; } toSQL(): Query { @@ -202,11 +266,15 @@ export class MySqlInsertBase< return rest; } - prepare(): MySqlInsertPrepare { + prepare(): MySqlInsertPrepare { + const { sql, generatedIds } = this.dialect.buildInsertQuery(this.config); return this.session.prepareQuery( - this.dialect.sqlToQuery(this.getSQL()), + this.dialect.sqlToQuery(sql), + undefined, undefined, - ) as MySqlInsertPrepare; + generatedIds, + this.config.returning, + ) as MySqlInsertPrepare; } override execute: ReturnType['execute'] = (placeholderValues) => { diff --git a/drizzle-orm/src/mysql-core/session.ts b/drizzle-orm/src/mysql-core/session.ts index 528782d7b..0b77f2940 100644 --- a/drizzle-orm/src/mysql-core/session.ts +++ b/drizzle-orm/src/mysql-core/session.ts @@ -72,6 +72,8 @@ export abstract class MySqlSession< query: Query, fields: SelectedFieldsOrdered | undefined, customResultMapper?: (rows: unknown[][]) => T['execute'], + generatedIds?: Record[], + returningIds?: SelectedFieldsOrdered, ): PreparedQueryKind; execute(query: SQL): Promise { diff --git a/drizzle-orm/src/mysql2/session.ts b/drizzle-orm/src/mysql2/session.ts index f29e11d6f..402137276 100644 --- a/drizzle-orm/src/mysql2/session.ts +++ b/drizzle-orm/src/mysql2/session.ts @@ -10,7 +10,8 @@ import type { RowDataPacket, } from 'mysql2/promise'; import { once } from 'node:events'; -import { entityKind } from '~/entity.ts'; +import { Column } from '~/column'; +import { entityKind, is } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { NoopLogger } from '~/logger.ts'; import type { MySqlDialect } from '~/mysql-core/dialect.ts'; @@ -27,7 +28,8 @@ import { type QueryResultHKT, } from '~/mysql-core/session.ts'; import type { RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; -import { fillPlaceholders, type Query, type SQL, sql } from '~/sql/sql.ts'; +import { fillPlaceholders, sql } from '~/sql/sql.ts'; +import type { Query, SQL } from '~/sql/sql.ts'; import { type Assume, mapResultRow } from '~/utils.ts'; export type MySql2Client = Pool | Connection; @@ -51,6 +53,10 @@ export class MySql2PreparedQuery extends Prepared private logger: Logger, private fields: SelectedFieldsOrdered | undefined, private customResultMapper?: (rows: unknown[][]) => T['execute'], + // Keys that were used in $default and the value that was generated for them + private generatedIds?: Record[], + // Keys that should be returned, it has the column with all properries + key from object + private returningIds?: SelectedFieldsOrdered, ) { super(); this.rawQuery = { @@ -80,9 +86,36 @@ export class MySql2PreparedQuery extends Prepared this.logger.logQuery(this.rawQuery.sql, params); - const { fields, client, rawQuery, query, joinsNotNullableMap, customResultMapper } = this; + const { fields, client, rawQuery, query, joinsNotNullableMap, customResultMapper, returningIds, generatedIds } = + this; if (!fields && !customResultMapper) { - return client.query(rawQuery, params); + const res = await client.query(rawQuery, params); + const insertId = res[0].insertId; + const affectedRows = res[0].affectedRows; + // for each row, I need to check keys from + if (returningIds) { + const returningResponse = []; + let j = 0; + for (let i = insertId; i < insertId + affectedRows; i++) { + for (const column of returningIds) { + const key = returningIds[0]!.path[0]!; + if (is(column.field, Column)) { + // @ts-ignore + if (column.field.primary && column.field.autoIncrement) { + returningResponse.push({ [key]: i }); + } + if (column.field.defaultFn && generatedIds) { + // generatedIds[rowIdx][key] + returningResponse.push({ [key]: generatedIds[j]![key] }); + } + } + } + j++; + } + + return returningResponse; + } + return res; } const result = await client.query(query, params); @@ -177,7 +210,11 @@ export class MySql2Session< query: Query, fields: SelectedFieldsOrdered | undefined, customResultMapper?: (rows: unknown[][]) => T['execute'], + generatedIds?: Record[], + returningIds?: SelectedFieldsOrdered, ): PreparedQueryKind { + // Add returningId fields + // Each driver gets them from response from database return new MySql2PreparedQuery( this.client, query.sql, @@ -185,6 +222,8 @@ export class MySql2Session< this.logger, fields, customResultMapper, + generatedIds, + returningIds, ) as PreparedQueryKind; } diff --git a/drizzle-orm/src/sqlite-core/columns/integer.ts b/drizzle-orm/src/sqlite-core/columns/integer.ts index 334ab0af5..1c839837d 100644 --- a/drizzle-orm/src/sqlite-core/columns/integer.ts +++ b/drizzle-orm/src/sqlite-core/columns/integer.ts @@ -3,6 +3,7 @@ import type { ColumnBuilderRuntimeConfig, ColumnDataType, HasDefault, + IsPrimaryKey, MakeColumnConfig, NotNull, } from '~/column-builder.ts'; @@ -35,12 +36,12 @@ export abstract class SQLiteBaseIntegerBuilder< this.config.autoIncrement = false; } - override primaryKey(config?: PrimaryKeyConfig): HasDefault> { + override primaryKey(config?: PrimaryKeyConfig): IsPrimaryKey>> { if (config?.autoIncrement) { this.config.autoIncrement = true; } this.config.hasDefault = true; - return super.primaryKey() as HasDefault>; + return super.primaryKey() as IsPrimaryKey>>; } /** @internal */ diff --git a/drizzle-orm/type-tests/mysql/insert.ts b/drizzle-orm/type-tests/mysql/insert.ts index edc48d5a2..b354410bd 100644 --- a/drizzle-orm/type-tests/mysql/insert.ts +++ b/drizzle-orm/type-tests/mysql/insert.ts @@ -1,11 +1,22 @@ import type { Equal } from 'type-tests/utils.ts'; import { Expect } from 'type-tests/utils.ts'; -import { int, type MySqlInsert, mysqlTable, text } from '~/mysql-core/index.ts'; +import { int, mysqlTable, text } from '~/mysql-core/index.ts'; +import type { MySqlInsert } from '~/mysql-core/index.ts'; import type { MySqlRawQueryResult } from '~/mysql2/index.ts'; import { sql } from '~/sql/sql.ts'; import { db } from './db.ts'; import { users } from './tables.ts'; +const mysqlInsertReturning = await db.insert(users).values({ + // ^? + homeCity: 1, + class: 'A', + age1: 1, + enumCol: 'a', +}).$returningId(); + +Expect>; + const insert = await db.insert(users).values({ homeCity: 1, class: 'A', @@ -110,6 +121,7 @@ Expect>; const qbBase = db.insert(users).values({ age1: 0, class: 'A', enumCol: 'a', homeCity: 0 }).$dynamic(); const qb = dynamic(qbBase); const result = await qb; + Expect>; } diff --git a/drizzle-orm/type-tests/mysql/tables.ts b/drizzle-orm/type-tests/mysql/tables.ts index 4b5b703b5..35c86c7e6 100644 --- a/drizzle-orm/type-tests/mysql/tables.ts +++ b/drizzle-orm/type-tests/mysql/tables.ts @@ -73,6 +73,62 @@ export const cities = mysqlTable('cities_table', { citiesNameIdx: index('citiesNameIdx').on(cities.id), })); +Expect< + Equal< + { + id: MySqlColumn<{ + name: 'id'; + tableName: 'cities_table'; + dataType: 'number'; + columnType: 'MySqlSerial'; + data: number; + driverParam: number; + notNull: true; + hasDefault: true; + isPrimaryKey: true; + enumValues: undefined; + baseColumn: never; + generated: undefined; + isAutoincrement: true; + hasRuntimeDefault: false; + }, object>; + name: MySqlColumn<{ + name: 'name_db'; + tableName: 'cities_table'; + dataType: 'string'; + columnType: 'MySqlText'; + data: string; + driverParam: string; + notNull: true; + hasDefault: false; + isPrimaryKey: false; + enumValues: [string, ...string[]]; + baseColumn: never; + generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + }, object>; + population: MySqlColumn<{ + name: 'population'; + tableName: 'cities_table'; + dataType: 'number'; + columnType: 'MySqlInt'; + data: number; + driverParam: string | number; + notNull: false; + hasDefault: true; + isPrimaryKey: false; + enumValues: undefined; + baseColumn: never; + generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + }, object>; + }, + typeof cities._.columns + > +>; + Expect< Equal<{ id: number; @@ -144,6 +200,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: true; + hasRuntimeDefault: false; }>; cityId: MySqlColumn<{ name: 'id'; @@ -157,6 +216,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: true; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -195,6 +257,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: true; + hasRuntimeDefault: false; }>; cityId: MySqlColumn<{ name: 'id'; @@ -208,6 +273,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: true; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -244,6 +312,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -257,6 +328,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -293,6 +367,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -306,6 +383,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -334,6 +414,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -347,6 +430,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -375,6 +461,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: MySqlColumn<{ name: 'city_id'; @@ -388,6 +477,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -419,6 +511,9 @@ Expect< baseColumn: never; dialect: 'mysql'; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }, Simplify['_']> > diff --git a/drizzle-orm/type-tests/pg/array.ts b/drizzle-orm/type-tests/pg/array.ts index 87ba3e3d0..3961e92d0 100644 --- a/drizzle-orm/type-tests/pg/array.ts +++ b/drizzle-orm/type-tests/pg/array.ts @@ -21,6 +21,9 @@ import { integer, pgTable } from '~/pg-core/index.ts'; enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; } >, typeof table['a']['_']['baseColumn'] diff --git a/drizzle-orm/type-tests/pg/tables.ts b/drizzle-orm/type-tests/pg/tables.ts index 267af3672..5faca5d7b 100644 --- a/drizzle-orm/type-tests/pg/tables.ts +++ b/drizzle-orm/type-tests/pg/tables.ts @@ -202,6 +202,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -215,6 +218,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -255,6 +261,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -268,6 +277,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -306,6 +318,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -319,6 +334,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -357,6 +375,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -370,6 +391,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -398,6 +422,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -411,6 +438,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -439,6 +469,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -452,6 +485,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers @@ -495,6 +531,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -508,6 +547,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers2 @@ -551,6 +593,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -564,6 +609,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers2 @@ -605,6 +653,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -618,6 +669,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers2 @@ -659,6 +713,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -672,6 +729,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers2 @@ -700,6 +760,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -713,6 +776,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers2 @@ -741,6 +807,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; cityId: PgColumn<{ tableName: 'new_yorkers'; @@ -754,6 +823,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }>, typeof newYorkers2 @@ -857,6 +929,9 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; name: PgColumn<{ tableName: 'cities_table'; @@ -870,6 +945,9 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); notNull: true; baseColumn: never; generated: undefined; + isPrimaryKey: true; + isAutoincrement: false; + hasRuntimeDefault: false; }>; role: PgColumn<{ tableName: 'cities_table'; @@ -883,6 +961,9 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); notNull: true; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; population: PgColumn<{ tableName: 'cities_table'; @@ -896,6 +977,9 @@ await db.refreshMaterializedView(newYorkers2).withNoData().concurrently(); enumValues: undefined; baseColumn: never; generated: undefined; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; }>; }; }>, diff --git a/drizzle-orm/type-tests/sqlite/tables.ts b/drizzle-orm/type-tests/sqlite/tables.ts index bc0a07736..01419b27a 100644 --- a/drizzle-orm/type-tests/sqlite/tables.ts +++ b/drizzle-orm/type-tests/sqlite/tables.ts @@ -167,6 +167,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + isPrimaryKey: true; }>; cityId: SQLiteColumn<{ name: 'id'; @@ -180,6 +183,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + isPrimaryKey: true; }>; }>, typeof newYorkers @@ -212,6 +218,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + isPrimaryKey: false; }>; cityId: SQLiteColumn<{ name: 'city_id'; @@ -225,6 +234,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + isPrimaryKey: false; }>; }>, typeof newYorkers @@ -253,6 +265,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + isPrimaryKey: false; }>; cityId: SQLiteColumn<{ name: 'city_id'; @@ -266,6 +281,9 @@ Expect< enumValues: undefined; baseColumn: never; generated: undefined; + isAutoincrement: false; + hasRuntimeDefault: false; + isPrimaryKey: false; }>; }>, typeof newYorkers diff --git a/integration-tests/package.json b/integration-tests/package.json index c5fb6a598..35b4067a5 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -13,7 +13,8 @@ }, "ava": { "files": [ - "tests/**/*.test.{ts,cts,mts,js,cjs,mjs}", + "!tests/**/*.test.{ts,cts,mts,js,cjs,mjs}", + "tests/mysql-returning.test.ts", "!tests/imports.test.mjs", "!tests/imports.test.cjs", "!tests/awsdatapi.alltypes.test.ts", @@ -47,6 +48,7 @@ "devDependencies": { "@neondatabase/serverless": "0.9.0", "@originjs/vite-plugin-commonjs": "^1.0.3", + "@paralleldrive/cuid2": "^2.2.2", "@types/axios": "^0.14.0", "@types/better-sqlite3": "^7.6.4", "@types/dockerode": "^3.3.18", diff --git a/integration-tests/tests/mysql-returning.test.ts b/integration-tests/tests/mysql-returning.test.ts new file mode 100644 index 000000000..557d8a756 --- /dev/null +++ b/integration-tests/tests/mysql-returning.test.ts @@ -0,0 +1,205 @@ +import 'dotenv/config'; + +import type { TestFn } from 'ava'; +import anyTest from 'ava'; +import Docker from 'dockerode'; +import { DefaultLogger, sql } from 'drizzle-orm'; +import { boolean, json, mysqlTable, serial, text, timestamp, varchar } from 'drizzle-orm/mysql-core'; +import type { MySql2Database } from 'drizzle-orm/mysql2'; +import { drizzle } from 'drizzle-orm/mysql2'; +import getPort from 'get-port'; +import * as mysql from 'mysql2/promise'; +import { v4 as uuid } from 'uuid'; + +const ENABLE_LOGGING = false; + +const usersTable = mysqlTable('userstest', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + verified: boolean('verified').notNull().default(false), + jsonb: json('jsonb').$type(), + createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), +}); + +interface Context { + docker: Docker; + mysqlContainer: Docker.Container; + db: MySql2Database; + client: mysql.Connection; +} + +const test = anyTest as TestFn; + +async function createDockerDB(ctx: Context): Promise { + const docker = (ctx.docker = new Docker()); + const port = await getPort({ port: 3306 }); + const image = 'mysql:8'; + + const pullStream = await docker.pull(image); + await new Promise((resolve, reject) => + docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) + ); + + ctx.mysqlContainer = await docker.createContainer({ + Image: image, + Env: ['MYSQL_ROOT_PASSWORD=mysql', 'MYSQL_DATABASE=drizzle'], + name: `drizzle-integration-tests-${uuid()}`, + HostConfig: { + AutoRemove: true, + PortBindings: { + '3306/tcp': [{ HostPort: `${port}` }], + }, + }, + }); + + await ctx.mysqlContainer.start(); + + return `mysql://root:mysql@127.0.0.1:${port}/drizzle`; +} + +test.before(async (t) => { + const ctx = t.context; + const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB(ctx); + + const sleep = 1000; + let timeLeft = 20000; + let connected = false; + let lastError: unknown | undefined; + do { + try { + ctx.client = await mysql.createConnection(connectionString); + await ctx.client.connect(); + connected = true; + break; + } catch (e) { + lastError = e; + await new Promise((resolve) => setTimeout(resolve, sleep)); + timeLeft -= sleep; + } + } while (timeLeft > 0); + if (!connected) { + console.error('Cannot connect to MySQL'); + await ctx.client?.end().catch(console.error); + await ctx.mysqlContainer?.stop().catch(console.error); + throw lastError; + } + ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING ? new DefaultLogger() : undefined }); +}); + +test.after.always(async (t) => { + const ctx = t.context; + await ctx.client?.end().catch(console.error); + await ctx.mysqlContainer?.stop().catch(console.error); +}); + +test.beforeEach(async (t) => { + const ctx = t.context; + await ctx.db.execute(sql`drop table if exists \`userstest\``); + await ctx.db.execute(sql`drop table if exists \`users2\``); + await ctx.db.execute(sql`drop table if exists \`cities\``); + + await ctx.db.execute( + sql` + create table \`userstest\` ( + \`id\` serial primary key, + \`name\` text not null, + \`verified\` boolean not null default false, + \`jsonb\` json, + \`created_at\` timestamp not null default now() + ) + `, + ); + + await ctx.db.execute( + sql` + create table \`users2\` ( + \`id\` serial primary key, + \`name\` text not null, + \`city_id\` int references \`cities\`(\`id\`) + ) + `, + ); + + await ctx.db.execute( + sql` + create table \`cities\` ( + \`id\` serial primary key, + \`name\` text not null + ) + `, + ); +}); + +async function setupReturningFunctionsTest(db: MySql2Database) { + await db.execute(sql`drop table if exists \`users_default_fn\``); + await db.execute( + sql` + create table \`users_default_fn\` ( + \`id\` varchar(256) primary key, + \`name\` text not null + ); + `, + ); +} + +test.serial('insert $returningId: serail as id', async (t) => { + const { db } = t.context; + + const result = await db.insert(usersTable).values({ name: 'John' }).$returningId(); + // ^? + t.deepEqual(result, [{ id: 1 }]); +}); + +test.serial('insert $returningId: serail as id, batch insert', async (t) => { + const { db } = t.context; + + const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); + // ^? + t.deepEqual(result, [{ id: 1 }, { id: 2 }]); +}); + +test.serial('insert $returningId: $default as primary key', async (t) => { + const { db } = t.context; + + const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; + let iterator = 0; + + const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { + const value = uniqueKeys[iterator]!; + iterator++; + return value; + }), + name: text('name').notNull(), + }); + + await setupReturningFunctionsTest(db); + + const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]) + // ^? + .$returningId(); + t.deepEqual(result, [{ customId: 'ao865jf3mcmkfkk8o5ri495z' }, { customId: 'dyqs529eom0iczo2efxzbcut' }]); +}); + +test.serial('insert $returningId: $default as primary key with value', async (t) => { + const { db } = t.context; + + const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; + let iterator = 0; + + const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { + const value = uniqueKeys[iterator]!; + iterator++; + return value; + }), + name: text('name').notNull(), + }); + + await setupReturningFunctionsTest(db); + + const result = await db.insert(usersTableDefFn).values([{ name: 'John', customId: 'test' }, { name: 'John1' }]) + // ^? + .$returningId(); + t.deepEqual(result, [{ customId: 'test' }, { customId: 'ao865jf3mcmkfkk8o5ri495z' }]); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50905be4a..ffca74e58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,19 +15,19 @@ importers: devDependencies: '@arethetypeswrong/cli': specifier: ^0.12.1 - version: 0.12.1 + version: 0.12.1(encoding@0.1.13) '@trivago/prettier-plugin-sort-imports': specifier: ^4.2.0 version: 4.2.0(prettier@3.0.3) '@typescript-eslint/eslint-plugin': specifier: ^6.7.3 - version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/experimental-utils': specifier: ^5.62.0 - version: 5.62.0(eslint@8.50.0)(typescript@5.2.2) + version: 5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/parser': specifier: ^6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) bun-types: specifier: ^1.0.3 version: 1.0.3 @@ -45,7 +45,7 @@ importers: version: link:drizzle-orm/dist drizzle-orm-old: specifier: npm:drizzle-orm@^0.27.2 - version: drizzle-orm@0.27.2(bun-types@1.0.3) + version: drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.549.0)(@cloudflare/workers-types@4.20230904.0)(@libsql/client@0.5.6(encoding@0.1.13))(@neondatabase/serverless@0.9.0)(@opentelemetry/api@1.4.1)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.4)(@types/pg@8.10.1)(@types/sql.js@1.4.4)(@vercel/postgres@0.8.0)(better-sqlite3@8.4.0)(bun-types@1.0.3)(knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)))(kysely@0.25.0)(mysql2@3.3.3)(pg@8.11.0)(postgres@3.3.5)(sql.js@1.8.0)(sqlite3@5.1.6(encoding@0.1.13)) eslint: specifier: ^8.50.0 version: 8.50.0 @@ -54,7 +54,7 @@ importers: version: link:eslint/eslint-plugin-drizzle eslint-plugin-import: specifier: ^2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0) eslint-plugin-no-instanceof: specifier: ^1.0.1 version: 1.0.1 @@ -63,7 +63,7 @@ importers: version: 48.0.1(eslint@8.50.0) eslint-plugin-unused-imports: specifier: ^3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@6.7.3)(eslint@8.50.0) + version: 3.0.0(@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0) glob: specifier: ^10.3.10 version: 10.3.10 @@ -75,10 +75,10 @@ importers: version: 0.23.4 resolve-tspaths: specifier: ^0.8.16 - version: 0.8.16(typescript@5.2.2) + version: 0.8.16(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) tsup: specifier: ^7.2.0 - version: 7.2.0(typescript@5.2.2) + version: 7.2.0(postcss@8.4.38)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) turbo: specifier: ^1.10.14 version: 1.10.14 @@ -99,13 +99,13 @@ importers: version: 0.1.1 '@libsql/client': specifier: ^0.5.6 - version: 0.5.6 + version: 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@neondatabase/serverless': specifier: ^0.9.0 version: 0.9.0 '@op-engineering/op-sqlite': specifier: ^2.0.16 - version: 2.0.16(react-native@0.73.6)(react@18.2.0) + version: 2.0.16(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3))(react@18.2.0) '@opentelemetry/api': specifier: ^1.4.1 version: 1.4.1 @@ -138,7 +138,7 @@ importers: version: 0.8.0 '@xata.io/client': specifier: ^0.29.3 - version: 0.29.3(typescript@5.2.2) + version: 0.29.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) better-sqlite3: specifier: ^8.4.0 version: 8.4.0 @@ -150,10 +150,10 @@ importers: version: 10.1.0 expo-sqlite: specifier: ^13.2.0 - version: 13.2.0(expo@50.0.14) + version: 13.2.0(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) knex: specifier: ^2.4.2 - version: 2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6) + version: 2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)) kysely: specifier: ^0.25.0 version: 0.25.0 @@ -174,7 +174,7 @@ importers: version: 1.8.0 sqlite3: specifier: ^5.1.2 - version: 5.1.6 + version: 5.1.6(encoding@0.1.13) tslib: specifier: ^2.5.2 version: 2.5.2 @@ -183,10 +183,10 @@ importers: version: 3.12.7 vite-tsconfig-paths: specifier: ^4.2.0 - version: 4.2.0(typescript@5.2.2)(vite@4.3.9) + version: 4.2.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))(vite@4.3.9(@types/node@20.2.5)(terser@5.30.3)) vitest: specifier: ^0.31.4 - version: 0.31.4(@vitest/ui@0.31.4) + version: 0.31.4(@vitest/ui@0.31.4)(terser@5.30.3) zod: specifier: ^3.20.2 version: 3.21.4 @@ -201,7 +201,7 @@ importers: version: 0.4.1(rollup@3.27.2) '@rollup/plugin-typescript': specifier: ^11.1.0 - version: 11.1.1(rollup@3.27.2)(typescript@5.2.2) + version: 11.1.1(rollup@3.27.2)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@sinclair/typebox': specifier: ^0.29.6 version: 0.29.6 @@ -237,7 +237,7 @@ importers: version: 0.4.1(rollup@3.27.2) '@rollup/plugin-typescript': specifier: ^11.1.0 - version: 11.1.1(rollup@3.27.2)(typescript@5.2.2) + version: 11.1.1(rollup@3.27.2)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@types/node': specifier: ^18.15.10 version: 18.15.10 @@ -273,7 +273,7 @@ importers: version: 0.4.1(rollup@3.20.7) '@rollup/plugin-typescript': specifier: ^11.1.0 - version: 11.1.0(rollup@3.20.7)(typescript@5.2.2) + version: 11.1.0(rollup@3.20.7)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@types/node': specifier: ^18.15.10 version: 18.15.10 @@ -309,13 +309,13 @@ importers: version: 20.10.1 '@typescript-eslint/parser': specifier: ^6.10.0 - version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/rule-tester': specifier: ^6.10.0 - version: 6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2) + version: 6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/utils': specifier: ^6.10.0 - version: 6.10.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) cpy-cli: specifier: ^5.0.0 version: 5.0.0 @@ -327,7 +327,7 @@ importers: version: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) vitest: specifier: ^0.34.6 - version: 0.34.6 + version: 0.34.6(@vitest/ui@0.31.4)(terser@5.30.3) integration-tests: dependencies: @@ -342,7 +342,7 @@ importers: version: 0.1.1 '@libsql/client': specifier: ^0.5.6 - version: 0.5.6 + version: 0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3) '@miniflare/d1': specifier: ^2.14.2 version: 2.14.2 @@ -363,7 +363,7 @@ importers: version: 0.3.0 '@xata.io/client': specifier: ^0.29.3 - version: 0.29.3(typescript@5.2.2) + version: 0.29.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) better-sqlite3: specifier: ^8.4.0 version: 8.4.0 @@ -405,7 +405,7 @@ importers: version: 1.8.0 sqlite3: specifier: ^5.1.4 - version: 5.1.6 + version: 5.1.6(encoding@0.1.13) sst: specifier: ^3.0.4 version: 3.0.4 @@ -417,7 +417,7 @@ importers: version: 0.5.6 vitest: specifier: ^0.31.4 - version: 0.31.4(@vitest/ui@0.31.4) + version: 0.31.4(@vitest/ui@0.31.4)(terser@5.30.3) zod: specifier: ^3.20.2 version: 3.21.4 @@ -428,6 +428,9 @@ importers: '@originjs/vite-plugin-commonjs': specifier: ^1.0.3 version: 1.0.3 + '@paralleldrive/cuid2': + specifier: ^2.2.2 + version: 2.2.2 '@types/axios': specifier: ^0.14.0 version: 0.14.0 @@ -466,10 +469,10 @@ importers: version: 3.12.7 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@20.2.5) + version: 4.3.9(@types/node@20.2.5)(terser@5.30.3) vite-tsconfig-paths: specifier: ^4.2.0 - version: 4.2.0(typescript@5.2.2)(vite@4.3.9) + version: 4.2.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))(vite@4.3.9(@types/node@20.2.5)(terser@5.30.3)) zx: specifier: ^7.2.2 version: 7.2.2 @@ -2176,6 +2179,10 @@ packages: '@neondatabase/serverless@0.9.0': resolution: {integrity: sha512-mmJnUAzlzvxNSZuuhI6kgJjH+JgFdBMYUWxihtq/nj0Tjt+Y5UU3W+SvRFoucnd5NObYkuLYQzk+zV5DGFKGJg==} + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2209,6 +2216,9 @@ packages: '@originjs/vite-plugin-commonjs@1.0.3': resolution: {integrity: sha512-KuEXeGPptM2lyxdIEJ4R11+5ztipHoE7hy8ClZt3PYaOVQ/pyngd2alaSrPnwyFeOW1UagRBaQ752aA1dTMdOQ==} + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -5401,7 +5411,6 @@ packages: libsql@0.3.10: resolution: {integrity: sha512-/8YMTbwWFPmrDWY+YFK3kYqVPFkMgQre0DGmBaOmjogMdSe+7GHm1/q9AZ61AWkEub/vHmi+bA4tqIzVhKnqzg==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lighthouse-logger@1.4.2: @@ -7039,9 +7048,6 @@ packages: sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} - peerDependenciesMeta: - node-gyp: - optional: true sqlstring@2.3.3: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} @@ -8049,23 +8055,23 @@ snapshots: '@andrewbranch/untar.js@1.0.2': {} - '@arethetypeswrong/cli@0.12.1': + '@arethetypeswrong/cli@0.12.1(encoding@0.1.13)': dependencies: - '@arethetypeswrong/core': 0.12.1 + '@arethetypeswrong/core': 0.12.1(encoding@0.1.13) chalk: 4.1.2 cli-table3: 0.6.3 commander: 10.0.1 marked: 5.1.2 marked-terminal: 5.2.0(marked@5.1.2) - node-fetch: 2.6.11 + node-fetch: 2.6.11(encoding@0.1.13) semver: 7.5.4 transitivePeerDependencies: - encoding - '@arethetypeswrong/core@0.12.1': + '@arethetypeswrong/core@0.12.1(encoding@0.1.13)': dependencies: '@andrewbranch/untar.js': 1.0.2 - fetch-ponyfill: 7.1.0 + fetch-ponyfill: 7.1.0(encoding@0.1.13) fflate: 0.7.4 semver: 7.5.4 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) @@ -10098,7 +10104,7 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 - '@expo/cli@0.17.8(@react-native/babel-preset@0.73.21)(expo-modules-autolinking@1.10.3)': + '@expo/cli@0.17.8(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.10.3)(utf-8-validate@6.0.3)': dependencies: '@babel/runtime': 7.24.4 '@expo/code-signing-certificates': 0.0.5 @@ -10106,17 +10112,17 @@ snapshots: '@expo/config-plugins': 7.8.4 '@expo/devcert': 1.1.0 '@expo/env': 0.2.2 - '@expo/image-utils': 0.4.1 + '@expo/image-utils': 0.4.1(encoding@0.1.13) '@expo/json-file': 8.3.0 - '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21) + '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))) '@expo/osascript': 2.1.0 '@expo/package-manager': 1.4.2 '@expo/plist': 0.1.0 - '@expo/prebuild-config': 6.7.4(expo-modules-autolinking@1.10.3) - '@expo/rudder-sdk-node': 1.1.1 + '@expo/prebuild-config': 6.7.4(encoding@0.1.13)(expo-modules-autolinking@1.10.3) + '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) '@expo/spawn-async': 1.5.0 '@expo/xcpretty': 4.3.1 - '@react-native/dev-middleware': 0.73.8 + '@react-native/dev-middleware': 0.73.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@urql/core': 2.3.6(graphql@15.8.0) '@urql/exchange-retry': 0.3.0(graphql@15.8.0) accepts: 1.3.8 @@ -10147,7 +10153,7 @@ snapshots: md5hex: 1.0.0 minimatch: 3.1.2 minipass: 3.3.6 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) node-forge: 1.3.1 npm-package-arg: 7.0.0 open: 8.4.2 @@ -10270,14 +10276,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/image-utils@0.4.1': + '@expo/image-utils@0.4.1(encoding@0.1.13)': dependencies: '@expo/spawn-async': 1.5.0 chalk: 4.1.2 fs-extra: 9.0.0 getenv: 1.0.0 jimp-compact: 0.16.1 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) parse-png: 2.1.0 resolve-from: 5.0.0 semver: 7.3.2 @@ -10291,7 +10297,7 @@ snapshots: json5: 2.2.3 write-file-atomic: 2.4.3 - '@expo/metro-config@0.17.6(@react-native/babel-preset@0.73.21)': + '@expo/metro-config@0.17.6(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))': dependencies: '@babel/core': 7.24.4 '@babel/generator': 7.24.4 @@ -10301,7 +10307,7 @@ snapshots: '@expo/env': 0.2.2 '@expo/json-file': 8.3.0 '@expo/spawn-async': 1.7.2 - '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4) + '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) babel-preset-fbjs: 3.4.0(@babel/core@7.24.4) chalk: 4.1.2 debug: 4.3.4 @@ -10343,12 +10349,12 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 14.0.0 - '@expo/prebuild-config@6.7.4(expo-modules-autolinking@1.10.3)': + '@expo/prebuild-config@6.7.4(encoding@0.1.13)(expo-modules-autolinking@1.10.3)': dependencies: '@expo/config': 8.5.4 '@expo/config-plugins': 7.8.4 '@expo/config-types': 50.0.0 - '@expo/image-utils': 0.4.1 + '@expo/image-utils': 0.4.1(encoding@0.1.13) '@expo/json-file': 8.3.0 debug: 4.3.4 expo-modules-autolinking: 1.10.3 @@ -10360,13 +10366,13 @@ snapshots: - encoding - supports-color - '@expo/rudder-sdk-node@1.1.1': + '@expo/rudder-sdk-node@1.1.1(encoding@0.1.13)': dependencies: '@expo/bunyan': 4.0.0 '@segment/loosely-validate-event': 2.0.0 fetch-retry: 4.1.1 md5: 2.3.0 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) remove-trailing-slash: 0.1.1 uuid: 8.3.2 transitivePeerDependencies: @@ -10533,10 +10539,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@libsql/client@0.5.6': + '@libsql/client@0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@libsql/core': 0.5.6 - '@libsql/hrana-client': 0.5.6 + '@libsql/hrana-client': 0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3) js-base64: 3.7.5 libsql: 0.3.10 transitivePeerDependencies: @@ -10544,6 +10550,29 @@ snapshots: - encoding - utf-8-validate + '@libsql/client@0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + dependencies: + '@libsql/core': 0.5.6 + '@libsql/hrana-client': 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + js-base64: 3.7.5 + libsql: 0.3.10 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + '@libsql/client@0.5.6(encoding@0.1.13)': + dependencies: + '@libsql/core': 0.5.6 + '@libsql/hrana-client': 0.5.6(encoding@0.1.13) + js-base64: 3.7.5 + libsql: 0.3.10 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + optional: true + '@libsql/core@0.5.6': dependencies: js-base64: 3.7.5 @@ -10554,9 +10583,31 @@ snapshots: '@libsql/darwin-x64@0.3.10': optional: true - '@libsql/hrana-client@0.5.6': + '@libsql/hrana-client@0.5.6(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: - '@libsql/isomorphic-fetch': 0.1.12 + '@libsql/isomorphic-fetch': 0.1.12(encoding@0.1.13) + '@libsql/isomorphic-ws': 0.1.5(bufferutil@4.0.7)(utf-8-validate@6.0.3) + js-base64: 3.7.5 + node-fetch: 3.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + '@libsql/hrana-client@0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + dependencies: + '@libsql/isomorphic-fetch': 0.1.12(encoding@0.1.13) + '@libsql/isomorphic-ws': 0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3) + js-base64: 3.7.5 + node-fetch: 3.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + '@libsql/hrana-client@0.5.6(encoding@0.1.13)': + dependencies: + '@libsql/isomorphic-fetch': 0.1.12(encoding@0.1.13) '@libsql/isomorphic-ws': 0.1.5 js-base64: 3.7.5 node-fetch: 3.3.2 @@ -10564,15 +10615,33 @@ snapshots: - bufferutil - encoding - utf-8-validate + optional: true - '@libsql/isomorphic-fetch@0.1.12': + '@libsql/isomorphic-fetch@0.1.12(encoding@0.1.13)': dependencies: '@types/node-fetch': 2.6.11 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) transitivePeerDependencies: - encoding '@libsql/isomorphic-ws@0.1.5': + dependencies: + '@types/ws': 8.5.4 + ws: 8.14.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + + '@libsql/isomorphic-ws@0.1.5(bufferutil@4.0.7)(utf-8-validate@6.0.3)': + dependencies: + '@types/ws': 8.5.4 + ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/isomorphic-ws@0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: '@types/ws': 8.5.4 ws: 8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) @@ -10595,12 +10664,12 @@ snapshots: '@libsql/win32-x64-msvc@0.3.10': optional: true - '@mapbox/node-pre-gyp@1.0.10': + '@mapbox/node-pre-gyp@1.0.10(encoding@0.1.13)': dependencies: detect-libc: 2.0.1 https-proxy-agent: 5.0.1 make-dir: 3.1.0 - node-fetch: 2.6.9 + node-fetch: 2.6.9(encoding@0.1.13) nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 @@ -10657,6 +10726,8 @@ snapshots: dependencies: '@types/pg': 8.6.6 + '@noble/hashes@1.4.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -10679,10 +10750,10 @@ snapshots: mkdirp: 1.0.4 rimraf: 3.0.2 - '@op-engineering/op-sqlite@2.0.16(react-native@0.73.6)(react@18.2.0)': + '@op-engineering/op-sqlite@2.0.16(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3) '@opentelemetry/api@1.4.1': {} @@ -10690,6 +10761,10 @@ snapshots: dependencies: esbuild: 0.14.54 + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.4.0 + '@pkgjs/parseargs@0.11.0': optional: true @@ -10697,17 +10772,17 @@ snapshots: '@polka/url@1.0.0-next.21': {} - '@react-native-community/cli-clean@12.3.6': + '@react-native-community/cli-clean@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 transitivePeerDependencies: - encoding - '@react-native-community/cli-config@12.3.6': + '@react-native-community/cli-config@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 cosmiconfig: 5.2.1 deepmerge: 4.3.1 @@ -10722,12 +10797,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native-community/cli-doctor@12.3.6': + '@react-native-community/cli-doctor@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-config': 12.3.6 - '@react-native-community/cli-platform-android': 12.3.6 - '@react-native-community/cli-platform-ios': 12.3.6 - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 command-exists: 1.2.9 deepmerge: 4.3.1 @@ -10743,18 +10818,18 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-hermes@12.3.6': + '@react-native-community/cli-hermes@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-platform-android': 12.3.6 - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 hermes-profile-transformer: 0.0.6 transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-android@12.3.6': + '@react-native-community/cli-platform-android@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.6 @@ -10763,9 +10838,9 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-ios@12.3.6': + '@react-native-community/cli-platform-ios@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.6 @@ -10776,30 +10851,30 @@ snapshots: '@react-native-community/cli-plugin-metro@12.3.6': {} - '@react-native-community/cli-server-api@12.3.6': + '@react-native-community/cli-server-api@12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) compression: 1.7.4 connect: 3.7.0 errorhandler: 1.5.1 nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 7.5.9 + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - '@react-native-community/cli-tools@12.3.6': + '@react-native-community/cli-tools@12.3.6(encoding@0.1.13)': dependencies: appdirsjs: 1.2.7 chalk: 4.1.2 find-up: 5.0.0 mime: 2.6.0 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) open: 6.4.0 ora: 5.4.1 semver: 7.6.0 @@ -10812,16 +10887,16 @@ snapshots: dependencies: joi: 17.12.3 - '@react-native-community/cli@12.3.6': + '@react-native-community/cli@12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: - '@react-native-community/cli-clean': 12.3.6 - '@react-native-community/cli-config': 12.3.6 + '@react-native-community/cli-clean': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-doctor': 12.3.6 - '@react-native-community/cli-hermes': 12.3.6 + '@react-native-community/cli-doctor': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-hermes': 12.3.6(encoding@0.1.13) '@react-native-community/cli-plugin-metro': 12.3.6 - '@react-native-community/cli-server-api': 12.3.6 - '@react-native-community/cli-tools': 12.3.6 + '@react-native-community/cli-server-api': 12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) '@react-native-community/cli-types': 12.3.6 chalk: 4.1.2 commander: 9.5.0 @@ -10840,14 +10915,14 @@ snapshots: '@react-native/assets-registry@0.73.1': {} - '@react-native/babel-plugin-codegen@0.73.4(@babel/preset-env@7.24.4)': + '@react-native/babel-plugin-codegen@0.73.4(@babel/preset-env@7.24.4(@babel/core@7.24.4))': dependencies: - '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4) + '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4(@babel/core@7.24.4)) transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4)': + '@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))': dependencies: '@babel/core': 7.24.4 '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.24.4) @@ -10888,38 +10963,38 @@ snapshots: '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.4) '@babel/plugin-transform-unicode-regex': 7.24.1(@babel/core@7.24.4) '@babel/template': 7.24.0 - '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.24.4) + '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.24.4(@babel/core@7.24.4)) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.24.4) react-refresh: 0.14.0 transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/codegen@0.73.3(@babel/preset-env@7.24.4)': + '@react-native/codegen@0.73.3(@babel/preset-env@7.24.4(@babel/core@7.24.4))': dependencies: '@babel/parser': 7.24.4 '@babel/preset-env': 7.24.4(@babel/core@7.24.4) flow-parser: 0.206.0 glob: 7.2.3 invariant: 2.2.4 - jscodeshift: 0.14.0(@babel/preset-env@7.24.4) + jscodeshift: 0.14.0(@babel/preset-env@7.24.4(@babel/core@7.24.4)) mkdirp: 0.5.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4)': + '@react-native/community-cli-plugin@0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: - '@react-native-community/cli-server-api': 12.3.6 - '@react-native-community/cli-tools': 12.3.6 - '@react-native/dev-middleware': 0.73.8 - '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4) + '@react-native-community/cli-server-api': 12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native/dev-middleware': 0.73.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.8 - metro-config: 0.80.8 + metro: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-config: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.8 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) readline: 1.3.0 transitivePeerDependencies: - '@babel/core' @@ -10931,7 +11006,7 @@ snapshots: '@react-native/debugger-frontend@0.73.3': {} - '@react-native/dev-middleware@0.73.8': + '@react-native/dev-middleware@0.73.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.73.3 @@ -10939,11 +11014,11 @@ snapshots: chromium-edge-launcher: 1.0.0 connect: 3.7.0 debug: 2.6.9 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) open: 7.4.2 serve-static: 1.15.0 temp-dir: 2.0.0 - ws: 6.2.2 + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -10954,10 +11029,10 @@ snapshots: '@react-native/js-polyfills@0.73.1': {} - '@react-native/metro-babel-transformer@0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4)': + '@react-native/metro-babel-transformer@0.73.15(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))': dependencies: '@babel/core': 7.24.4 - '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4) + '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) hermes-parser: 0.15.0 nullthrows: 1.1.1 transitivePeerDependencies: @@ -10968,45 +11043,52 @@ snapshots: '@react-native/normalize-colors@0.73.2': {} - '@react-native/virtualized-lists@0.73.4(react-native@0.73.6)': + '@react-native/virtualized-lists@0.73.4(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3))': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3) '@rollup/plugin-terser@0.4.1(rollup@3.20.7)': dependencies: - rollup: 3.20.7 serialize-javascript: 6.0.1 smob: 0.0.6 terser: 5.17.1 + optionalDependencies: + rollup: 3.20.7 '@rollup/plugin-terser@0.4.1(rollup@3.27.2)': dependencies: - rollup: 3.27.2 serialize-javascript: 6.0.1 smob: 0.0.6 terser: 5.17.1 + optionalDependencies: + rollup: 3.27.2 - '@rollup/plugin-typescript@11.1.0(rollup@3.20.7)(typescript@5.2.2)': + '@rollup/plugin-typescript@11.1.0(rollup@3.20.7)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@rollup/pluginutils': 5.0.2(rollup@3.20.7) resolve: 1.22.1 - rollup: 3.20.7 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) + optionalDependencies: + rollup: 3.20.7 + tslib: 2.6.2 - '@rollup/plugin-typescript@11.1.1(rollup@3.27.2)(typescript@5.2.2)': + '@rollup/plugin-typescript@11.1.1(rollup@3.27.2)(tslib@2.6.2)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@rollup/pluginutils': 5.0.2(rollup@3.27.2) resolve: 1.22.2 - rollup: 3.27.2 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) + optionalDependencies: + rollup: 3.27.2 + tslib: 2.6.2 '@rollup/pluginutils@5.0.2(rollup@3.20.7)': dependencies: '@types/estree': 1.0.1 estree-walker: 2.0.2 picomatch: 2.3.1 + optionalDependencies: rollup: 3.20.7 '@rollup/pluginutils@5.0.2(rollup@3.27.2)': @@ -11014,6 +11096,7 @@ snapshots: '@types/estree': 1.0.1 estree-walker: 2.0.2 picomatch: 2.3.1 + optionalDependencies: rollup: 3.27.2 '@segment/loosely-validate-event@2.0.0': @@ -11512,13 +11595,13 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@eslint-community/regexpp': 4.9.0 - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/scope-manager': 6.7.3 - '@typescript-eslint/type-utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/type-utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/visitor-keys': 6.7.3 debug: 4.3.4 eslint: 8.50.0 @@ -11526,48 +11609,51 @@ snapshots: ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/experimental-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint: 8.50.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2)': + '@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/visitor-keys': 6.10.0 debug: 4.3.4 eslint: 8.53.0 + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@typescript-eslint/scope-manager': 6.7.3 '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) '@typescript-eslint/visitor-keys': 6.7.3 debug: 4.3.4 eslint: 8.50.0 + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/rule-tester@6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2)': + '@typescript-eslint/rule-tester@6.10.0(@eslint/eslintrc@3.0.2)(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@eslint/eslintrc': 3.0.2 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) ajv: 6.12.6 eslint: 8.53.0 lodash.merge: 4.6.2 @@ -11591,13 +11677,14 @@ snapshots: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 - '@typescript-eslint/type-utils@6.7.3(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/type-utils@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) debug: 4.3.4 eslint: 8.50.0 - ts-api-utils: 1.0.3(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color @@ -11608,7 +11695,7 @@ snapshots: '@typescript-eslint/types@6.7.3': {} - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2)': + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 @@ -11616,12 +11703,13 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.2.2) + tsutils: 3.21.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2)': + '@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@typescript-eslint/types': 6.10.0 '@typescript-eslint/visitor-keys': 6.10.0 @@ -11629,12 +11717,13 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@6.7.3(typescript@5.2.2)': + '@typescript-eslint/typescript-estree@6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 @@ -11642,19 +11731,20 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint: 8.50.0 eslint-scope: 5.1.1 semver: 7.5.4 @@ -11662,28 +11752,28 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2)': + '@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.10.0 '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint: 8.53.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@6.7.3(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/utils@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.7.3 '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint: 8.50.0 semver: 7.5.4 transitivePeerDependencies: @@ -11798,7 +11888,19 @@ snapshots: pathe: 1.1.1 picocolors: 1.0.0 sirv: 2.0.3 - vitest: 0.31.4(@vitest/ui@0.31.4) + vitest: 0.31.4(@vitest/ui@0.31.4)(terser@5.30.3) + + '@vitest/ui@0.31.4(vitest@0.34.6)': + dependencies: + '@vitest/utils': 0.31.4 + fast-glob: 3.2.12 + fflate: 0.7.4 + flatted: 3.2.7 + pathe: 1.1.1 + picocolors: 1.0.0 + sirv: 2.0.3 + vitest: 0.34.6(@vitest/ui@0.31.4)(terser@5.30.3) + optional: true '@vitest/utils@0.31.4': dependencies: @@ -11812,7 +11914,7 @@ snapshots: loupe: 2.3.6 pretty-format: 29.7.0 - '@xata.io/client@0.29.3(typescript@5.2.2)': + '@xata.io/client@0.29.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))': dependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) @@ -12191,7 +12293,7 @@ snapshots: '@babel/plugin-transform-parameters': 7.24.1(@babel/core@7.24.4) '@babel/preset-env': 7.24.4(@babel/core@7.24.4) '@babel/preset-react': 7.24.1(@babel/core@7.24.4) - '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4) + '@react-native/babel-preset': 0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)) babel-plugin-react-native-web: 0.18.12 react-refresh: 0.14.0 transitivePeerDependencies: @@ -12743,9 +12845,9 @@ snapshots: p-filter: 3.0.0 p-map: 6.0.0 - cross-fetch@3.1.8: + cross-fetch@3.1.8(encoding@0.1.13): dependencies: - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -12962,9 +13064,27 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.27.2(bun-types@1.0.3): - dependencies: + drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.549.0)(@cloudflare/workers-types@4.20230904.0)(@libsql/client@0.5.6(encoding@0.1.13))(@neondatabase/serverless@0.9.0)(@opentelemetry/api@1.4.1)(@planetscale/database@1.16.0)(@types/better-sqlite3@7.6.4)(@types/pg@8.10.1)(@types/sql.js@1.4.4)(@vercel/postgres@0.8.0)(better-sqlite3@8.4.0)(bun-types@1.0.3)(knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)))(kysely@0.25.0)(mysql2@3.3.3)(pg@8.11.0)(postgres@3.3.5)(sql.js@1.8.0)(sqlite3@5.1.6(encoding@0.1.13)): + optionalDependencies: + '@aws-sdk/client-rds-data': 3.549.0 + '@cloudflare/workers-types': 4.20230904.0 + '@libsql/client': 0.5.6(encoding@0.1.13) + '@neondatabase/serverless': 0.9.0 + '@opentelemetry/api': 1.4.1 + '@planetscale/database': 1.16.0 + '@types/better-sqlite3': 7.6.4 + '@types/pg': 8.10.1 + '@types/sql.js': 1.4.4 + '@vercel/postgres': 0.8.0 + better-sqlite3: 8.4.0 bun-types: 1.0.3 + knex: 2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)) + kysely: 0.25.0 + mysql2: 3.3.3 + pg: 8.11.0 + postgres: 3.3.5 + sql.js: 1.8.0 + sqlite3: 5.1.6(encoding@0.1.13) duplexer@0.1.2: {} @@ -13261,18 +13381,18 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0): + eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint-import-resolver-node@0.3.9)(eslint@8.50.0): dependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0): + eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0): dependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) array-includes: 3.1.6 array.prototype.findlastindex: 1.2.2 array.prototype.flat: 1.3.1 @@ -13281,7 +13401,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint-import-resolver-node@0.3.9)(eslint@8.50.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -13291,6 +13411,8 @@ snapshots: object.values: 1.1.6 semver: 6.3.1 tsconfig-paths: 3.14.2 + optionalDependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -13317,11 +13439,12 @@ snapshots: semver: 7.5.4 strip-indent: 3.0.0 - eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.7.3)(eslint@8.50.0): + eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0): dependencies: - '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) eslint: 8.50.0 eslint-rule-composer: 0.3.0 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)))(eslint@8.50.0)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) eslint-rule-composer@0.3.0: {} @@ -13515,37 +13638,37 @@ snapshots: expand-template@2.0.3: {} - expo-asset@9.0.2(expo@50.0.14): + expo-asset@9.0.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@react-native/assets-registry': 0.73.1 blueimp-md5: 2.19.0 - expo-constants: 15.4.5(expo@50.0.14) - expo-file-system: 16.0.8(expo@50.0.14) + expo-constants: 15.4.5(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-file-system: 16.0.8(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) invariant: 2.2.4 md5-file: 3.2.3 transitivePeerDependencies: - expo - supports-color - expo-constants@15.4.5(expo@50.0.14): + expo-constants@15.4.5(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/config': 8.5.4 - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) transitivePeerDependencies: - supports-color - expo-file-system@16.0.8(expo@50.0.14): + expo-file-system@16.0.8(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo-font@11.10.3(expo@50.0.14): + expo-font@11.10.3(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) fontfaceobserver: 2.3.0 - expo-keep-awake@12.8.2(expo@50.0.14): + expo-keep-awake@12.8.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) expo-modules-autolinking@1.10.3: dependencies: @@ -13562,27 +13685,27 @@ snapshots: dependencies: invariant: 2.2.4 - expo-sqlite@13.2.0(expo@50.0.14): + expo-sqlite@13.2.0(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/websql': 1.0.1 - expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21) + expo: 50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21): + expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/runtime': 7.24.4 - '@expo/cli': 0.17.8(@react-native/babel-preset@0.73.21)(expo-modules-autolinking@1.10.3) + '@expo/cli': 0.17.8(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.10.3)(utf-8-validate@6.0.3) '@expo/config': 8.5.4 '@expo/config-plugins': 7.8.4 - '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21) + '@expo/metro-config': 0.17.6(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))) '@expo/vector-icons': 14.0.0 babel-preset-expo: 10.0.1(@babel/core@7.24.4) - expo-asset: 9.0.2(expo@50.0.14) - expo-file-system: 16.0.8(expo@50.0.14) - expo-font: 11.10.3(expo@50.0.14) - expo-keep-awake: 12.8.2(expo@50.0.14) + expo-asset: 9.0.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-file-system: 16.0.8(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-font: 11.10.3(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-keep-awake: 12.8.2(expo@50.0.14(@babel/core@7.24.4)(@react-native/babel-preset@0.73.21(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4)))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) expo-modules-autolinking: 1.10.3 expo-modules-core: 1.11.12 - fbemitter: 3.0.0 + fbemitter: 3.0.0(encoding@0.1.13) whatwg-url-without-unicode: 8.0.0-3 transitivePeerDependencies: - '@babel/core' @@ -13681,17 +13804,17 @@ snapshots: dependencies: bser: 2.1.1 - fbemitter@3.0.0: + fbemitter@3.0.0(encoding@0.1.13): dependencies: - fbjs: 3.0.5 + fbjs: 3.0.5(encoding@0.1.13) transitivePeerDependencies: - encoding fbjs-css-vars@1.0.2: {} - fbjs@3.0.5: + fbjs@3.0.5(encoding@0.1.13): dependencies: - cross-fetch: 3.1.8 + cross-fetch: 3.1.8(encoding@0.1.13) fbjs-css-vars: 1.0.2 loose-envify: 1.4.0 object-assign: 4.1.1 @@ -13706,9 +13829,9 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.2.1 - fetch-ponyfill@7.1.0: + fetch-ponyfill@7.1.0(encoding@0.1.13): dependencies: - node-fetch: 2.6.11 + node-fetch: 2.6.11(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -14520,7 +14643,7 @@ snapshots: jsc-safe-url@0.2.4: {} - jscodeshift@0.14.0(@babel/preset-env@7.24.4): + jscodeshift@0.14.0(@babel/preset-env@7.24.4(@babel/core@7.24.4)): dependencies: '@babel/core': 7.24.4 '@babel/parser': 7.24.4 @@ -14615,9 +14738,8 @@ snapshots: kleur@4.1.5: {} - knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6): + knex@2.4.2(better-sqlite3@8.4.0)(mysql2@3.3.3)(pg@8.11.0)(sqlite3@5.1.6(encoding@0.1.13)): dependencies: - better-sqlite3: 8.4.0 colorette: 2.0.19 commander: 9.5.0 debug: 4.3.4 @@ -14627,14 +14749,16 @@ snapshots: getopts: 2.3.0 interpret: 2.2.0 lodash: 4.17.21 - mysql2: 3.3.3 - pg: 8.11.0 pg-connection-string: 2.5.0 rechoir: 0.8.0 resolve-from: 5.0.0 - sqlite3: 5.1.6 tarn: 3.0.2 tildify: 2.0.0 + optionalDependencies: + better-sqlite3: 8.4.0 + mysql2: 3.3.3 + pg: 8.11.0 + sqlite3: 5.1.6(encoding@0.1.13) transitivePeerDependencies: - supports-color @@ -14921,12 +15045,12 @@ snapshots: metro-core: 0.80.8 rimraf: 3.0.2 - metro-config@0.80.8: + metro-config@0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: connect: 3.7.0 cosmiconfig: 5.2.1 jest-validate: 29.7.0 - metro: 0.80.8 + metro: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-cache: 0.80.8 metro-core: 0.80.8 metro-runtime: 0.80.8 @@ -15002,13 +15126,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.8: + metro-transform-worker@0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/core': 7.24.4 '@babel/generator': 7.24.4 '@babel/parser': 7.24.4 '@babel/types': 7.24.0 - metro: 0.80.8 + metro: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-babel-transformer: 0.80.8 metro-cache: 0.80.8 metro-cache-key: 0.80.8 @@ -15022,7 +15146,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.8: + metro@0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/code-frame': 7.24.2 '@babel/core': 7.24.4 @@ -15048,7 +15172,7 @@ snapshots: metro-babel-transformer: 0.80.8 metro-cache: 0.80.8 metro-cache-key: 0.80.8 - metro-config: 0.80.8 + metro-config: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.8 metro-file-map: 0.80.8 metro-resolver: 0.80.8 @@ -15056,16 +15180,16 @@ snapshots: metro-source-map: 0.80.8 metro-symbolicate: 0.80.8 metro-transform-plugins: 0.80.8 - metro-transform-worker: 0.80.8 + metro-transform-worker: 0.80.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) mime-types: 2.1.35 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) nullthrows: 1.1.1 rimraf: 3.0.2 serialize-error: 2.1.0 source-map: 0.5.7 strip-ansi: 6.0.1 throat: 5.0.0 - ws: 7.5.9 + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -15261,17 +15385,23 @@ snapshots: dependencies: lodash: 4.17.21 - node-fetch@2.6.11: + node-fetch@2.6.11(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 - node-fetch@2.6.9: + node-fetch@2.6.9(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 - node-fetch@2.7.0: + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 node-fetch@3.3.1: dependencies: @@ -15719,10 +15849,12 @@ snapshots: pngjs@3.4.0: {} - postcss-load-config@4.0.1: + postcss-load-config@4.0.1(postcss@8.4.38): dependencies: lilconfig: 2.1.0 yaml: 2.3.1 + optionalDependencies: + postcss: 8.4.38 postcss@8.4.24: dependencies: @@ -15889,10 +16021,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-devtools-core@4.28.5: + react-devtools-core@4.28.5(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: shell-quote: 1.8.1 - ws: 7.5.9 + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -15903,19 +16035,19 @@ snapshots: react-is@18.2.0: {} - react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0): + react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 12.3.6 - '@react-native-community/cli-platform-android': 12.3.6 - '@react-native-community/cli-platform-ios': 12.3.6 + '@react-native-community/cli': 12.3.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) '@react-native/assets-registry': 0.73.1 - '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4) - '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4) + '@react-native/codegen': 0.73.3(@babel/preset-env@7.24.4(@babel/core@7.24.4)) + '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 - '@react-native/virtualized-lists': 0.73.4(react-native@0.73.6) + '@react-native/virtualized-lists': 0.73.4(react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4(@babel/core@7.24.4))(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@6.0.3)) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -15935,14 +16067,14 @@ snapshots: pretty-format: 26.6.2 promise: 8.3.0 react: 18.2.0 - react-devtools-core: 4.28.5 + react-devtools-core: 4.28.5(bufferutil@4.0.8)(utf-8-validate@6.0.3) react-refresh: 0.14.0 react-shallow-renderer: 16.15.0(react@18.2.0) regenerator-runtime: 0.13.11 scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 whatwg-fetch: 3.6.20 - ws: 6.2.2 + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 transitivePeerDependencies: - '@babel/core' @@ -16087,7 +16219,7 @@ snapshots: resolve-from@5.0.0: {} - resolve-tspaths@0.8.16(typescript@5.2.2): + resolve-tspaths@0.8.16(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): dependencies: ansi-colors: 4.1.3 commander: 11.0.0 @@ -16421,9 +16553,9 @@ snapshots: sql.js@1.8.0: {} - sqlite3@5.1.6: + sqlite3@5.1.6(encoding@0.1.13): dependencies: - '@mapbox/node-pre-gyp': 1.0.10 + '@mapbox/node-pre-gyp': 1.0.10(encoding@0.1.13) node-addon-api: 4.3.0 tar: 6.1.13 optionalDependencies: @@ -16757,14 +16889,14 @@ snapshots: treeify@1.1.0: {} - ts-api-utils@1.0.3(typescript@5.2.2): + ts-api-utils@1.0.3(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): dependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) ts-interface-checker@0.1.13: {} - tsconfck@2.1.1(typescript@5.2.2): - dependencies: + tsconfck@2.1.1(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): + optionalDependencies: typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) tsconfig-paths@3.14.2: @@ -16782,7 +16914,7 @@ snapshots: tslib@2.6.2: {} - tsup@7.2.0(typescript@5.2.2): + tsup@7.2.0(postcss@8.4.38)(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): dependencies: bundle-require: 4.0.2(esbuild@0.18.20) cac: 6.7.14 @@ -16792,18 +16924,20 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.1 + postcss-load-config: 4.0.1(postcss@8.4.38) resolve-from: 5.0.0 rollup: 3.27.2 source-map: 0.8.0-beta.0 sucrase: 3.34.0 tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.4.38 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) transitivePeerDependencies: - supports-color - ts-node - tsutils@3.21.0(typescript@5.2.2): + tsutils@3.21.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)): dependencies: tslib: 1.14.1 typescript: 5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq) @@ -17045,14 +17179,14 @@ snapshots: vary@1.1.2: {} - vite-node@0.31.4(@types/node@20.8.7): + vite-node@0.31.4(@types/node@20.8.7)(terser@5.30.3): dependencies: cac: 6.7.14 debug: 4.3.4 mlly: 1.3.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.3.9(@types/node@20.8.7) + vite: 4.3.9(@types/node@20.8.7)(terser@5.30.3) transitivePeerDependencies: - '@types/node' - less @@ -17062,14 +17196,14 @@ snapshots: - supports-color - terser - vite-node@0.34.6(@types/node@20.10.1): + vite-node@0.34.6(@types/node@20.10.1)(terser@5.30.3): dependencies: cac: 6.7.14 debug: 4.3.4 mlly: 1.4.2 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.3.9(@types/node@20.10.1) + vite: 4.3.9(@types/node@20.10.1)(terser@5.30.3) transitivePeerDependencies: - '@types/node' - less @@ -17079,44 +17213,48 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.2.0(typescript@5.2.2)(vite@4.3.9): + vite-tsconfig-paths@4.2.0(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq))(vite@4.3.9(@types/node@20.2.5)(terser@5.30.3)): dependencies: debug: 4.3.4 globrex: 0.1.2 - tsconfck: 2.1.1(typescript@5.2.2) - vite: 4.3.9(@types/node@20.2.5) + tsconfck: 2.1.1(typescript@5.2.2(patch_hash=wmhs4olj6eveeldp6si4l46ssq)) + optionalDependencies: + vite: 4.3.9(@types/node@20.2.5)(terser@5.30.3) transitivePeerDependencies: - supports-color - typescript - vite@4.3.9(@types/node@20.10.1): + vite@4.3.9(@types/node@20.10.1)(terser@5.30.3): dependencies: - '@types/node': 20.10.1 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.27.2 optionalDependencies: + '@types/node': 20.10.1 fsevents: 2.3.3 + terser: 5.30.3 - vite@4.3.9(@types/node@20.2.5): + vite@4.3.9(@types/node@20.2.5)(terser@5.30.3): dependencies: - '@types/node': 20.2.5 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.27.2 optionalDependencies: + '@types/node': 20.2.5 fsevents: 2.3.3 + terser: 5.30.3 - vite@4.3.9(@types/node@20.8.7): + vite@4.3.9(@types/node@20.8.7)(terser@5.30.3): dependencies: - '@types/node': 20.8.7 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.27.2 optionalDependencies: + '@types/node': 20.8.7 fsevents: 2.3.3 + terser: 5.30.3 - vitest@0.31.4(@vitest/ui@0.31.4): + vitest@0.31.4(@vitest/ui@0.31.4)(terser@5.30.3): dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 @@ -17125,7 +17263,6 @@ snapshots: '@vitest/runner': 0.31.4 '@vitest/snapshot': 0.31.4 '@vitest/spy': 0.31.4 - '@vitest/ui': 0.31.4(vitest@0.31.4) '@vitest/utils': 0.31.4 acorn: 8.8.2 acorn-walk: 8.2.0 @@ -17141,9 +17278,11 @@ snapshots: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.5.0 - vite: 4.3.9(@types/node@20.8.7) - vite-node: 0.31.4(@types/node@20.8.7) + vite: 4.3.9(@types/node@20.8.7)(terser@5.30.3) + vite-node: 0.31.4(@types/node@20.8.7)(terser@5.30.3) why-is-node-running: 2.2.2 + optionalDependencies: + '@vitest/ui': 0.31.4(vitest@0.31.4) transitivePeerDependencies: - less - sass @@ -17152,7 +17291,7 @@ snapshots: - supports-color - terser - vitest@0.34.6: + vitest@0.34.6(@vitest/ui@0.31.4)(terser@5.30.3): dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 @@ -17175,9 +17314,11 @@ snapshots: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.3.9(@types/node@20.10.1) - vite-node: 0.34.6(@types/node@20.10.1) + vite: 4.3.9(@types/node@20.10.1)(terser@5.30.3) + vite-node: 0.34.6(@types/node@20.10.1)(terser@5.30.3) why-is-node-running: 2.2.2 + optionalDependencies: + '@vitest/ui': 0.31.4(vitest@0.34.6) transitivePeerDependencies: - less - sass @@ -17306,19 +17447,33 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.0.2 - ws@6.2.2: + ws@6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: async-limiter: 1.0.1 + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 - ws@7.5.9: {} + ws@7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 6.0.3 ws@8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3): - dependencies: + optionalDependencies: + bufferutil: 4.0.7 + utf-8-validate: 6.0.3 + + ws@8.14.2: + optional: true + + ws@8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3): + optionalDependencies: bufferutil: 4.0.7 utf-8-validate: 6.0.3 ws@8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): - dependencies: + optionalDependencies: bufferutil: 4.0.8 utf-8-validate: 6.0.3 From 8e4735dfd4f66cc1a0beb67af66a341b5abb3697 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Mon, 8 Jul 2024 17:12:41 +0300 Subject: [PATCH 35/40] Fix a few tests for returning in a new structure --- drizzle-orm/src/mysql2/session.ts | 2 +- integration-tests/tests/mysql/mysql-common.ts | 95 ++++++++++++++++++- integration-tests/vitest.config.ts | 16 ++-- 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/drizzle-orm/src/mysql2/session.ts b/drizzle-orm/src/mysql2/session.ts index 8fac78fbd..ab11d1f17 100644 --- a/drizzle-orm/src/mysql2/session.ts +++ b/drizzle-orm/src/mysql2/session.ts @@ -10,7 +10,7 @@ import type { RowDataPacket, } from 'mysql2/promise'; import { once } from 'node:events'; -import { Column } from '~/column'; +import { Column } from '~/column.ts'; import { entityKind, is } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { NoopLogger } from '~/logger.ts'; diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index e67e706fb..ee0064f3c 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -59,13 +59,14 @@ import { unique, uniqueIndex, uniqueKeyName, + varchar, year, } from 'drizzle-orm/mysql-core'; import type { MySqlRemoteDatabase } from 'drizzle-orm/mysql-proxy'; import { migrate } from 'drizzle-orm/mysql2/migrator'; import getPort from 'get-port'; import { v4 as uuid } from 'uuid'; -import { afterAll, beforeEach, describe, expect, test } from 'vitest'; +import { afterAll, beforeEach, describe, expect, expectTypeOf, test } from 'vitest'; import { Expect, toLocalDate } from '~/utils.ts'; import type { Equal } from '~/utils.ts'; @@ -3388,3 +3389,95 @@ export function tests(driver?: string) { }); }); } + +async function setupReturningFunctionsTest(db: MySqlDatabase) { + await db.execute(sql`drop table if exists \`users_default_fn\``); + await db.execute( + sql` + create table \`users_default_fn\` ( + \`id\` varchar(256) primary key, + \`name\` text not null + ); + `, + ); +} + +test('insert $returningId: serail as id', async (ctx) => { + const { db } = ctx.mysql; + + const result = await db.insert(usersTable).values({ name: 'John' }).$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + id: number; + }[]>(); + + expect(result).toStrictEqual([{ id: 1 }]); +}); + +test('insert $returningId: serail as id, batch insert', async (ctx) => { + const { db } = ctx.mysql; + + const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + id: number; + }[]>(); + + expect(result).toStrictEqual([{ id: 1 }, { id: 2 }]); +}); + +test('insert $returningId: $default as primary key', async (ctx) => { + const { db } = ctx.mysql; + + const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; + let iterator = 0; + + const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { + const value = uniqueKeys[iterator]!; + iterator++; + return value; + }), + name: text('name').notNull(), + }); + + await setupReturningFunctionsTest(db); + + const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]) + // ^? + .$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + customId: string; + }[]>(); + + expect(result).toStrictEqual([{ customId: 'ao865jf3mcmkfkk8o5ri495z' }, { customId: 'dyqs529eom0iczo2efxzbcut' }]); +}); + +test('insert $returningId: $default as primary key with value', async (ctx) => { + const { db } = ctx.mysql; + + const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; + let iterator = 0; + + const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { + const value = uniqueKeys[iterator]!; + iterator++; + return value; + }), + name: text('name').notNull(), + }); + + await setupReturningFunctionsTest(db); + + const result = await db.insert(usersTableDefFn).values([{ name: 'John', customId: 'test' }, { name: 'John1' }]) + // ^? + .$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + customId: string; + }[]>(); + + expect(result).toStrictEqual([{ customId: 'test' }, { customId: 'ao865jf3mcmkfkk8o5ri495z' }]); +}); diff --git a/integration-tests/vitest.config.ts b/integration-tests/vitest.config.ts index defc44cc4..d4b93ff33 100644 --- a/integration-tests/vitest.config.ts +++ b/integration-tests/vitest.config.ts @@ -5,15 +5,15 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { include: [ - 'tests/extensions/postgis/**/*', - 'tests/relational/**/*.test.ts', - 'tests/pg/**/*.test.ts', + // 'tests/extensions/postgis/**/*', + // 'tests/relational/**/*.test.ts', + // 'tests/pg/**/*.test.ts', 'tests/mysql/**/*.test.ts', - 'tests/sqlite/**/*.test.ts', - 'tests/replicas/**/*', - 'tests/imports/**/*', - 'tests/extensions/vectors/**/*', - 'tests/version.test.ts', + // 'tests/sqlite/**/*.test.ts', + // 'tests/replicas/**/*', + // 'tests/imports/**/*', + // 'tests/extensions/vectors/**/*', + // 'tests/version.test.ts', ], exclude: [ ...(process.env.SKIP_EXTERNAL_DB_TESTS From 4c9a51dc27c0498a0ad3d5eeae2147fd8101f578 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 9 Jul 2024 11:39:37 +0300 Subject: [PATCH 36/40] Update planetscale and mysql-proxy returning runtime --- drizzle-orm/src/mysql-proxy/driver.ts | 2 +- drizzle-orm/src/mysql-proxy/session.ts | 46 +++- .../src/planetscale-serverless/session.ts | 67 +++++- integration-tests/tests/mysql/mysql-common.ts | 196 +++++++++--------- 4 files changed, 204 insertions(+), 107 deletions(-) diff --git a/drizzle-orm/src/mysql-proxy/driver.ts b/drizzle-orm/src/mysql-proxy/driver.ts index e9008f816..574db42c1 100644 --- a/drizzle-orm/src/mysql-proxy/driver.ts +++ b/drizzle-orm/src/mysql-proxy/driver.ts @@ -18,7 +18,7 @@ export type RemoteCallback = ( sql: string, params: any[], method: 'all' | 'execute', -) => Promise<{ rows: any[] }>; +) => Promise<{ rows: any[]; insertId?: number; affectedRows?: number }>; export function drizzle = Record>( callback: RemoteCallback, diff --git a/drizzle-orm/src/mysql-proxy/session.ts b/drizzle-orm/src/mysql-proxy/session.ts index c5ab0295d..03039cfb2 100644 --- a/drizzle-orm/src/mysql-proxy/session.ts +++ b/drizzle-orm/src/mysql-proxy/session.ts @@ -1,5 +1,6 @@ import type { FieldPacket, ResultSetHeader } from 'mysql2/promise'; -import { entityKind } from '~/entity.ts'; +import { Column } from '~/column.ts'; +import { entityKind, is } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { NoopLogger } from '~/logger.ts'; import type { MySqlDialect } from '~/mysql-core/dialect.ts'; @@ -47,6 +48,8 @@ export class MySqlRemoteSession< query: Query, fields: SelectedFieldsOrdered | undefined, customResultMapper?: (rows: unknown[][]) => T['execute'], + generatedIds?: Record[], + returningIds?: SelectedFieldsOrdered, ): PreparedQueryKind { return new PreparedQuery( this.client, @@ -55,6 +58,8 @@ export class MySqlRemoteSession< this.logger, fields, customResultMapper, + generatedIds, + returningIds, ) as PreparedQueryKind; } @@ -95,6 +100,10 @@ export class PreparedQuery extends PreparedQ private logger: Logger, private fields: SelectedFieldsOrdered | undefined, private customResultMapper?: (rows: unknown[][]) => T['execute'], + // Keys that were used in $default and the value that was generated for them + private generatedIds?: Record[], + // Keys that should be returned, it has the column with all properries + key from object + private returningIds?: SelectedFieldsOrdered, ) { super(); } @@ -102,14 +111,41 @@ export class PreparedQuery extends PreparedQ async execute(placeholderValues: Record | undefined = {}): Promise { const params = fillPlaceholders(this.params, placeholderValues); - const { fields, client, queryString, logger, joinsNotNullableMap, customResultMapper } = this; + const { fields, client, queryString, logger, joinsNotNullableMap, customResultMapper, returningIds, generatedIds } = + this; logger.logQuery(queryString, params); if (!fields && !customResultMapper) { - const { rows } = await client(queryString, params, 'execute'); - - return rows; + const { rows: data } = await client(queryString, params, 'execute'); + + const insertId = data[0].insertId as number; + const affectedRows = data[0].affectedRows; + + if (returningIds) { + const returningResponse = []; + let j = 0; + for (let i = insertId; i < insertId + affectedRows; i++) { + for (const column of returningIds) { + const key = returningIds[0]!.path[0]!; + if (is(column.field, Column)) { + // @ts-ignore + if (column.field.primary && column.field.autoIncrement) { + returningResponse.push({ [key]: i }); + } + if (column.field.defaultFn && generatedIds) { + // generatedIds[rowIdx][key] + returningResponse.push({ [key]: generatedIds[j]![key] }); + } + } + } + j++; + } + + return returningResponse; + } + + return data; } const { rows } = await client(queryString, params, 'all'); diff --git a/drizzle-orm/src/planetscale-serverless/session.ts b/drizzle-orm/src/planetscale-serverless/session.ts index 60b7d83d8..f2275b7f2 100644 --- a/drizzle-orm/src/planetscale-serverless/session.ts +++ b/drizzle-orm/src/planetscale-serverless/session.ts @@ -1,5 +1,6 @@ import type { Client, Connection, ExecutedQuery, Transaction } from '@planetscale/database'; -import { entityKind } from '~/entity.ts'; +import { Column } from '~/column.ts'; +import { entityKind, is } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { NoopLogger } from '~/logger.ts'; import type { MySqlDialect } from '~/mysql-core/dialect.ts'; @@ -29,6 +30,10 @@ export class PlanetScalePreparedQuery extend private logger: Logger, private fields: SelectedFieldsOrdered | undefined, private customResultMapper?: (rows: unknown[][]) => T['execute'], + // Keys that were used in $default and the value that was generated for them + private generatedIds?: Record[], + // Keys that should be returned, it has the column with all properries + key from object + private returningIds?: SelectedFieldsOrdered, ) { super(); } @@ -38,11 +43,47 @@ export class PlanetScalePreparedQuery extend this.logger.logQuery(this.queryString, params); - const { fields, client, queryString, rawQuery, query, joinsNotNullableMap, customResultMapper } = this; + const { + fields, + client, + queryString, + rawQuery, + query, + joinsNotNullableMap, + customResultMapper, + returningIds, + generatedIds, + } = this; if (!fields && !customResultMapper) { - return client.execute(queryString, params, rawQuery); + const res = await client.execute(queryString, params, rawQuery); + + const insertId = Number.parseFloat(res.insertId); + const affectedRows = res.rowsAffected; + + // for each row, I need to check keys from + if (returningIds) { + const returningResponse = []; + let j = 0; + for (let i = insertId; i < insertId + affectedRows; i++) { + for (const column of returningIds) { + const key = returningIds[0]!.path[0]!; + if (is(column.field, Column)) { + // @ts-ignore + if (column.field.primary && column.field.autoIncrement) { + returningResponse.push({ [key]: i }); + } + if (column.field.defaultFn && generatedIds) { + // generatedIds[rowIdx][key] + returningResponse.push({ [key]: generatedIds[j]![key] }); + } + } + } + j++; + } + return returningResponse; + } + return res; } - const { rows } = await client.execute(queryString, params, query); if (customResultMapper) { @@ -86,8 +127,19 @@ export class PlanetscaleSession< query: Query, fields: SelectedFieldsOrdered | undefined, customResultMapper?: (rows: unknown[][]) => T['execute'], + generatedIds?: Record[], + returningIds?: SelectedFieldsOrdered, ): MySqlPreparedQuery { - return new PlanetScalePreparedQuery(this.client, query.sql, query.params, this.logger, fields, customResultMapper); + return new PlanetScalePreparedQuery( + this.client, + query.sql, + query.params, + this.logger, + fields, + customResultMapper, + generatedIds, + returningIds, + ); } async query(query: string, params: unknown[]): Promise { @@ -106,7 +158,10 @@ export class PlanetscaleSession< override all(query: SQL): Promise { const querySql = this.dialect.sqlToQuery(query); this.logger.logQuery(querySql.sql, querySql.params); - return this.client.execute(querySql.sql, querySql.params, { as: 'object' }).then((eQuery) => eQuery.rows as T[]); + + return this.client.execute(querySql.sql, querySql.params, { as: 'object' }).then(( + eQuery, + ) => eQuery.rows as T[]); } override transaction( diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index ee0064f3c..a23cb7193 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -214,12 +214,16 @@ export async function createDockerDB(): Promise { return `mysql://root:mysql@127.0.0.1:${port}/drizzle`; } -afterAll(async () => { - await mysqlContainer?.stop().catch(console.error); -}); +// afterAll(async () => { +// await mysqlContainer?.stop().catch(console.error); +// }); export function tests(driver?: string) { describe('common', () => { + afterAll(async () => { + await mysqlContainer?.stop().catch(console.error); + }); + beforeEach(async (ctx) => { const { db } = ctx.mysql; await db.execute(sql`drop table if exists userstest`); @@ -297,6 +301,18 @@ export function tests(driver?: string) { } }); + async function setupReturningFunctionsTest(db: MySqlDatabase) { + await db.execute(sql`drop table if exists \`users_default_fn\``); + await db.execute( + sql` + create table \`users_default_fn\` ( + \`id\` varchar(256) primary key, + \`name\` text not null + ); + `, + ); + } + async function setupSetOperationTest(db: TestMySQLDB) { await db.execute(sql`drop table if exists \`users2\``); await db.execute(sql`drop table if exists \`cities\``); @@ -3325,6 +3341,88 @@ export function tests(driver?: string) { }]); }); + test('insert $returningId: serail as id', async (ctx) => { + const { db } = ctx.mysql; + + const result = await db.insert(usersTable).values({ name: 'John' }).$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + id: number; + }[]>(); + + expect(result).toStrictEqual([{ id: 1 }]); + }); + + test('insert $returningId: serail as id, batch insert', async (ctx) => { + const { db } = ctx.mysql; + + const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + id: number; + }[]>(); + + expect(result).toStrictEqual([{ id: 1 }, { id: 2 }]); + }); + + test('insert $returningId: $default as primary key', async (ctx) => { + const { db } = ctx.mysql; + + const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; + let iterator = 0; + + const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { + const value = uniqueKeys[iterator]!; + iterator++; + return value; + }), + name: text('name').notNull(), + }); + + await setupReturningFunctionsTest(db); + + const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]) + // ^? + .$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + customId: string; + }[]>(); + + expect(result).toStrictEqual([{ customId: 'ao865jf3mcmkfkk8o5ri495z' }, { + customId: 'dyqs529eom0iczo2efxzbcut', + }]); + }); + + test('insert $returningId: $default as primary key with value', async (ctx) => { + const { db } = ctx.mysql; + + const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; + let iterator = 0; + + const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { + const value = uniqueKeys[iterator]!; + iterator++; + return value; + }), + name: text('name').notNull(), + }); + + await setupReturningFunctionsTest(db); + + const result = await db.insert(usersTableDefFn).values([{ name: 'John', customId: 'test' }, { name: 'John1' }]) + // ^? + .$returningId(); + + expectTypeOf(result).toEqualTypeOf<{ + customId: string; + }[]>(); + + expect(result).toStrictEqual([{ customId: 'test' }, { customId: 'ao865jf3mcmkfkk8o5ri495z' }]); + }); + test('mySchema :: view', async (ctx) => { const { db } = ctx.mysql; @@ -3389,95 +3487,3 @@ export function tests(driver?: string) { }); }); } - -async function setupReturningFunctionsTest(db: MySqlDatabase) { - await db.execute(sql`drop table if exists \`users_default_fn\``); - await db.execute( - sql` - create table \`users_default_fn\` ( - \`id\` varchar(256) primary key, - \`name\` text not null - ); - `, - ); -} - -test('insert $returningId: serail as id', async (ctx) => { - const { db } = ctx.mysql; - - const result = await db.insert(usersTable).values({ name: 'John' }).$returningId(); - - expectTypeOf(result).toEqualTypeOf<{ - id: number; - }[]>(); - - expect(result).toStrictEqual([{ id: 1 }]); -}); - -test('insert $returningId: serail as id, batch insert', async (ctx) => { - const { db } = ctx.mysql; - - const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); - - expectTypeOf(result).toEqualTypeOf<{ - id: number; - }[]>(); - - expect(result).toStrictEqual([{ id: 1 }, { id: 2 }]); -}); - -test('insert $returningId: $default as primary key', async (ctx) => { - const { db } = ctx.mysql; - - const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; - let iterator = 0; - - const usersTableDefFn = mysqlTable('users_default_fn', { - customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { - const value = uniqueKeys[iterator]!; - iterator++; - return value; - }), - name: text('name').notNull(), - }); - - await setupReturningFunctionsTest(db); - - const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]) - // ^? - .$returningId(); - - expectTypeOf(result).toEqualTypeOf<{ - customId: string; - }[]>(); - - expect(result).toStrictEqual([{ customId: 'ao865jf3mcmkfkk8o5ri495z' }, { customId: 'dyqs529eom0iczo2efxzbcut' }]); -}); - -test('insert $returningId: $default as primary key with value', async (ctx) => { - const { db } = ctx.mysql; - - const uniqueKeys = ['ao865jf3mcmkfkk8o5ri495z', 'dyqs529eom0iczo2efxzbcut']; - let iterator = 0; - - const usersTableDefFn = mysqlTable('users_default_fn', { - customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => { - const value = uniqueKeys[iterator]!; - iterator++; - return value; - }), - name: text('name').notNull(), - }); - - await setupReturningFunctionsTest(db); - - const result = await db.insert(usersTableDefFn).values([{ name: 'John', customId: 'test' }, { name: 'John1' }]) - // ^? - .$returningId(); - - expectTypeOf(result).toEqualTypeOf<{ - customId: string; - }[]>(); - - expect(result).toStrictEqual([{ customId: 'test' }, { customId: 'ao865jf3mcmkfkk8o5ri495z' }]); -}); From ee8277f85aff926c8bef8d466feb0481907cfaeb Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 9 Jul 2024 11:58:02 +0300 Subject: [PATCH 37/40] Update typo --- integration-tests/tests/mysql/mysql-common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index a23cb7193..db1486270 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -3341,7 +3341,7 @@ export function tests(driver?: string) { }]); }); - test('insert $returningId: serail as id', async (ctx) => { + test('insert $returningId: serial as id', async (ctx) => { const { db } = ctx.mysql; const result = await db.insert(usersTable).values({ name: 'John' }).$returningId(); @@ -3353,7 +3353,7 @@ export function tests(driver?: string) { expect(result).toStrictEqual([{ id: 1 }]); }); - test('insert $returningId: serail as id, batch insert', async (ctx) => { + test('insert $returningId: serial as id, batch insert', async (ctx) => { const { db } = ctx.mysql; const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); From 50dca32153d4b2f5e713a75e54d77b67b3748df6 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 9 Jul 2024 12:04:37 +0300 Subject: [PATCH 38/40] Add 1 more test for infer insert for mysql package --- drizzle-orm/type-tests/mysql/tables.ts | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drizzle-orm/type-tests/mysql/tables.ts b/drizzle-orm/type-tests/mysql/tables.ts index 35c86c7e6..eac796e6f 100644 --- a/drizzle-orm/type-tests/mysql/tables.ts +++ b/drizzle-orm/type-tests/mysql/tables.ts @@ -12,6 +12,7 @@ import { foreignKey, index, int, + json, longtext, mediumtext, type MySqlColumn, @@ -22,6 +23,7 @@ import { text, timestamp, tinytext, + unique, uniqueIndex, varchar, } from '~/mysql-core/index.ts'; @@ -712,3 +714,49 @@ Expect< id4: int('id').$defaultFn(() => '1'), }); } +{ + const emailLog = mysqlTable( + 'email_log', + { + id: int('id', { unsigned: true }).autoincrement().notNull(), + clientId: int('id_client', { unsigned: true }).references((): MySqlColumn => emailLog.id, { + onDelete: 'set null', + onUpdate: 'cascade', + }), + receiverEmail: varchar('receiver_email', { length: 255 }).notNull(), + messageId: varchar('message_id', { length: 255 }), + contextId: int('context_id', { unsigned: true }), + contextType: mysqlEnum('context_type', ['test']).$type<['test']>(), + action: varchar('action', { length: 80 }).$type<['test']>(), + events: json('events').$type<{ t: 'test' }[]>(), + createdAt: timestamp('created_at', { mode: 'string' }).defaultNow().notNull(), + updatedAt: timestamp('updated_at', { mode: 'string' }).defaultNow().onUpdateNow(), + }, + (table) => { + return { + emailLogId: primaryKey({ columns: [table.id], name: 'email_log_id' }), + emailLogMessageIdUnique: unique('email_log_message_id_unique').on(table.messageId), + }; + }, + ); + + Expect< + Equal<{ + receiverEmail: string; + id?: number | undefined; + createdAt?: string | undefined; + clientId?: number | null | undefined; + messageId?: string | null | undefined; + contextId?: number | null | undefined; + contextType?: ['test'] | null | undefined; + action?: ['test'] | null | undefined; + events?: + | { + t: 'test'; + }[] + | null + | undefined; + updatedAt?: string | null | undefined; + }, typeof emailLog.$inferInsert> + >; +} From 7612dda4008083d38eddd06ae3879258adb5345d Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 9 Jul 2024 12:41:16 +0300 Subject: [PATCH 39/40] Update release notes --- changelogs/drizzle-orm/0.32.0.md | 220 +++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 changelogs/drizzle-orm/0.32.0.md diff --git a/changelogs/drizzle-orm/0.32.0.md b/changelogs/drizzle-orm/0.32.0.md new file mode 100644 index 000000000..a8a2c17e7 --- /dev/null +++ b/changelogs/drizzle-orm/0.32.0.md @@ -0,0 +1,220 @@ +# Release notes for `drizzle-orm@0.32.0` and `drizzle-kit@0.23.0` + +> It's not mandatory to upgrade both packages, but if you want to use the new features in both queries and migrations, you will need to upgrade both packages + +## New Features + +### 🎉 MySQL `$returningId()` function + +MySQL itself doesn't have native support for `RETURNING` after using `INSERT`. There is only one way to do it for `primary keys` with `autoincrement` (or `serial`) types, where you can access `insertId` and `affectedRows` fields. We've prepared an automatic way for you to handle such cases with Drizzle and automatically receive all inserted IDs as separate objects + +```ts +import { boolean, int, text, mysqlTable } from 'drizzle-orm/mysql-core'; + +const usersTable = mysqlTable('users', { + id: int('id').primaryKey(), + name: text('name').notNull(), + verified: boolean('verified').notNull().default(false), +}); + + +const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); +// ^? { id: number }[] +``` + +Also with Drizzle, you can specify a `primary key` with `$default` function that will generate custom primary keys at runtime. We will also return those generated keys for you in the `$returningId()` call + +```ts +import { varchar, text, mysqlTable } from 'drizzle-orm/mysql-core'; +import { createId } from '@paralleldrive/cuid2'; + +const usersTableDefFn = mysqlTable('users_default_fn', { + customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(createId), + name: text('name').notNull(), +}); + + +const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]).$returningId(); +// ^? { customId: string }[] +``` + +> If there is no primary keys -> type will be `{}[]` for such queries + +### 🎉 PostgreSQL Sequences + +You can now specify sequences in Postgres within any schema you need and define all the available properties + +##### **Example** + +```ts +import { pgSchema, pgSequence } from "drizzle-orm/pg-core"; + +// No params specified +export const customSequence = pgSequence("name"); + +// Sequence with params +export const customSequence = pgSequence("name", { + startWith: 100, + maxValue: 10000, + minValue: 100, + cycle: true, + cache: 10, + increment: 2 +}); + +// Sequence in custom schema +export const customSchema = pgSchema('custom_schema'); + +export const customSequence = customSchema.sequence("name"); +``` + +### 🎉 PostgreSQL Identity Columns + +[Source](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_serial): As mentioned, the `serial` type in Postgres is outdated and should be deprecated. Ideally, you should not use it. `Identity columns` are the recommended way to specify sequences in your schema, which is why we are introducing the `identity columns` feature + +##### **Example** + +```ts +import { pgTable, integer, text } from 'drizzle-orm/pg-core' + +export const ingredients = pgTable("ingredients", { + id: integer("id").primaryKey().generatedAlwaysAsIdentity({ startWith: 1000 }), + name: text("name").notNull(), + description: text("description"), +}); +``` + +You can specify all properties available for sequences in the `.generatedAlwaysAsIdentity()` function. Additionally, you can specify custom names for these sequences + +PostgreSQL docs [reference](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-IDENTITY). + +### 🎉 PostgreSQL Generated Columns + +You can now specify generated columns on any column supported by PostgreSQL to use with generated columns + +##### **Example** with generated column for `tsvector` + +> Note: we will add `tsVector` column type before latest release + +```ts +import { SQL, sql } from "drizzle-orm"; +import { customType, index, integer, pgTable, text } from "drizzle-orm/pg-core"; + +const tsVector = customType<{ data: string }>({ + dataType() { + return "tsvector"; + }, +}); + +export const test = pgTable( + "test", + { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + content: text("content"), + contentSearch: tsVector("content_search", { + dimensions: 3, + }).generatedAlwaysAs( + (): SQL => sql`to_tsvector('english', ${test.content})` + ), + }, + (t) => ({ + idx: index("idx_content_search").using("gin", t.contentSearch), + }) +); +``` + +In case you don't need to reference any columns from your table, you can use just `sql` template or a `string` + +```ts +export const users = pgTable("users", { + id: integer("id"), + name: text("name"), + generatedName: text("gen_name").generatedAlwaysAs(sql`hello world!`), + generatedName1: text("gen_name1").generatedAlwaysAs("hello world!"), +}), +``` + +### 🎉 MySQL Generated Columns + +You can now specify generated columns on any column supported by MySQL to use with generated columns + +You can specify both `stored` and `virtual` options, for more info you can check [MySQL docs](https://dev.mysql.com/doc/refman/8.4/en/create-table-generated-columns.html) + +Also MySQL has a few limitation for such columns usage, which is described [here](https://dev.mysql.com/doc/refman/8.4/en/alter-table-generated-columns.html) + +Drizzle Kit will also have limitations for `push` command: + +1. You can't change the generated constraint expression and type using `push`. Drizzle-kit will ignore this change. To make it work, you would need to `drop the column`, `push`, and then `add a column with a new expression`. This was done due to the complex mapping from the database side, where the schema expression will be modified on the database side and, on introspection, we will get a different string. We can't be sure if you changed this expression or if it was changed and formatted by the database. As long as these are generated columns and `push` is mostly used for prototyping on a local database, it should be fast to `drop` and `create` generated columns. Since these columns are `generated`, all the data will be restored + +2. `generate` should have no limitations + +##### **Example** + +```ts +export const users = mysqlTable("users", { + id: int("id"), + id2: int("id2"), + name: text("name"), + generatedName: text("gen_name").generatedAlwaysAs( + (): SQL => sql`${schema2.users.name} || 'hello'`, + { mode: "stored" } + ), + generatedName1: text("gen_name1").generatedAlwaysAs( + (): SQL => sql`${schema2.users.name} || 'hello'`, + { mode: "virtual" } + ), +}), +``` + +In case you don't need to reference any columns from your table, you can use just `sql` template or a `string` in `.generatedAlwaysAs()` + +### 🎉 SQLite Generated Columns + +You can now specify generated columns on any column supported by SQLite to use with generated columns + +You can specify both `stored` and `virtual` options, for more info you can check [SQLite docs](https://www.sqlite.org/gencol.html) + +Also SQLite has a few limitation for such columns usage, which is described [here](https://www.sqlite.org/gencol.html) + +Drizzle Kit will also have limitations for `push` and `generate` command: + +1. You can't change the generated constraint expression with the stored type in an existing table. You would need to delete this table and create it again. This is due to SQLite limitations for such actions. We will handle this case in future releases (it will involve the creation of a new table with data migration). + +2. You can't add a `stored` generated expression to an existing column for the same reason as above. However, you can add a `virtual` expression to an existing column. + +3. You can't change a `stored` generated expression in an existing column for the same reason as above. However, you can change a `virtual` expression. + +4. You can't change the generated constraint type from `virtual` to `stored` for the same reason as above. However, you can change from `stored` to `virtual`. + +## New Drizzle Kit features + +### 🎉 Migrations support for all the new orm features + +PostgreSQL sequences, identity columns and generated columns for all dialects + +### 🎉 New flag `--force` for `drizzle-kit push` + +You can auto-accept all data-loss statements using the push command. It's only available in CLI parameters. Make sure you always use it if you are fine with running data-loss statements on your database + +### 🎉 New `migrations` flag `prefix` + +You can now customize migration file prefixes to make the format suitable for your migration tools: + +- `index` is the default type and will result in `0001_name.sql` file names; +- `supabase` and `timestamp` are equal and will result in `20240627123900_name.sql` file names; +- `unix` will result in unix seconds prefixes `1719481298_name.sql` file names; +- `none` will omit the prefix completely; + + +##### **Example**: Supabase migrations format +```ts +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "postgresql", + migrations: { + prefix: 'supabase' + } +}); + +``` From aaf764c3c55f2dfd347328230d1554c4d2238760 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Tue, 9 Jul 2024 13:48:19 +0300 Subject: [PATCH 40/40] Open all tests --- drizzle-orm/src/tidb-serverless/session.ts | 41 +- .../tests/mysql/tidb-serverless.test.ts | 2814 +---------------- integration-tests/vitest.config.ts | 16 +- 3 files changed, 99 insertions(+), 2772 deletions(-) diff --git a/drizzle-orm/src/tidb-serverless/session.ts b/drizzle-orm/src/tidb-serverless/session.ts index e87c7a7e2..64a8d61d7 100644 --- a/drizzle-orm/src/tidb-serverless/session.ts +++ b/drizzle-orm/src/tidb-serverless/session.ts @@ -1,6 +1,7 @@ import type { Connection, ExecuteOptions, FullResult, Tx } from '@tidbcloud/serverless'; +import { Column } from '~/column.ts'; -import { entityKind } from '~/entity.ts'; +import { entityKind, is } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { NoopLogger } from '~/logger.ts'; import type { MySqlDialect } from '~/mysql-core/dialect.ts'; @@ -30,6 +31,10 @@ export class TiDBServerlessPreparedQuery ext private logger: Logger, private fields: SelectedFieldsOrdered | undefined, private customResultMapper?: (rows: unknown[][]) => T['execute'], + // Keys that were used in $default and the value that was generated for them + private generatedIds?: Record[], + // Keys that should be returned, it has the column with all properries + key from object + private returningIds?: SelectedFieldsOrdered, ) { super(); } @@ -39,9 +44,35 @@ export class TiDBServerlessPreparedQuery ext this.logger.logQuery(this.queryString, params); - const { fields, client, queryString, joinsNotNullableMap, customResultMapper } = this; + const { fields, client, queryString, joinsNotNullableMap, customResultMapper, returningIds, generatedIds } = this; if (!fields && !customResultMapper) { - return client.execute(queryString, params, executeRawConfig); + const res = await client.execute(queryString, params, executeRawConfig) as FullResult; + const insertId = res.lastInsertId ?? 0; + const affectedRows = res.rowsAffected ?? 0; + // for each row, I need to check keys from + if (returningIds) { + const returningResponse = []; + let j = 0; + for (let i = insertId; i < insertId + affectedRows; i++) { + for (const column of returningIds) { + const key = returningIds[0]!.path[0]!; + if (is(column.field, Column)) { + // @ts-ignore + if (column.field.primary && column.field.autoIncrement) { + returningResponse.push({ [key]: i }); + } + if (column.field.defaultFn && generatedIds) { + // generatedIds[rowIdx][key] + returningResponse.push({ [key]: generatedIds[j]![key] }); + } + } + } + j++; + } + + return returningResponse; + } + return res; } const rows = await client.execute(queryString, params, queryConfig) as unknown[][]; @@ -87,6 +118,8 @@ export class TiDBServerlessSession< query: Query, fields: SelectedFieldsOrdered | undefined, customResultMapper?: (rows: unknown[][]) => T['execute'], + generatedIds?: Record[], + returningIds?: SelectedFieldsOrdered, ): MySqlPreparedQuery { return new TiDBServerlessPreparedQuery( this.client, @@ -95,6 +128,8 @@ export class TiDBServerlessSession< this.logger, fields, customResultMapper, + generatedIds, + returningIds, ); } diff --git a/integration-tests/tests/mysql/tidb-serverless.test.ts b/integration-tests/tests/mysql/tidb-serverless.test.ts index 05199e836..8187882af 100644 --- a/integration-tests/tests/mysql/tidb-serverless.test.ts +++ b/integration-tests/tests/mysql/tidb-serverless.test.ts @@ -1,152 +1,16 @@ import 'dotenv/config'; import { connect } from '@tidbcloud/serverless'; -import { - and, - asc, - avg, - avgDistinct, - count, - countDistinct, - eq, - exists, - getTableColumns, - gt, - gte, - inArray, - lt, - max, - min, - Name, - sql, - sum, - sumDistinct, - TransactionRollbackError, -} from 'drizzle-orm'; -import { - alias, - bigint, - boolean, - date, - datetime, - decimal, - except, - exceptAll, - foreignKey, - getTableConfig, - getViewConfig, - int, - intersect, - intersectAll, - json, - mediumint, - mysqlEnum, - mysqlTable, - mysqlTableCreator, - mysqlView, - primaryKey, - serial, - smallint, - text, - time, - timestamp, - tinyint, - union, - unionAll, - unique, - uniqueIndex, - uniqueKeyName, - varchar, - year, -} from 'drizzle-orm/mysql-core'; import type { TiDBServerlessDatabase } from 'drizzle-orm/tidb-serverless'; import { drizzle } from 'drizzle-orm/tidb-serverless'; -import { migrate } from 'drizzle-orm/tidb-serverless/migrator'; -import { beforeAll, beforeEach, expect, test } from 'vitest'; -import { type Equal, Expect, toLocalDate } from '../utils.ts'; +import { beforeAll, beforeEach } from 'vitest'; +import { skipTests } from '~/common.ts'; +import { tests } from './mysql-common.ts'; const ENABLE_LOGGING = false; let db: TiDBServerlessDatabase; -const usersTable = mysqlTable('userstest', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: json('jsonb').$type(), - createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), -}); - -const users2Table = mysqlTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').references(() => citiesTable.id), -}); - -const citiesTable = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const usersOnUpdate = mysqlTable('users_on_update', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - updateCounter: int('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), - updatedAt: datetime('updated_at', { mode: 'date', fsp: 3 }).$onUpdate(() => new Date()), - uppercaseName: text('uppercase_name').$onUpdateFn(() => sql`upper(name)`), - alwaysNull: text('always_null').$type().$onUpdateFn(() => null), // need to add $type because $onUpdate add a default value -}); - -const datesTable = mysqlTable('datestable', { - date: date('date'), - dateAsString: date('date_as_string', { mode: 'string' }), - time: time('time', { fsp: 1 }), - datetime: datetime('datetime', { fsp: 2 }), - datetimeAsString: datetime('datetime_as_string', { fsp: 2, mode: 'string' }), - timestamp: timestamp('timestamp', { fsp: 3 }), - timestampAsString: timestamp('timestamp_as_string', { fsp: 3, mode: 'string' }), - year: year('year'), -}); - -const coursesTable = mysqlTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: int('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = mysqlTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = mysqlTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: int('amount').notNull(), - quantity: int('quantity').notNull(), -}); - -const usersMigratorTable = mysqlTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => { - return { - name: uniqueIndex('').on(table.name).using('btree'), - }; -}); - -// To test aggregate functions -const aggregateTable = mysqlTable('aggregate_table', { - id: serial('id').notNull(), - name: text('name').notNull(), - a: int('a'), - b: int('b'), - c: int('c'), - nullOnly: int('null_only'), -}); - beforeAll(async () => { const connectionString = process.env['TIDB_CONNECTION_STRING']; if (!connectionString) { @@ -157,2627 +21,55 @@ beforeAll(async () => { db = drizzle(client!, { logger: ENABLE_LOGGING }); }); -beforeEach(async () => { - await db.execute(sql`drop table if exists \`userstest\``); - await db.execute(sql`drop table if exists \`users2\``); - await db.execute(sql`drop table if exists \`cities\``); - - await db.execute( - sql` - create table \`userstest\` ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await db.execute( - sql` - create table \`users2\` ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references \`cities\`(\`id\`) - ) - `, - ); - - await db.execute( - sql` - create table \`cities\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); -}); - -async function setupSetOperationTest(db: TiDBServerlessDatabase) { - await db.execute(sql`drop table if exists \`users2\``); - await db.execute(sql`drop table if exists \`cities\``); - await db.execute( - sql` - create table \`users2\` ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references \`cities\`(\`id\`) - ) - `, - ); - - await db.execute( - sql` - create table \`cities\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await db.insert(citiesTable).values([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - await db.insert(users2Table).values([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 2 }, - { id: 3, name: 'Jack', cityId: 3 }, - { id: 4, name: 'Peter', cityId: 3 }, - { id: 5, name: 'Ben', cityId: 2 }, - { id: 6, name: 'Jill', cityId: 1 }, - { id: 7, name: 'Mary', cityId: 2 }, - { id: 8, name: 'Sally', cityId: 1 }, - ]); -} - -async function setupAggregateFunctionsTest(db: TiDBServerlessDatabase) { - await db.execute(sql`drop table if exists \`aggregate_table\``); - await db.execute( - sql` - create table \`aggregate_table\` ( - \`id\` integer primary key auto_increment not null, - \`name\` text not null, - \`a\` integer, - \`b\` integer, - \`c\` integer, - \`null_only\` integer - ); - `, - ); - await db.insert(aggregateTable).values([ - { name: 'value 1', a: 5, b: 10, c: 20 }, - { name: 'value 1', a: 5, b: 20, c: 30 }, - { name: 'value 2', a: 10, b: 50, c: 60 }, - { name: 'value 3', a: 20, b: 20, c: null }, - { name: 'value 4', a: null, b: 90, c: 120 }, - { name: 'value 5', a: 80, b: 10, c: null }, - { name: 'value 6', a: null, b: null, c: 150 }, - ]); -} - -test('table config: unsigned ints', async () => { - const unsignedInts = mysqlTable('cities1', { - bigint: bigint('bigint', { mode: 'number', unsigned: true }), - int: int('int', { unsigned: true }), - smallint: smallint('smallint', { unsigned: true }), - mediumint: mediumint('mediumint', { unsigned: true }), - tinyint: tinyint('tinyint', { unsigned: true }), - }); - - const tableConfig = getTableConfig(unsignedInts); - - const bigintColumn = tableConfig.columns.find((c) => c.name === 'bigint')!; - const intColumn = tableConfig.columns.find((c) => c.name === 'int')!; - const smallintColumn = tableConfig.columns.find((c) => c.name === 'smallint')!; - const mediumintColumn = tableConfig.columns.find((c) => c.name === 'mediumint')!; - const tinyintColumn = tableConfig.columns.find((c) => c.name === 'tinyint')!; - - expect(bigintColumn.getSQLType()).toEqual('bigint unsigned'); - expect(intColumn.getSQLType()).toEqual('int unsigned'); - expect(smallintColumn.getSQLType()).toEqual('smallint unsigned'); - expect(mediumintColumn.getSQLType()).toEqual('mediumint unsigned'); - expect(tinyintColumn.getSQLType()).toEqual('tinyint unsigned'); -}); - -test('table config: signed ints', async () => { - const unsignedInts = mysqlTable('cities1', { - bigint: bigint('bigint', { mode: 'number' }), - int: int('int'), - smallint: smallint('smallint'), - mediumint: mediumint('mediumint'), - tinyint: tinyint('tinyint'), - }); - - const tableConfig = getTableConfig(unsignedInts); - - const bigintColumn = tableConfig.columns.find((c) => c.name === 'bigint')!; - const intColumn = tableConfig.columns.find((c) => c.name === 'int')!; - const smallintColumn = tableConfig.columns.find((c) => c.name === 'smallint')!; - const mediumintColumn = tableConfig.columns.find((c) => c.name === 'mediumint')!; - const tinyintColumn = tableConfig.columns.find((c) => c.name === 'tinyint')!; - - expect(bigintColumn.getSQLType()).toEqual('bigint'); - expect(intColumn.getSQLType()).toEqual('int'); - expect(smallintColumn.getSQLType()).toEqual('smallint'); - expect(mediumintColumn.getSQLType()).toEqual('mediumint'); - expect(tinyintColumn.getSQLType()).toEqual('tinyint'); -}); - -test('table config: foreign keys name', async () => { - const table = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: foreignKey({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk' }), - })); - - const tableConfig = getTableConfig(table); - - expect(tableConfig.foreignKeys.length).toEqual(1); - expect(tableConfig.foreignKeys[0]!.getName()).toEqual('custom_fk'); -}); - -test('table config: primary keys name', async () => { - const table = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: primaryKey({ columns: [t.id, t.name], name: 'custom_pk' }), - })); - - const tableConfig = getTableConfig(table); - - expect(tableConfig.primaryKeys.length).toEqual(1); - expect(tableConfig.primaryKeys[0]!.getName()).toEqual('custom_pk'); -}); - -test('table configs: unique third param', async () => { - const cities1Table = mysqlTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: unique('custom_name').on(t.name, t.state), - f1: unique('custom_name1').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - expect(tableConfig.uniqueConstraints).toHaveLength(2); - - expect(tableConfig.uniqueConstraints[0]?.name).toEqual('custom_name'); - expect(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name)).toEqual(['name', 'state']); - - expect(tableConfig.uniqueConstraints[1]?.name).toEqual('custom_name1'); - expect(tableConfig.uniqueConstraints[1]?.columns.map((t) => t.name)).toEqual(['name', 'state']); -}); - -test('table configs: unique in column', async () => { - const cities1Table = mysqlTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - state: text('state').unique('custom'), - field: text('field').unique('custom_field'), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - expect(columnName?.uniqueName).toEqual(uniqueKeyName(cities1Table, [columnName!.name])); - expect(columnName?.isUnique).toEqual(true); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - expect(columnState?.uniqueName === 'custom').toEqual(true); - expect(columnState?.isUnique).toEqual(true); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - expect(columnField?.uniqueName === 'custom_field').toEqual(true); - expect(columnField?.isUnique).toEqual(true); -}); - -test('select all fields', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - expect(result[0]!.createdAt instanceof Date).toEqual(true); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // expect(Math.abs(result[0]!.createdAt.getTime() - now) < 2000).toEqual(true); - expect(result).toEqual([{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test('select sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - expect(users).toEqual([{ name: 'JOHN' }]); -}); - -test('select typed sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - expect(users).toEqual([{ name: 'JOHN' }]); -}); - -test('select distinct', async () => { - const usersDistinctTable = mysqlTable('users_distinct', { - id: int('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id int, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - expect(users).toEqual([{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test('insert returning sql', async () => { - const result = await db.insert(usersTable).values({ name: 'John' }); - - expect(result.lastInsertId).toEqual(1); -}); - -test('delete returning sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - expect(users.rowsAffected).toEqual(1); -}); - -test('update returning sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - expect(users.rowsAffected).toEqual(1); -}); - -test('update with returning all fields', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select().from(usersTable).where(eq(usersTable.id, 1)); - - expect(updatedUsers.rowsAffected).toEqual(1); - - expect(users[0]!.createdAt instanceof Date).toEqual(true); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // expect(Math.abs(users[0]!.createdAt.getTime() - now) < 2000).toEqual(true); - expect(users).toEqual([{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test('update with returning partial', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - expect(updatedUsers.rowsAffected).toEqual(1); - - expect(users).toEqual([{ id: 1, name: 'Jane' }]); -}); - -test('delete with returning all fields', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - expect(deletedUser.rowsAffected).toEqual(1); -}); - -test('delete with returning partial', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - expect(deletedUser.rowsAffected).toEqual(1); -}); - -test('insert + select', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - expect(result).toEqual([{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - expect(result2).toEqual([ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test('json insert', async () => { - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - expect(result).toEqual([{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test('insert with overridden default values', async () => { - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - expect(result).toEqual([{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test('insert many', async () => { - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - expect(result).toEqual([ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test('insert many with returning', async () => { - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - - expect(result.rowsAffected).toEqual(4); -}); - -test('select with group by as field', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name).orderBy(usersTable.name); - - expect(result).toEqual([{ name: 'Jane' }, { name: 'John' }]); -}); - -test('select with exists', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const user = alias(usersTable, 'user'); - const result = await db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ); - - expect(result).toEqual([{ name: 'John' }]); -}); - -test('select with group by as sql', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - expect(result).toEqual(expect.arrayContaining([{ name: 'Jane' }, { name: 'John' }])); -}); - -test('$default function', async () => { - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }); - const selectedOrder = await db.select().from(orders); - - expect(selectedOrder).toEqual([{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -// Default value for TEXT is not supported -test.skip('$default with empty array - text column', async () => { - await db.execute(sql`drop table if exists \`s_orders\``); - await db.execute( - sql` - create table \`s_orders\` ( - \`id\` serial primary key, - \`region\` text default ('Ukraine'), - \`product\` text not null - ) - `, - ); - - const users = mysqlTable('s_orders', { - id: serial('id').primaryKey(), - region: text('region').default('Ukraine'), - product: text('product').$defaultFn(() => 'random_string'), - }); - - await db.insert(users).values({}); - const selectedOrder = await db.select().from(users); - - expect(selectedOrder).toEqual([{ - id: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test('$default with empty array', async () => { - await db.execute(sql`drop table if exists \`s_orders\``); - await db.execute( - sql` - create table \`s_orders\` ( - \`id\` serial primary key, - \`region\` varchar(255) default 'Ukraine', - \`product\` text not null - ) - `, - ); - - const users = mysqlTable('s_orders', { - id: serial('id').primaryKey(), - region: varchar('region', { length: 255 }).default('Ukraine'), - product: text('product').$defaultFn(() => 'random_string'), - }); - - await db.insert(users).values({}); - const selectedOrder = await db.select().from(users); - - expect(selectedOrder).toEqual([{ - id: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test('select with group by as sql + column', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - expect(result).toEqual([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test('select with group by as column + sql', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - expect(result).toEqual([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test('select with group by complex query', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - expect(result).toEqual([{ name: 'Jane' }]); -}); - -test('build query', async () => { - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - expect(query).toEqual({ - sql: `select \`id\`, \`name\` from \`userstest\` group by \`userstest\`.\`id\`, \`userstest\`.\`name\``, - params: [], - }); -}); - -test('Query check: Insert all defaults in 1 row', async () => { - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - expect(query).toEqual({ - sql: 'insert into `users` (`id`, `name`, `state`) values (default, default, default)', - params: [], - }); -}); - -test('Query check: Insert all defaults in multiple rows', async () => { - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - expect(query).toEqual({ - sql: 'insert into `users` (`id`, `name`, `state`) values (default, default, default), (default, default, default)', - params: [], - }); -}); - -// Default value for TEXT is not supported -test.skip('Insert all defaults in 1 row - text column', async () => { - const users = mysqlTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default ('Dan'), state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - expect(res).toEqual([{ id: 1, name: 'Dan', state: null }]); -}); - -test('Insert all defaults in 1 row', async () => { - const users = mysqlTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: varchar('name', { length: 255 }).default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name varchar(255) default 'Dan', state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - expect(res).toEqual([{ id: 1, name: 'Dan', state: null }]); -}); - -// Default value for TEXT is not supported -test.skip('Insert all defaults in multiple rows - text column', async () => { - const users = mysqlTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default ('Dan'), state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - expect(res).toEqual([{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test('Insert all defaults in multiple rows', async () => { - const users = mysqlTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: varchar('name', { length: 255 }).default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name varchar(255) default 'Dan', state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - expect(res).toEqual([{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test('build query insert with onDuplicate', async () => { - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }) - .toSQL(); - - expect(query).toEqual({ - sql: - 'insert into `userstest` (`id`, `name`, `verified`, `jsonb`, `created_at`) values (default, ?, default, ?, default) on duplicate key update `name` = ?', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test('insert with onDuplicate', async () => { - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - expect(res).toEqual([{ id: 1, name: 'John1' }]); -}); - -test('insert conflict', async () => { - await db.insert(usersTable) - .values({ name: 'John' }); - - await expect(() => db.insert(usersTable).values({ id: 1, name: 'John1' })) - .rejects.toThrowError( - "Execute SQL fail: Error 1062 (23000): Duplicate entry '?' for key 'userstest.id'", - ); -}); - -test('insert conflict with ignore', async () => { - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .ignore() - .values({ id: 1, name: 'John1' }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - expect(res).toEqual([{ id: 1, name: 'John' }]); -}); - -test('insert sql', async () => { - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - expect(result).toEqual([{ id: 1, name: 'John' }]); -}); - -test('partial join with alias', async () => { - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - expect(result).toEqual([{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test('full join with alias', async () => { - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - expect(result).toEqual([{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('select from alias', async () => { - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - expect(result).toEqual([{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('insert with spaces', async () => { - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - expect(result).toEqual([{ id: 1, name: 'Jo h n' }]); -}); - -test('prepared statement', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare(); - const result = await statement.execute(); - - expect(result).toEqual([{ id: 1, name: 'John' }]); -}); - -test('prepared statement reuse', async () => { - const stmt = db.insert(usersTable).values({ - verified: true, - name: sql.placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - expect(result).toEqual([ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test('prepared statement with placeholder in .where', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, sql.placeholder('id'))) - .prepare(); - const result = await stmt.execute({ id: 1 }); - - expect(result).toEqual([{ id: 1, name: 'John' }]); -}); - -test('migrator', async () => { - await db.execute(sql`drop table if exists cities_migration`); - await db.execute(sql`drop table if exists users_migration`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/mysql' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table cities_migration`); - await db.execute(sql`drop table users_migration`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table __drizzle_migrations`); -}); - -test('insert via db.execute + select via db.execute', async () => { - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: string; name: string }>(sql`select id, name from ${usersTable}`); - expect(result.rows).toEqual([{ id: '1', name: 'John' }]); +beforeEach((ctx) => { + ctx.mysql = { + db, + }; }); -test('insert via db.execute w/ query builder', async () => { - const inserted = await db.execute( - db.insert(usersTable).values({ name: 'John' }), - ); - expect(inserted.rowsAffected).toEqual(1); -}); - -test('insert + select all possible dates', async () => { - await db.execute(sql`drop table if exists \`datestable\``); - await db.execute( - sql` - create table \`datestable\` ( - \`date\` date, - \`date_as_string\` date, - \`time\` time, - \`datetime\` datetime, - \`datetime_as_string\` datetime, - \`timestamp\` timestamp(3), - \`timestamp_as_string\` timestamp(3), - \`year\` year - ) - `, - ); - - const date = new Date('2022-11-11'); - const dateWithMilliseconds = new Date('2022-11-11 12:12:12.123'); - - await db.insert(datesTable).values({ - date: date, - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: date, - year: 22, - datetimeAsString: '2022-11-11 12:12:12', - timestamp: dateWithMilliseconds, - timestampAsString: '2022-11-11 12:12:12.123', - }); - - const res = await db.select().from(datesTable); - - expect(res[0]?.date instanceof Date).toEqual(true); // eslint-disable-line no-instanceof/no-instanceof - expect(res[0]?.datetime instanceof Date).toEqual(true); // eslint-disable-line no-instanceof/no-instanceof - expect(typeof res[0]?.dateAsString === 'string').toEqual(true); - expect(typeof res[0]?.datetimeAsString === 'string').toEqual(true); - - expect(res).toEqual([{ - date: toLocalDate(new Date('2022-11-11')), - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: new Date('2022-11-11'), - year: 2022, - datetimeAsString: '2022-11-11 12:12:12', - timestamp: new Date('2022-11-11 12:12:12.123'), - timestampAsString: '2022-11-11 12:12:12.123', - }]); - - await db.execute(sql`drop table if exists \`datestable\``); -}); - -const tableWithEnums = mysqlTable('enums_test_case', { - id: serial('id').primaryKey(), - enum1: mysqlEnum('enum1', ['a', 'b', 'c']).notNull(), - enum2: mysqlEnum('enum2', ['a', 'b', 'c']).default('a'), - enum3: mysqlEnum('enum3', ['a', 'b', 'c']).notNull().default('b'), -}); - -test('Mysql enum test case #1', async () => { - await db.execute(sql`drop table if exists \`enums_test_case\``); - - await db.execute(sql` - create table \`enums_test_case\` ( - \`id\` serial primary key, - \`enum1\` ENUM('a', 'b', 'c') not null, - \`enum2\` ENUM('a', 'b', 'c') default 'a', - \`enum3\` ENUM('a', 'b', 'c') not null default 'b' - ) - `); - - await db.insert(tableWithEnums).values([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum3: 'c' }, - { id: 3, enum1: 'a' }, - ]); - - const res = await db.select().from(tableWithEnums); - - await db.execute(sql`drop table \`enums_test_case\``); - - expect(res).toEqual([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum2: 'a', enum3: 'c' }, - { id: 3, enum1: 'a', enum2: 'a', enum3: 'b' }, - ]); -}); - -test('left join (flat object fields)', async () => { - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - expect(res).toEqual([ - { userId: 1, userName: 'John', cityId: 1, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test('left join (grouped fields)', async () => { - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - expect(res).toEqual([ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: 1, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test('left join (all fields)', async () => { - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - expect(res).toEqual([ - { - users2: { - id: 1, - name: 'John', - cityId: 1, - }, - cities: { - id: 1, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test('join subquery', async () => { - await db.execute(sql`drop table if exists \`courses\``); - await db.execute(sql`drop table if exists \`course_categories\``); - - await db.execute( - sql` - create table \`course_categories\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await db.execute( - sql` - create table \`courses\` ( - \`id\` serial primary key, - \`name\` text not null, - \`category_id\` int references \`course_categories\`(\`id\`) - ) - `, - ); - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - expect(res).toEqual([ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); - - await db.execute(sql`drop table if exists \`courses\``); - await db.execute(sql`drop table if exists \`course_categories\``); -}); - -test('with ... select', async () => { - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})`.mapWith(Number), - productSales: sql`sum(${orders.amount})`.mapWith(Number), - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - expect(result).toEqual([ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test('with ... update', async () => { - const products = mysqlTable('products', { - id: serial('id').primaryKey(), - price: decimal('price', { - precision: 15, - scale: 2, - }).notNull(), - cheap: boolean('cheap').notNull().default(false), - }); - - await db.execute(sql`drop table if exists ${products}`); - await db.execute(sql` - create table ${products} ( - id serial primary key, - price decimal(15, 2) not null, - cheap boolean not null default false - ) - `); - - await db.insert(products).values([ - { price: '10.99' }, - { price: '25.85' }, - { price: '32.99' }, - { price: '2.50' }, - { price: '4.59' }, - ]); - - const averagePrice = db - .$with('average_price') - .as( - db - .select({ - value: sql`avg(${products.price})`.as('value'), - }) - .from(products), - ); - - await db - .with(averagePrice) - .update(products) - .set({ - cheap: true, - }) - .where(lt(products.price, sql`(select * from ${averagePrice})`)); - - const result = await db - .select({ - id: products.id, - }) - .from(products) - .where(eq(products.cheap, true)); - - expect(result).toEqual([ - { id: 1 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test('with ... delete', async () => { - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const averageAmount = db - .$with('average_amount') - .as( - db - .select({ - value: sql`avg(${orders.amount})`.as('value'), - }) - .from(orders), - ); - - await db - .with(averageAmount) - .delete(orders) - .where(gt(orders.amount, sql`(select * from ${averageAmount})`)); - - const result = await db - .select({ - id: orders.id, - }) - .from(orders); - - expect(result).toEqual([ - { id: 1 }, - { id: 2 }, - { id: 3 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test('select from subquery sql', async () => { - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`concat(${users2Table.name}, " modified")`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - expect(res).toEqual([{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test('select a field without joining its table', () => { - expect(() => db.select({ name: users2Table.name }).from(usersTable).prepare()).toThrowError(); -}); - -test('select all fields from subquery without alias', () => { - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - expect(() => db.select().from(sq).prepare()).toThrowError(); -}); - -test('select count()', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - expect(res).toEqual([{ count: '2' }]); -}); - -test('select for ...', () => { - { - const query = db.select().from(users2Table).for('update').toSQL(); - expect(query.sql).toMatch(/ for update$/); - } - { - const query = db.select().from(users2Table).for('share', { skipLocked: true }).toSQL(); - expect(query.sql).toMatch(/ for share skip locked$/); - } - { - const query = db.select().from(users2Table).for('update', { noWait: true }).toSQL(); - expect(query.sql).toMatch(/ for update no wait$/); - } -}); - -test('having', async () => { - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.mapWith(Number).as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - expect(result).toEqual([ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test('view', async () => { - const newYorkers1 = mysqlView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).existing(); - - await db.execute(sql`drop view if exists ${newYorkers1}`); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - expect(result).toEqual([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - expect(result).toEqual([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - expect(result).toEqual([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - expect(result).toEqual([ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test('select from raw sql', async () => { - const result = await db.select({ - id: sql`id`.mapWith(Number), - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - expect(result).toEqual([ - { id: 1, name: 'John' }, - ]); -}); - -test('select from raw sql with joins', async () => { - const result = await db - .select({ - id: sql`users.id`.mapWith(Number), - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - expect(result).toEqual([ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test('join on aliased sql from select', async () => { - const result = await db - .select({ - userId: sql`users.id`.mapWith(Number).as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.mapWith(Number).as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - expect(result).toEqual([ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test('join on aliased sql from with clause', async () => { - const users = db.$with('users').as( - db.select({ - id: sql`id`.mapWith(Number).as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.mapWith(Number).as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - expect(result).toEqual([ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test('prefixed table', async () => { - const mysqlTable = mysqlTableCreator((name) => `myprefix_${name}`); - - const users = mysqlTable('test_prefixed_table_with_unique_name', { - id: int('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id int not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - expect(result).toEqual([{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('orderBy with aliased column', () => { - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - expect(query.sql).toEqual('select something as `test` from `users2` order by `test`'); -}); - -test('timestamp timezone', async () => { - const date = new Date(Date.parse('2020-01-01T12:34:56+07:00')); - - await db.insert(usersTable).values({ name: 'With default times' }); - await db.insert(usersTable).values({ - name: 'Without default times', - createdAt: date, - }); - const users = await db.select().from(usersTable); - - // check that the timestamps are set correctly for default times - expect(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000).toEqual(true); - - // check that the timestamps are set correctly for non default times - expect(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000).toEqual(true); -}); - -test('transaction', async () => { - const users = mysqlTable('users_transactions', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - const products = mysqlTable('products_transactions', { - id: serial('id').primaryKey(), - price: int('price').notNull(), - stock: int('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance int not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price int not null, stock int not null)`, - ); - - const { lastInsertId: userId } = await db.insert(users).values({ balance: 100 }); - const user = await db.select().from(users).where(eq(users.id, userId!)).then((rows) => rows[0]!); - const { lastInsertId: productId } = await db.insert(products).values({ price: 10, stock: 10 }); - const product = await db.select().from(products).where(eq(products.id, productId!)).then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - expect(result).toEqual([{ id: 1, balance: 90 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test('transaction rollback', async () => { - const users = mysqlTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance int not null)`, - ); - - await expect(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }) - ).rejects.toThrowError(TransactionRollbackError); - - const result = await db.select().from(users); - - expect(result).toEqual([]); - - await db.execute(sql`drop table ${users}`); -}); - -test('nested transaction', async () => { - const users = mysqlTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance int not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - expect(result).toEqual([{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('nested transaction rollback', async () => { - const users = mysqlTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance int not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await expect(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }) - ).rejects.toThrowError(TransactionRollbackError); - }); - - const result = await db.select().from(users); - - expect(result).toEqual([{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('join subquery with join', async () => { - const internalStaff = mysqlTable('internal_staff', { - userId: int('user_id').notNull(), - }); - - const customUser = mysqlTable('custom_user', { - id: int('id').notNull(), - }); - - const ticket = mysqlTable('ticket', { - staffId: int('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - expect(mainQuery).toEqual([{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test('subquery with view', async () => { - const users = mysqlTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - expect(result).toEqual([ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test('join view as subquery', async () => { - const users = mysqlTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - expect(result).toEqual([ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test('insert undefined', async () => { - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await expect(db.insert(users).values({ name: undefined })).resolves.not.toThrowError(); - - await db.execute(sql`drop table ${users}`); -}); - -test('update undefined', async () => { - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await expect(async () => await db.update(users).set({ name: undefined })).rejects.toThrowError(); - await expect(db.update(users).set({ id: 1, name: undefined })).resolves.not.toThrowError(); - - await db.execute(sql`drop table ${users}`); -}); - -test('utc config for datetime', async () => { - await db.execute(sql`drop table if exists \`datestable\``); - await db.execute( - sql` - create table \`datestable\` ( - \`datetime_utc\` datetime(3), - \`datetime\` datetime(3), - \`datetime_as_string\` datetime - ) - `, - ); - const datesTable = mysqlTable('datestable', { - datetimeUTC: datetime('datetime_utc', { fsp: 3, mode: 'date' }), - datetime: datetime('datetime', { fsp: 3 }), - datetimeAsString: datetime('datetime_as_string', { mode: 'string' }), - }); - - const dateObj = new Date('2022-11-11'); - const dateUtc = new Date('2022-11-11T12:12:12.122Z'); - - await db.insert(datesTable).values({ - datetimeUTC: dateUtc, - datetime: dateObj, - datetimeAsString: '2022-11-11 12:12:12', - }); - - const res = await db.select().from(datesTable); - - const rawSelect = await db.execute(sql`select \`datetime_utc\` from \`datestable\``); - const selectedRow = (rawSelect.rows as [{ datetime_utc: string }])[0]; - - expect(selectedRow.datetime_utc).toEqual('2022-11-11 12:12:12.122'); - expect(new Date(selectedRow.datetime_utc.replace(' ', 'T') + 'Z')).toEqual(dateUtc); - - expect(res[0]?.datetime instanceof Date).toEqual(true); // eslint-disable-line no-instanceof/no-instanceof - expect(res[0]?.datetimeUTC instanceof Date).toEqual(true); // eslint-disable-line no-instanceof/no-instanceof - expect(typeof res[0]?.datetimeAsString === 'string').toEqual(true); - - expect(res).toEqual([{ - datetimeUTC: dateUtc, - datetime: new Date('2022-11-11'), - datetimeAsString: '2022-11-11 12:12:12', - }]); - - await db.execute(sql`drop table if exists \`datestable\``); -}); - -test('set operations (union) from query builder with subquery', async () => { - await setupSetOperationTest(db); - const sq = db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).as('sq'); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).union( - db.select().from(sq).orderBy(sq.id), - ) - .orderBy(citiesTable.id) - .limit(8); - - expect(result).toHaveLength(8); - - expect(result).toEqual(expect.arrayContaining([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'Jack' }, - { id: 4, name: 'Peter' }, - { id: 5, name: 'Ben' }, - ])); - - // union should throw if selected fields are not in the same order - expect(() => - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).union( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table), - ) - ).toThrow(); -}); - -test('set operations (union) as function', async () => { - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - expect(result).toHaveLength(2); - - expect(result).toEqual(expect.arrayContaining([ - { id: 1, name: 'New York' }, - { id: 1, name: 'John' }, - ])); - - expect(() => { - union( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }).toThrow(); -}); - -test('set operations (union all) from query builder', async () => { - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)).limit(3); - - expect(result.length === 3).toEqual(true); - - expect(result).toEqual([ - { id: 1, name: 'New York' }, - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - expect(() => { - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).unionAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)); - }).toThrow(); -}); - -test('set operations (union all) as function', async () => { - await setupSetOperationTest(db); - - const result = await unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)) - .limit(1), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)) - .limit(1), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)) - .limit(1), - ); - - expect(result).toHaveLength(3); - - expect(result).toEqual(expect.arrayContaining([ - { id: 1, name: 'New York' }, - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - ])); - - expect(() => { - unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - }).toThrow(); -}); - -test('set operations (intersect) from query builder', async () => { - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - - expect(result.length === 2).toEqual(true); - - expect(result).toEqual([ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - expect(() => { - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - }).toThrow(); -}); - -test('set operations (intersect) as function', async () => { - await setupSetOperationTest(db); - - const result = await intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - - expect(result.length === 0).toEqual(true); - - expect(result).toEqual([]); - - expect(() => { - intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - }).toThrow(); -}); - -// "intersect all" is not supported in TiDB -test.skip('set operations (intersect all) from query builder', async () => { - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).intersectAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)); - - expect(result.length === 2).toEqual(true); - - expect(result).toEqual([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - expect(() => { - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).intersectAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)); - }).toThrow(); -}); - -// "intersect all" is not supported in TiDB -test.skip('set operations (intersect all) as function', async () => { - await setupSetOperationTest(db); - - const result = await intersectAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - expect(result.length === 1).toEqual(true); - - expect(result).toEqual([ - { id: 1, name: 'John' }, - ]); - - expect(() => { - intersectAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }).toThrow(); -}); - -test('set operations (except) from query builder', async () => { - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).except( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - - expect(result.length === 1).toEqual(true); - - expect(result).toEqual([ - { id: 1, name: 'New York' }, - ]); -}); - -test('set operations (except) as function', async () => { - await setupSetOperationTest(db); - - const result = await except( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable), - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(3); - - expect(result.length === 2).toEqual(true); - - expect(result).toEqual([ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - expect(() => { - except( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable), - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(3); - }).toThrow(); -}); - -// "except all" is not supported in TiDB -test.skip('set operations (except all) from query builder', async () => { - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).exceptAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - - expect(result.length === 2).toEqual(true); - - expect(result).toEqual([ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - expect(() => { - db - .select() - .from(citiesTable).exceptAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }).toThrow(); -}); - -// "except all" is not supported in TiDB -test.skip('set operations (except all) as function', async () => { - await setupSetOperationTest(db); - - const result = await exceptAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(6).orderBy(asc(sql.identifier('id'))); - - expect(result.length === 6).toEqual(true); - - expect(result).toEqual([ - { id: 2, name: 'Jane' }, - { id: 3, name: 'Jack' }, - { id: 4, name: 'Peter' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - { id: 7, name: 'Mary' }, - ]); - - expect(() => { - exceptAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(6); - }).toThrow(); -}); - -test('set operations (mixed) from query builder', async () => { - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).except( - ({ unionAll }) => - unionAll( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - db.select().from(citiesTable).where(eq(citiesTable.id, 2)), - ).orderBy(asc(citiesTable.id)).limit(1).offset(1), - ); - - expect(result.length === 2).toEqual(true); - - expect(result).toEqual([ - { id: 1, name: 'New York' }, - { id: 3, name: 'Tampa' }, - ]); - - expect(() => { - db - .select() - .from(citiesTable).except( - ({ unionAll }) => - unionAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - db.select().from(citiesTable).where(eq(citiesTable.id, 2)), - ), - ); - }).toThrow(); -}); - -test('set operations (mixed all) as function with subquery', async () => { - await setupSetOperationTest(db); - - const sq = except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 7)), - ).orderBy(asc(sql.identifier('id'))).as('sq'); - - const result = await union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)).orderBy(users2Table.id), - db.select().from(sq).limit(1).orderBy(sq.id), - db - .select().from(citiesTable).where(gt(citiesTable.id, 1)).orderBy(citiesTable.id), - ); - - expect(result).toHaveLength(4); - - expect(result).toEqual(expect.arrayContaining([ - { id: 1, name: 'John' }, - { id: 5, name: 'Ben' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ])); - - expect(() => { - union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 7)), - ).limit(1), - db - .select().from(citiesTable).where(gt(citiesTable.id, 1)), - ); - }).toThrow(); -}); - -test('aggregate function: count', async () => { - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: count() }).from(table); - const result2 = await db.select({ value: count(table.a) }).from(table); - const result3 = await db.select({ value: countDistinct(table.name) }).from(table); - - expect(result1[0]?.value).toEqual(7); - expect(result2[0]?.value).toEqual(5); - expect(result3[0]?.value).toEqual(6); -}); - -test('aggregate function: avg', async () => { - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: avg(table.b) }).from(table); - const result2 = await db.select({ value: avg(table.nullOnly) }).from(table); - const result3 = await db.select({ value: avgDistinct(table.b) }).from(table); - - expect(result1[0]?.value).toEqual('33.3333'); - expect(result2[0]?.value).toEqual(null); - expect(result3[0]?.value).toEqual('42.5000'); -}); - -test('aggregate function: sum', async () => { - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: sum(table.b) }).from(table); - const result2 = await db.select({ value: sum(table.nullOnly) }).from(table); - const result3 = await db.select({ value: sumDistinct(table.b) }).from(table); - - expect(result1[0]?.value).toEqual('200'); - expect(result2[0]?.value).toEqual(null); - expect(result3[0]?.value).toEqual('170'); -}); - -test('aggregate function: max', async () => { - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: max(table.b) }).from(table); - const result2 = await db.select({ value: max(table.nullOnly) }).from(table); - - expect(result1[0]?.value).toEqual(90); - expect(result2[0]?.value).toEqual(null); -}); - -test('aggregate function: min', async () => { - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: min(table.b) }).from(table); - const result2 = await db.select({ value: min(table.nullOnly) }).from(table); - - expect(result1[0]?.value).toEqual(10); - expect(result2[0]?.value).toEqual(null); -}); - -test('test $onUpdateFn and $onUpdate works as $default', async () => { - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial not null primary key, - name text not null, - update_counter integer default 1 not null, - updated_at datetime(3), - uppercase_name text, - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate); - - const response = await db.select({ ...rest }).from(usersOnUpdate); - - expect(response).toEqual([ - { name: 'John', id: 1, updateCounter: 1, uppercaseName: 'JOHN', alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, uppercaseName: 'JANE', alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, uppercaseName: 'JACK', alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, uppercaseName: 'JILL', alwaysNull: null }, - ]); - const msDelay = 2000; - - for (const eachUser of justDates) { - expect(eachUser.updatedAt!.valueOf() > Date.now() - msDelay).toEqual(true); - } -}); - -test('test $onUpdateFn and $onUpdate works updating', async () => { - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial not null primary key, - name text not null, - update_counter integer default 1 not null, - updated_at datetime(3), - uppercase_name text, - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John', alwaysNull: 'this will will be null after updating' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - const initial = await db.select({ updatedAt }).from(usersOnUpdate); - - await db.update(usersOnUpdate).set({ name: 'Angel', uppercaseName: null }).where(eq(usersOnUpdate.id, 1)); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate); - - const response = await db.select({ ...rest }).from(usersOnUpdate); - - expect(response).toEqual([ - { name: 'Angel', id: 1, updateCounter: 2, uppercaseName: null, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, uppercaseName: 'JANE', alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, uppercaseName: 'JACK', alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, uppercaseName: 'JILL', alwaysNull: null }, - ]); - const msDelay = 2000; - - expect(initial[0]?.updatedAt?.valueOf() !== justDates[0]?.updatedAt?.valueOf()).toEqual(true); - - for (const eachUser of justDates) { - expect(eachUser.updatedAt!.valueOf() > Date.now() - msDelay).toEqual(true); - } -}); +skipTests([ + 'mySchema :: select with group by as field', + 'mySchema :: delete with returning all fields', + 'mySchema :: update with returning partial', + 'mySchema :: delete returning sql', + 'mySchema :: insert returning sql', + 'test $onUpdateFn and $onUpdate works updating', + 'set operations (mixed all) as function with subquery', + 'set operations (union) from query builder with subquery', + 'join on aliased sql from with clause', + 'join on aliased sql from select', + 'select from raw sql with joins', + 'select from raw sql', + 'having', + 'select count()', + 'with ... select', + 'insert via db.execute w/ query builder', + 'insert via db.execute + select via db.execute', + 'select with group by as sql', + 'select with group by as field', + 'insert many with returning', + 'delete with returning partial', + 'delete with returning all fields', + 'update with returning partial', + 'update with returning all fields', + 'update returning sql', + 'delete returning sql', + 'insert returning sql', + + // not supported + 'set operations (except all) as function', + 'set operations (except all) from query builder', + 'set operations (intersect all) as function', + 'set operations (intersect all) from query builder', + 'set operations (union all) as function', + 'tc config for datetime', + 'select iterator w/ prepared statement', + 'select iterator', + 'transaction', + 'Insert all defaults in multiple rows', + 'Insert all defaults in 1 row', + '$default with empty array', + 'utc config for datetime', +]); + +tests(); diff --git a/integration-tests/vitest.config.ts b/integration-tests/vitest.config.ts index d4b93ff33..defc44cc4 100644 --- a/integration-tests/vitest.config.ts +++ b/integration-tests/vitest.config.ts @@ -5,15 +5,15 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { include: [ - // 'tests/extensions/postgis/**/*', - // 'tests/relational/**/*.test.ts', - // 'tests/pg/**/*.test.ts', + 'tests/extensions/postgis/**/*', + 'tests/relational/**/*.test.ts', + 'tests/pg/**/*.test.ts', 'tests/mysql/**/*.test.ts', - // 'tests/sqlite/**/*.test.ts', - // 'tests/replicas/**/*', - // 'tests/imports/**/*', - // 'tests/extensions/vectors/**/*', - // 'tests/version.test.ts', + 'tests/sqlite/**/*.test.ts', + 'tests/replicas/**/*', + 'tests/imports/**/*', + 'tests/extensions/vectors/**/*', + 'tests/version.test.ts', ], exclude: [ ...(process.env.SKIP_EXTERNAL_DB_TESTS