From dd09dac67b83b2c77ee23927537f3e01fba90a7f Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 26 Aug 2024 10:22:19 +0300 Subject: [PATCH 01/56] Test implementation --- drizzle-orm/src/monodriver.ts | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 drizzle-orm/src/monodriver.ts diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts new file mode 100644 index 000000000..c1b2b4df1 --- /dev/null +++ b/drizzle-orm/src/monodriver.ts @@ -0,0 +1,81 @@ +import type { ClientConfig as NeonHttpConfig, PoolConfig as NeonServerlessConfig } from '@neondatabase/serverless'; +import type { VercelPostgresPoolConfig } from '@vercel/postgres'; +import type { PoolConfig } from 'pg'; +import type { Options, PostgresType } from 'postgres'; +import type { NodePgDatabase } from './node-postgres'; +import type { DrizzleConfig } from './utils'; + +type DatabaseClientType = + | 'node-postgres' + | 'postgres.js' + | 'neon-serverless' + | 'neon-http' + | 'vercel-postgres' + | 'aws-data-api' + | 'planetscale' + | 'mysql2' + | 'tidb-serverless' + | 'libsql' + | 'd1' + | 'bun-sqlite' + | 'better-sqlite3'; + +type ClientConfigMap = { + 'node-postgres': PoolConfig; + 'postgres.js': Options>; + 'neon-serverless': NeonServerlessConfig; + 'neon-http': NeonHttpConfig; + 'vercel-postgres': {}; + 'aws-data-api': {}; + planetscale: {}; + mysql2: {}; + 'tidb-serverless': {}; + 'mysql-http-proxy': {}; + libsql: {}; + d1: {}; + 'bun-sqlite': {}; + 'better-sqlite3': {}; + 'sqlite-http-proxy': {}; +}; + +type ClientDrizzleInstanceMap> = { + 'node-postgres': NodePgDatabase; + 'postgres.js': {}; + 'neon-serverless': {}; + 'neon-http': {}; + 'vercel-postgres': {}; + 'aws-data-api': {}; + planetscale: {}; + mysql2: {}; + 'tidb-serverless': {}; + 'mysql-http-proxy': {}; + libsql: {}; + d1: {}; + 'bun-sqlite': {}; + 'better-sqlite3': {}; + 'sqlite-http-proxy': {}; +}; + +type ClientParams = ClientConfigMap[TClientType]; + +type InitializerParams< + TClientType extends DatabaseClientType, + TSchema extends Record = Record, +> = { + client: TClientType; + connection: ClientParams; +} & DrizzleConfig; + +type DetermineClient< + TParams extends InitializerParams, + TSchema extends Record = TParams['schema'] extends Record ? TParams['schema'] + : Record, +> = ClientDrizzleInstanceMap[TParams['client']]; + +export const drizzle = < + TClientType extends DatabaseClientType, + TSchema extends Record, + TParams extends InitializerParams, +>(params: TParams): DetermineClient => { + return {} as any; +}; From 25d3753e0d3999d590c566880c907b56f1a613dc Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 26 Aug 2024 11:06:47 +0300 Subject: [PATCH 02/56] Type-level implementation --- drizzle-orm/src/monodriver.ts | 114 +++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 31 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index c1b2b4df1..bc31f6893 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -1,9 +1,60 @@ +import type { RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; +import type { Config as LibsqlConfig } from '@libsql/client'; import type { ClientConfig as NeonHttpConfig, PoolConfig as NeonServerlessConfig } from '@neondatabase/serverless'; -import type { VercelPostgresPoolConfig } from '@vercel/postgres'; -import type { PoolConfig } from 'pg'; -import type { Options, PostgresType } from 'postgres'; +import type { Config as PlanetscaleConfig } from '@planetscale/database'; +import type { Config as TiDBServerlessConfig } from '@tidbcloud/serverless'; +import type { VercelPostgresPoolConfig as VercelPostgresConfig } from '@vercel/postgres'; +import type { Options as BetterSQLite3Options } from 'better-sqlite3'; +import type { ConnectionConfig as Mysql2Config } from 'mysql2'; +import type { PoolConfig as NodePGPoolConfig } from 'pg'; +import type { Options as PostgresJSOptions, PostgresType as PostgresJSPostgresType } from 'postgres'; +import type { AwsDataApiPgDatabase } from './aws-data-api/pg'; +import type { BetterSQLite3Database } from './better-sqlite3'; +import type { BunSQLiteDatabase } from './bun-sqlite'; +import type { DrizzleD1Database } from './d1'; +import type { LibSQLDatabase } from './libsql'; +import type { MySql2Database } from './mysql2'; +import type { NeonHttpDatabase } from './neon-http'; +import type { NeonDatabase } from './neon-serverless'; import type { NodePgDatabase } from './node-postgres'; +import type { PlanetScaleDatabase } from './planetscale-serverless'; +import type { PostgresJsDatabase } from './postgres-js'; +import type { TiDBServerlessDatabase } from './tidb-serverless'; import type { DrizzleConfig } from './utils'; +import type { VercelPgDatabase } from './vercel-postgres'; + +type BunSqliteDatabaseOptions = + | number + | { + /** + * Open the database as read-only (no write operations, no create). + * + * Equivalent to {@link constants.SQLITE_OPEN_READONLY} + */ + readonly?: boolean; + /** + * Allow creating a new database + * + * Equivalent to {@link constants.SQLITE_OPEN_CREATE} + */ + create?: boolean; + /** + * Open the database as read-write + * + * Equivalent to {@link constants.SQLITE_OPEN_READWRITE} + */ + readwrite?: boolean; + }; + +type BunSqliteDatabaseConfig = { + filename?: string; + options?: BunSqliteDatabaseOptions; +}; + +type BetterSQLite3DatabaseConfig = { + filename?: string | Buffer; + options?: BetterSQLite3Options; +}; type DatabaseClientType = | 'node-postgres' @@ -21,39 +72,35 @@ type DatabaseClientType = | 'better-sqlite3'; type ClientConfigMap = { - 'node-postgres': PoolConfig; - 'postgres.js': Options>; + 'node-postgres': NodePGPoolConfig; + 'postgres.js': PostgresJSOptions>; 'neon-serverless': NeonServerlessConfig; 'neon-http': NeonHttpConfig; - 'vercel-postgres': {}; - 'aws-data-api': {}; - planetscale: {}; - mysql2: {}; - 'tidb-serverless': {}; - 'mysql-http-proxy': {}; - libsql: {}; - d1: {}; - 'bun-sqlite': {}; - 'better-sqlite3': {}; - 'sqlite-http-proxy': {}; + 'vercel-postgres': VercelPostgresConfig; + 'aws-data-api': RDSConfig; + planetscale: PlanetscaleConfig; + mysql2: Mysql2Config; + 'tidb-serverless': TiDBServerlessConfig; + libsql: LibsqlConfig; + d1: D1Database; + 'bun-sqlite': BunSqliteDatabaseConfig; + 'better-sqlite3': BetterSQLite3DatabaseConfig; }; type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; - 'postgres.js': {}; - 'neon-serverless': {}; - 'neon-http': {}; - 'vercel-postgres': {}; - 'aws-data-api': {}; - planetscale: {}; - mysql2: {}; - 'tidb-serverless': {}; - 'mysql-http-proxy': {}; - libsql: {}; - d1: {}; - 'bun-sqlite': {}; - 'better-sqlite3': {}; - 'sqlite-http-proxy': {}; + 'postgres.js': PostgresJsDatabase; + 'neon-serverless': NeonDatabase; + 'neon-http': NeonHttpDatabase; + 'vercel-postgres': VercelPgDatabase; + 'aws-data-api': AwsDataApiPgDatabase; + planetscale: PlanetScaleDatabase; + mysql2: MySql2Database; + 'tidb-serverless': TiDBServerlessDatabase; + libsql: LibSQLDatabase; + d1: DrizzleD1Database; + 'bun-sqlite': BunSQLiteDatabase; + 'better-sqlite3': BetterSQLite3Database; }; type ClientParams = ClientConfigMap[TClientType]; @@ -76,6 +123,11 @@ export const drizzle = < TClientType extends DatabaseClientType, TSchema extends Record, TParams extends InitializerParams, ->(params: TParams): DetermineClient => { +>({ + client, + connection, + logger, + schema, +}: TParams): DetermineClient => { return {} as any; }; From 8439bf11d31fcb356aa603a6e1c701ba24fac0e1 Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 26 Aug 2024 12:26:50 +0300 Subject: [PATCH 03/56] Added functional runtimes for node-pg, aws-data-api-pg [custom drizzle config WIP] --- drizzle-orm/src/monodriver.ts | 70 +++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index bc31f6893..298d9be34 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -8,7 +8,7 @@ import type { Options as BetterSQLite3Options } from 'better-sqlite3'; import type { ConnectionConfig as Mysql2Config } from 'mysql2'; import type { PoolConfig as NodePGPoolConfig } from 'pg'; import type { Options as PostgresJSOptions, PostgresType as PostgresJSPostgresType } from 'postgres'; -import type { AwsDataApiPgDatabase } from './aws-data-api/pg'; +import type { AwsDataApiPgDatabase, DrizzleAwsDataApiPgConfig } from './aws-data-api/pg'; import type { BetterSQLite3Database } from './better-sqlite3'; import type { BunSQLiteDatabase } from './bun-sqlite'; import type { DrizzleD1Database } from './d1'; @@ -62,7 +62,7 @@ type DatabaseClientType = | 'neon-serverless' | 'neon-http' | 'vercel-postgres' - | 'aws-data-api' + | 'aws-data-api-pg' | 'planetscale' | 'mysql2' | 'tidb-serverless' @@ -71,13 +71,13 @@ type DatabaseClientType = | 'bun-sqlite' | 'better-sqlite3'; -type ClientConfigMap = { +type ClientConnectionConfigMap = { 'node-postgres': NodePGPoolConfig; 'postgres.js': PostgresJSOptions>; 'neon-serverless': NeonServerlessConfig; 'neon-http': NeonHttpConfig; 'vercel-postgres': VercelPostgresConfig; - 'aws-data-api': RDSConfig; + 'aws-data-api-pg': RDSConfig; planetscale: PlanetscaleConfig; mysql2: Mysql2Config; 'tidb-serverless': TiDBServerlessConfig; @@ -87,13 +87,29 @@ type ClientConfigMap = { 'better-sqlite3': BetterSQLite3DatabaseConfig; }; +// type ClientDrizzleConfigMap> = { +// 'node-postgres': DrizzleConfig; +// 'postgres.js': DrizzleConfig; +// 'neon-serverless': DrizzleConfig; +// 'neon-http': DrizzleConfig; +// 'vercel-postgres': DrizzleConfig; +// 'aws-data-api-pg': DrizzleAwsDataApiPgConfig; +// planetscale: DrizzleConfig; +// mysql2: DrizzleConfig; +// 'tidb-serverless': DrizzleConfig; +// libsql: DrizzleConfig; +// d1: DrizzleConfig; +// 'bun-sqlite': DrizzleConfig; +// 'better-sqlite3': DrizzleConfig; +// }; + type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; 'postgres.js': PostgresJsDatabase; 'neon-serverless': NeonDatabase; 'neon-http': NeonHttpDatabase; 'vercel-postgres': VercelPgDatabase; - 'aws-data-api': AwsDataApiPgDatabase; + 'aws-data-api-pg': AwsDataApiPgDatabase; planetscale: PlanetScaleDatabase; mysql2: MySql2Database; 'tidb-serverless': TiDBServerlessDatabase; @@ -103,7 +119,7 @@ type ClientDrizzleInstanceMap> = { 'better-sqlite3': BetterSQLite3Database; }; -type ClientParams = ClientConfigMap[TClientType]; +type ClientParams = ClientConnectionConfigMap[TClientType]; type InitializerParams< TClientType extends DatabaseClientType, @@ -115,19 +131,41 @@ type InitializerParams< type DetermineClient< TParams extends InitializerParams, - TSchema extends Record = TParams['schema'] extends Record ? TParams['schema'] + TSchema extends Record = TParams extends { schema: Record } ? TParams['schema'] : Record, -> = ClientDrizzleInstanceMap[TParams['client']]; +> = TParams extends { client: DatabaseClientType } ? ClientDrizzleInstanceMap[TParams['client']] + : never; + +import { drizzle as rdsPgDrizzle } from './aws-data-api/pg/index.ts'; +import { drizzle as pgDrizzle } from './node-postgres/index.ts'; -export const drizzle = < +export const drizzle = async < TClientType extends DatabaseClientType, TSchema extends Record, TParams extends InitializerParams, ->({ - client, - connection, - logger, - schema, -}: TParams): DetermineClient => { - return {} as any; +>(params: TParams): Promise> => { + const { client, connection } = params; + const drizzleConfig = params as Omit; + // @ts-expect-error + delete drizzleConfig.client; + // @ts-expect-error + delete drizzleConfig.connection; + + switch (client) { + case 'node-postgres': { + const { Pool } = await import('pg'); + const instance = new Pool(connection as NodePGPoolConfig); + + return pgDrizzle(instance, drizzleConfig) as any; + } + case 'aws-data-api-pg': { + const { RDSDataClient } = await import('@aws-sdk/client-rds-data'); + const instance = new RDSDataClient(connection); + + return rdsPgDrizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; + } + } + + // @ts-ignore + return {}; }; From f2115f83916ca6e886427fa24f11c5f09aac04fb Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 26 Aug 2024 13:51:44 +0300 Subject: [PATCH 04/56] Resolved bun type related build issues --- drizzle-kit/build.ts | 3 +++ drizzle-kit/package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/drizzle-kit/build.ts b/drizzle-kit/build.ts index 701e9c84c..ec7fc76c0 100644 --- a/drizzle-kit/build.ts +++ b/drizzle-kit/build.ts @@ -1,3 +1,4 @@ +/// import * as esbuild from 'esbuild'; import { readFileSync, writeFileSync } from 'node:fs'; import * as tsup from 'tsup'; @@ -16,6 +17,7 @@ const driversPackages = [ // sqlite drivers '@libsql/client', 'better-sqlite3', + 'bun:sqlite', ]; esbuild.buildSync({ @@ -82,6 +84,7 @@ const main = async () => { await tsup.build({ entryPoints: ['./src/index.ts', './src/api.ts'], outDir: './dist', + external: ['bun:sqlite'], splitting: false, dts: true, format: ['cjs', 'esm'], diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index 9d9e1d227..552c14d0e 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -74,6 +74,7 @@ "@vercel/postgres": "^0.8.0", "ava": "^5.1.0", "better-sqlite3": "^9.4.3", + "bun-types": "^0.6.6", "camelcase": "^7.0.1", "chalk": "^5.2.0", "commander": "^12.1.0", From ab07dd7f295416bd19bd08fd60b165601aef2d89 Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 26 Aug 2024 13:52:00 +0300 Subject: [PATCH 05/56] Implemented extra drivers --- drizzle-orm/src/index.ts | 1 + drizzle-orm/src/monodriver.ts | 93 ++++++++++++++++++++++++----------- pnpm-lock.yaml | 25 +++++----- 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/drizzle-orm/src/index.ts b/drizzle-orm/src/index.ts index bc72260b9..5cabdb0d8 100644 --- a/drizzle-orm/src/index.ts +++ b/drizzle-orm/src/index.ts @@ -5,6 +5,7 @@ export * from './entity.ts'; export * from './errors.ts'; export * from './expressions.ts'; export * from './logger.ts'; +export * from './monodriver.ts'; export * from './operations.ts'; export * from './query-promise.ts'; export * from './relations.ts'; diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 298d9be34..b6b48c91b 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -1,3 +1,4 @@ +/// import type { RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; import type { Config as LibsqlConfig } from '@libsql/client'; import type { ClientConfig as NeonHttpConfig, PoolConfig as NeonServerlessConfig } from '@neondatabase/serverless'; @@ -8,20 +9,20 @@ import type { Options as BetterSQLite3Options } from 'better-sqlite3'; import type { ConnectionConfig as Mysql2Config } from 'mysql2'; import type { PoolConfig as NodePGPoolConfig } from 'pg'; import type { Options as PostgresJSOptions, PostgresType as PostgresJSPostgresType } from 'postgres'; -import type { AwsDataApiPgDatabase, DrizzleAwsDataApiPgConfig } from './aws-data-api/pg'; -import type { BetterSQLite3Database } from './better-sqlite3'; -import type { BunSQLiteDatabase } from './bun-sqlite'; -import type { DrizzleD1Database } from './d1'; -import type { LibSQLDatabase } from './libsql'; -import type { MySql2Database } from './mysql2'; -import type { NeonHttpDatabase } from './neon-http'; -import type { NeonDatabase } from './neon-serverless'; -import type { NodePgDatabase } from './node-postgres'; -import type { PlanetScaleDatabase } from './planetscale-serverless'; -import type { PostgresJsDatabase } from './postgres-js'; -import type { TiDBServerlessDatabase } from './tidb-serverless'; -import type { DrizzleConfig } from './utils'; -import type { VercelPgDatabase } from './vercel-postgres'; +import type { AwsDataApiPgDatabase, DrizzleAwsDataApiPgConfig } from './aws-data-api/pg/index.ts'; +import type { BetterSQLite3Database } from './better-sqlite3/index.ts'; +import type { BunSQLiteDatabase } from './bun-sqlite/index.ts'; +import type { DrizzleD1Database } from './d1/index.ts'; +import type { LibSQLDatabase } from './libsql/index.ts'; +import type { MySql2Database } from './mysql2/index.ts'; +import type { NeonHttpDatabase } from './neon-http/index.ts'; +import type { NeonDatabase } from './neon-serverless/index.ts'; +import type { NodePgDatabase } from './node-postgres/index.ts'; +import type { PlanetScaleDatabase } from './planetscale-serverless/index.ts'; +import type { PostgresJsDatabase } from './postgres-js/index.ts'; +import type { TiDBServerlessDatabase } from './tidb-serverless/index.ts'; +import type { DrizzleConfig } from './utils.ts'; +import type { VercelPgDatabase } from './vercel-postgres/index.ts'; type BunSqliteDatabaseOptions = | number @@ -87,22 +88,6 @@ type ClientConnectionConfigMap = { 'better-sqlite3': BetterSQLite3DatabaseConfig; }; -// type ClientDrizzleConfigMap> = { -// 'node-postgres': DrizzleConfig; -// 'postgres.js': DrizzleConfig; -// 'neon-serverless': DrizzleConfig; -// 'neon-http': DrizzleConfig; -// 'vercel-postgres': DrizzleConfig; -// 'aws-data-api-pg': DrizzleAwsDataApiPgConfig; -// planetscale: DrizzleConfig; -// mysql2: DrizzleConfig; -// 'tidb-serverless': DrizzleConfig; -// libsql: DrizzleConfig; -// d1: DrizzleConfig; -// 'bun-sqlite': DrizzleConfig; -// 'better-sqlite3': DrizzleConfig; -// }; - type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; 'postgres.js': PostgresJsDatabase; @@ -137,7 +122,18 @@ type DetermineClient< : never; import { drizzle as rdsPgDrizzle } from './aws-data-api/pg/index.ts'; +import { drizzle as betterSqliteDrizzle } from './better-sqlite3/index.ts'; +import { drizzle as bunSqliteDrizzle } from './bun-sqlite/index.ts'; +import { drizzle as d1Drizzle } from './d1/index.ts'; +import { drizzle as libsqlDrizzle } from './libsql/index.ts'; +// import { drizzle as mysql2Drizzle } from './mysql2/index.ts'; +// import { drizzle as neonHttpDrizzle } from './neon-http/index.ts'; +// import { drizzle as neonDrizzle } from './neon-serverless/index.ts'; import { drizzle as pgDrizzle } from './node-postgres/index.ts'; +// import { drizzle as planetscaleDrizzle } from './planetscale-serverless/index.ts'; +// import { drizzle as postgresJSDrizzle } from './postgres-js/index.ts'; +// import { drizzle as tidbDrizzle } from './tidb-serverless/index.ts'; +// import { drizzle as vercelDrizzle } from './vercel-postgres/index.ts'; export const drizzle = async < TClientType extends DatabaseClientType, @@ -164,6 +160,43 @@ export const drizzle = async < return rdsPgDrizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; } + case 'better-sqlite3': { + const { default: Client } = await import('better-sqlite3'); + const { filename, options } = connection as BetterSQLite3DatabaseConfig; + const instance = new Client(filename, options); + + return betterSqliteDrizzle(instance, drizzleConfig) as any; + } + case 'bun-sqlite': { + const { Database: Client } = await import('bun:sqlite'); + const { filename, options } = connection as BunSqliteDatabaseConfig; + const instance = new Client(filename, options); + + return bunSqliteDrizzle(instance, drizzleConfig) as any; + } + case 'd1': { + return d1Drizzle(connection as D1Database, drizzleConfig) as any; + } + case 'libsql': { + const { createClient } = await import('@libsql/client'); + const instance = createClient(connection as LibsqlConfig); + + return libsqlDrizzle(instance, drizzleConfig) as any; + } + // case 'mysql2': { + // } + // case 'neon-http': { + // } + // case 'neon-serverless': { + // } + // case 'planetscale': { + // } + // case 'postgres.js': { + // } + // case 'tidb-serverless': { + // } + // case 'vercel-postgres': { + // } } // @ts-ignore diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2d091ad6..209d2db3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -182,6 +182,9 @@ importers: better-sqlite3: specifier: ^9.4.3 version: 9.6.0 + bun-types: + specifier: ^0.6.6 + version: 0.6.14 camelcase: specifier: ^7.0.1 version: 7.0.1 @@ -10210,7 +10213,7 @@ snapshots: '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/core': 3.582.0 - '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 '@aws-sdk/middleware-logger': 3.577.0 '@aws-sdk/middleware-recursion-detection': 3.577.0 @@ -10300,7 +10303,7 @@ snapshots: '@aws-crypto/sha256-js': 3.0.0 '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/core': 3.582.0 - '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 '@aws-sdk/middleware-logger': 3.577.0 '@aws-sdk/middleware-recursion-detection': 3.577.0 @@ -10610,7 +10613,7 @@ snapshots: '@aws-crypto/sha256-js': 3.0.0 '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/core': 3.582.0 - '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 '@aws-sdk/middleware-logger': 3.577.0 '@aws-sdk/middleware-recursion-detection': 3.577.0 @@ -10799,12 +10802,12 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-ini@3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.583.0)': + '@aws-sdk/credential-provider-ini@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/credential-provider-env': 3.577.0 '@aws-sdk/credential-provider-process': 3.577.0 - '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/types': 3.577.0 '@smithy/credential-provider-imds': 3.0.0 @@ -10889,13 +10892,13 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.583.0)': + '@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: '@aws-sdk/credential-provider-env': 3.577.0 '@aws-sdk/credential-provider-http': 3.582.0 - '@aws-sdk/credential-provider-ini': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/credential-provider-ini': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/credential-provider-process': 3.577.0 - '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/types': 3.577.0 '@smithy/credential-provider-imds': 3.0.0 @@ -10970,10 +10973,10 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-sso@3.583.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))': + '@aws-sdk/credential-provider-sso@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-sdk/client-sso': 3.583.0 - '@aws-sdk/token-providers': 3.577.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)) + '@aws-sdk/token-providers': 3.577.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/types': 3.577.0 '@smithy/property-provider': 3.0.0 '@smithy/shared-ini-file-loader': 3.0.0 @@ -11216,7 +11219,7 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.6.2 - '@aws-sdk/token-providers@3.577.0(@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0))': + '@aws-sdk/token-providers@3.577.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/types': 3.577.0 From 5f493794d0d234c12b0bfdedaf5d8c4253d7af92 Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Tue, 27 Aug 2024 02:43:07 +0300 Subject: [PATCH 06/56] Complete runtime implementations [TO BE TESTED] --- drizzle-orm/src/monodriver.ts | 110 +++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 35 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index b6b48c91b..33c9bddbe 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -1,10 +1,11 @@ -/// import type { RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; import type { Config as LibsqlConfig } from '@libsql/client'; -import type { ClientConfig as NeonHttpConfig, PoolConfig as NeonServerlessConfig } from '@neondatabase/serverless'; +import type { + HTTPTransactionOptions as NeonHttpConfig, + PoolConfig as NeonServerlessConfig, +} from '@neondatabase/serverless'; import type { Config as PlanetscaleConfig } from '@planetscale/database'; import type { Config as TiDBServerlessConfig } from '@tidbcloud/serverless'; -import type { VercelPostgresPoolConfig as VercelPostgresConfig } from '@vercel/postgres'; import type { Options as BetterSQLite3Options } from 'better-sqlite3'; import type { ConnectionConfig as Mysql2Config } from 'mysql2'; import type { PoolConfig as NodePGPoolConfig } from 'pg'; @@ -14,7 +15,7 @@ import type { BetterSQLite3Database } from './better-sqlite3/index.ts'; import type { BunSQLiteDatabase } from './bun-sqlite/index.ts'; import type { DrizzleD1Database } from './d1/index.ts'; import type { LibSQLDatabase } from './libsql/index.ts'; -import type { MySql2Database } from './mysql2/index.ts'; +import type { MySql2Database, MySql2DrizzleConfig } from './mysql2/index.ts'; import type { NeonHttpDatabase } from './neon-http/index.ts'; import type { NeonDatabase } from './neon-serverless/index.ts'; import type { NodePgDatabase } from './node-postgres/index.ts'; @@ -57,6 +58,15 @@ type BetterSQLite3DatabaseConfig = { options?: BetterSQLite3Options; }; +type MonodriverMysql2Config = Mysql2Config & { + mode?: MySql2DrizzleConfig['mode']; +}; + +type MonodriverNeonHttpConfig = { + connectionString: string; + options?: NeonHttpConfig; +}; + type DatabaseClientType = | 'node-postgres' | 'postgres.js' @@ -76,11 +86,11 @@ type ClientConnectionConfigMap = { 'node-postgres': NodePGPoolConfig; 'postgres.js': PostgresJSOptions>; 'neon-serverless': NeonServerlessConfig; - 'neon-http': NeonHttpConfig; - 'vercel-postgres': VercelPostgresConfig; + 'neon-http': MonodriverNeonHttpConfig; + 'vercel-postgres': never; 'aws-data-api-pg': RDSConfig; planetscale: PlanetscaleConfig; - mysql2: Mysql2Config; + mysql2: MonodriverMysql2Config; 'tidb-serverless': TiDBServerlessConfig; libsql: LibsqlConfig; d1: D1Database; @@ -126,14 +136,14 @@ import { drizzle as betterSqliteDrizzle } from './better-sqlite3/index.ts'; import { drizzle as bunSqliteDrizzle } from './bun-sqlite/index.ts'; import { drizzle as d1Drizzle } from './d1/index.ts'; import { drizzle as libsqlDrizzle } from './libsql/index.ts'; -// import { drizzle as mysql2Drizzle } from './mysql2/index.ts'; -// import { drizzle as neonHttpDrizzle } from './neon-http/index.ts'; -// import { drizzle as neonDrizzle } from './neon-serverless/index.ts'; +import { drizzle as mysql2Drizzle } from './mysql2/index.ts'; +import { drizzle as neonHttpDrizzle } from './neon-http/index.ts'; +import { drizzle as neonDrizzle } from './neon-serverless/index.ts'; import { drizzle as pgDrizzle } from './node-postgres/index.ts'; -// import { drizzle as planetscaleDrizzle } from './planetscale-serverless/index.ts'; -// import { drizzle as postgresJSDrizzle } from './postgres-js/index.ts'; -// import { drizzle as tidbDrizzle } from './tidb-serverless/index.ts'; -// import { drizzle as vercelDrizzle } from './vercel-postgres/index.ts'; +import { drizzle as planetscaleDrizzle } from './planetscale-serverless/index.ts'; +import { drizzle as postgresJSDrizzle } from './postgres-js/index.ts'; +import { drizzle as tidbDrizzle } from './tidb-serverless/index.ts'; +import { drizzle as vercelDrizzle } from './vercel-postgres/index.ts'; export const drizzle = async < TClientType extends DatabaseClientType, @@ -141,11 +151,10 @@ export const drizzle = async < TParams extends InitializerParams, >(params: TParams): Promise> => { const { client, connection } = params; - const drizzleConfig = params as Omit; - // @ts-expect-error - delete drizzleConfig.client; - // @ts-expect-error - delete drizzleConfig.connection; + const drizzleConfig = { + logger: params.logger, + schema: params.schema, + }; switch (client) { case 'node-postgres': { @@ -183,22 +192,53 @@ export const drizzle = async < return libsqlDrizzle(instance, drizzleConfig) as any; } - // case 'mysql2': { - // } - // case 'neon-http': { - // } - // case 'neon-serverless': { - // } - // case 'planetscale': { - // } - // case 'postgres.js': { - // } - // case 'tidb-serverless': { - // } - // case 'vercel-postgres': { - // } + case 'mysql2': { + const { createConnection } = await import('mysql2/promise'); + const instance = await createConnection(connection as MonodriverMysql2Config); + const mode = (connection as MonodriverMysql2Config).mode ?? 'default'; + + return mysql2Drizzle(instance, { logger: params.logger, schema: params.schema, mode }) as any; + } + case 'neon-http': { + const { neon } = await import('@neondatabase/serverless'); + const { connectionString, options } = connection as MonodriverNeonHttpConfig; + const instance = neon(connectionString, options); + + return neonHttpDrizzle(instance, drizzleConfig) as any; + } + case 'neon-serverless': { + const { Pool } = await import('@neondatabase/serverless'); + const instance = new Pool(connection as NeonServerlessConfig); + + return neonDrizzle(instance, drizzleConfig) as any; + } + case 'planetscale': { + const { Client } = await import('@planetscale/database'); + const instance = new Client( + connection as PlanetscaleConfig, + ); + + return planetscaleDrizzle(instance, drizzleConfig) as any; + } + case 'postgres.js': { + const { default: client } = await import('postgres'); + const instance = client(connection as PostgresJSOptions>); + + return postgresJSDrizzle(instance, drizzleConfig) as any; + } + case 'tidb-serverless': { + const { connect } = await import('@tidbcloud/serverless'); + const instance = connect(connection as TiDBServerlessConfig); + + return tidbDrizzle(instance, drizzleConfig) as any; + } + case 'vercel-postgres': { + const { sql } = await import('@vercel/postgres'); + + return vercelDrizzle(sql, drizzleConfig) as any; + } } - // @ts-ignore - return {}; + // Shouldn't reach that point, but Typescript fails to infer that + return {} as any; }; From 45d2d13be1c33e35addbf4326b2ab1cc407cb826 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 27 Aug 2024 13:09:28 +0300 Subject: [PATCH 07/56] Prototype implementation --- drizzle-orm/src/monodriver.ts | 160 ++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 75 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 33c9bddbe..6fe6de8a0 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -6,8 +6,9 @@ import type { } from '@neondatabase/serverless'; import type { Config as PlanetscaleConfig } from '@planetscale/database'; import type { Config as TiDBServerlessConfig } from '@tidbcloud/serverless'; +import type { VercelPool } from '@vercel/postgres'; import type { Options as BetterSQLite3Options } from 'better-sqlite3'; -import type { ConnectionConfig as Mysql2Config } from 'mysql2'; +import type { PoolOptions as Mysql2Config } from 'mysql2'; import type { PoolConfig as NodePGPoolConfig } from 'pg'; import type { Options as PostgresJSOptions, PostgresType as PostgresJSPostgresType } from 'postgres'; import type { AwsDataApiPgDatabase, DrizzleAwsDataApiPgConfig } from './aws-data-api/pg/index.ts'; @@ -58,46 +59,11 @@ type BetterSQLite3DatabaseConfig = { options?: BetterSQLite3Options; }; -type MonodriverMysql2Config = Mysql2Config & { - mode?: MySql2DrizzleConfig['mode']; -}; - type MonodriverNeonHttpConfig = { connectionString: string; options?: NeonHttpConfig; }; -type DatabaseClientType = - | 'node-postgres' - | 'postgres.js' - | 'neon-serverless' - | 'neon-http' - | 'vercel-postgres' - | 'aws-data-api-pg' - | 'planetscale' - | 'mysql2' - | 'tidb-serverless' - | 'libsql' - | 'd1' - | 'bun-sqlite' - | 'better-sqlite3'; - -type ClientConnectionConfigMap = { - 'node-postgres': NodePGPoolConfig; - 'postgres.js': PostgresJSOptions>; - 'neon-serverless': NeonServerlessConfig; - 'neon-http': MonodriverNeonHttpConfig; - 'vercel-postgres': never; - 'aws-data-api-pg': RDSConfig; - planetscale: PlanetscaleConfig; - mysql2: MonodriverMysql2Config; - 'tidb-serverless': TiDBServerlessConfig; - libsql: LibsqlConfig; - d1: D1Database; - 'bun-sqlite': BunSqliteDatabaseConfig; - 'better-sqlite3': BetterSQLite3DatabaseConfig; -}; - type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; 'postgres.js': PostgresJsDatabase; @@ -106,7 +72,7 @@ type ClientDrizzleInstanceMap> = { 'vercel-postgres': VercelPgDatabase; 'aws-data-api-pg': AwsDataApiPgDatabase; planetscale: PlanetScaleDatabase; - mysql2: MySql2Database; + mysql2: Promise>; 'tidb-serverless': TiDBServerlessDatabase; libsql: LibSQLDatabase; d1: DrizzleD1Database; @@ -114,22 +80,65 @@ type ClientDrizzleInstanceMap> = { 'better-sqlite3': BetterSQLite3Database; }; -type ClientParams = ClientConnectionConfigMap[TClientType]; - type InitializerParams< - TClientType extends DatabaseClientType, TSchema extends Record = Record, -> = { - client: TClientType; - connection: ClientParams; -} & DrizzleConfig; +> = + | ({ + client: 'node-postgres'; + connection: NodePGPoolConfig; + } & DrizzleConfig) + | ({ + client: 'postgres.js'; + connection: PostgresJSOptions>; + } & DrizzleConfig) + | ({ + client: 'neon-serverless'; + connection: NeonServerlessConfig; + } & DrizzleConfig) + | ({ + client: 'neon-http'; + connection: MonodriverNeonHttpConfig; + } & DrizzleConfig) + | ({ + client: 'vercel-postgres'; + connection: VercelPool; + } & DrizzleConfig) + | ({ + client: 'aws-data-api-pg'; + connection: RDSConfig; + } & DrizzleAwsDataApiPgConfig) + | ({ + client: 'planetscale'; + connection: PlanetscaleConfig; + } & DrizzleConfig) + | ({ + client: 'mysql2'; + connection: Mysql2Config; + } & MySql2DrizzleConfig) + | ({ + client: 'tidb-serverless'; + connection: TiDBServerlessConfig; + } & DrizzleConfig) + | ({ + client: 'libsql'; + connection: LibsqlConfig; + } & DrizzleConfig) + | ({ + client: 'd1'; + connection: D1Database; + } & DrizzleConfig) + | ({ + client: 'bun-sqlite'; + connection: BunSqliteDatabaseConfig; + } & DrizzleConfig) + | ({ + client: 'better-sqlite3'; + connection: BetterSQLite3DatabaseConfig; + } & DrizzleConfig); type DetermineClient< - TParams extends InitializerParams, - TSchema extends Record = TParams extends { schema: Record } ? TParams['schema'] - : Record, -> = TParams extends { client: DatabaseClientType } ? ClientDrizzleInstanceMap[TParams['client']] - : never; + TParams extends InitializerParams, +> = ClientDrizzleInstanceMap[TParams['client']]; import { drizzle as rdsPgDrizzle } from './aws-data-api/pg/index.ts'; import { drizzle as betterSqliteDrizzle } from './better-sqlite3/index.ts'; @@ -145,39 +154,37 @@ import { drizzle as postgresJSDrizzle } from './postgres-js/index.ts'; import { drizzle as tidbDrizzle } from './tidb-serverless/index.ts'; import { drizzle as vercelDrizzle } from './vercel-postgres/index.ts'; -export const drizzle = async < - TClientType extends DatabaseClientType, +export const drizzle = < TSchema extends Record, - TParams extends InitializerParams, + TParams extends InitializerParams, >(params: TParams): Promise> => { const { client, connection } = params; - const drizzleConfig = { - logger: params.logger, - schema: params.schema, - }; + const drizzleConfig = params as DrizzleConfig; + delete ( drizzleConfig).client; + delete ( drizzleConfig).connection; switch (client) { case 'node-postgres': { - const { Pool } = await import('pg'); + const { Pool } = require('pg') as typeof import('pg'); const instance = new Pool(connection as NodePGPoolConfig); return pgDrizzle(instance, drizzleConfig) as any; } case 'aws-data-api-pg': { - const { RDSDataClient } = await import('@aws-sdk/client-rds-data'); + const { RDSDataClient } = require('@aws-sdk/client-rds-data') as typeof import('@aws-sdk/client-rds-data'); const instance = new RDSDataClient(connection); return rdsPgDrizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; } case 'better-sqlite3': { - const { default: Client } = await import('better-sqlite3'); + const Client = require('better-sqlite3') as typeof import('better-sqlite3'); const { filename, options } = connection as BetterSQLite3DatabaseConfig; const instance = new Client(filename, options); return betterSqliteDrizzle(instance, drizzleConfig) as any; } case 'bun-sqlite': { - const { Database: Client } = await import('bun:sqlite'); + const { Database: Client } = require('bun:sqlite') as typeof import('bun:sqlite'); const { filename, options } = connection as BunSqliteDatabaseConfig; const instance = new Client(filename, options); @@ -187,33 +194,39 @@ export const drizzle = async < return d1Drizzle(connection as D1Database, drizzleConfig) as any; } case 'libsql': { - const { createClient } = await import('@libsql/client'); + const { createClient } = require('@libsql/client') as typeof import('@libsql/client'); const instance = createClient(connection as LibsqlConfig); return libsqlDrizzle(instance, drizzleConfig) as any; } case 'mysql2': { - const { createConnection } = await import('mysql2/promise'); - const instance = await createConnection(connection as MonodriverMysql2Config); - const mode = (connection as MonodriverMysql2Config).mode ?? 'default'; - - return mysql2Drizzle(instance, { logger: params.logger, schema: params.schema, mode }) as any; + const { createConnection } = require('mysql2/promise') as typeof import('mysql2/promise'); + + return new Promise((res, rej) => { + createConnection(connection as Mysql2Config).then((instance) => { + try { + res(mysql2Drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any); + } catch (e) { + rej(e); + } + }).catch((e) => rej(e)); + }); } case 'neon-http': { - const { neon } = await import('@neondatabase/serverless'); + const { neon } = require('@neondatabase/serverless') as typeof import('@neondatabase/serverless'); const { connectionString, options } = connection as MonodriverNeonHttpConfig; const instance = neon(connectionString, options); return neonHttpDrizzle(instance, drizzleConfig) as any; } case 'neon-serverless': { - const { Pool } = await import('@neondatabase/serverless'); + const { Pool } = require('@neondatabase/serverless') as typeof import('@neondatabase/serverless'); const instance = new Pool(connection as NeonServerlessConfig); return neonDrizzle(instance, drizzleConfig) as any; } case 'planetscale': { - const { Client } = await import('@planetscale/database'); + const { Client } = require('@planetscale/database') as typeof import('@planetscale/database'); const instance = new Client( connection as PlanetscaleConfig, ); @@ -221,24 +234,21 @@ export const drizzle = async < return planetscaleDrizzle(instance, drizzleConfig) as any; } case 'postgres.js': { - const { default: client } = await import('postgres'); + const client = require('postgres') as typeof import('postgres'); const instance = client(connection as PostgresJSOptions>); return postgresJSDrizzle(instance, drizzleConfig) as any; } case 'tidb-serverless': { - const { connect } = await import('@tidbcloud/serverless'); + const { connect } = require('@tidbcloud/serverless') as typeof import('@tidbcloud/serverless'); const instance = connect(connection as TiDBServerlessConfig); return tidbDrizzle(instance, drizzleConfig) as any; } case 'vercel-postgres': { - const { sql } = await import('@vercel/postgres'); + const { sql } = require('@vercel/postgres') as typeof import('@vercel/postgres'); return vercelDrizzle(sql, drizzleConfig) as any; } } - - // Shouldn't reach that point, but Typescript fails to infer that - return {} as any; }; From be0aab22d331d5f12c64ef31335a90e3d0aba3f4 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 27 Aug 2024 14:25:50 +0300 Subject: [PATCH 08/56] switched bun to dynamic imports --- drizzle-orm/src/monodriver.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 6fe6de8a0..50106e4e9 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -76,7 +76,7 @@ type ClientDrizzleInstanceMap> = { 'tidb-serverless': TiDBServerlessDatabase; libsql: LibSQLDatabase; d1: DrizzleD1Database; - 'bun-sqlite': BunSQLiteDatabase; + 'bun-sqlite': Promise>; 'better-sqlite3': BetterSQLite3Database; }; @@ -184,11 +184,18 @@ export const drizzle = < return betterSqliteDrizzle(instance, drizzleConfig) as any; } case 'bun-sqlite': { - const { Database: Client } = require('bun:sqlite') as typeof import('bun:sqlite'); - const { filename, options } = connection as BunSqliteDatabaseConfig; - const instance = new Client(filename, options); + return new Promise((res, rej) => { + import('bun:sqlite').then(({ Database: Client }) => { + try { + const { filename, options } = connection as BunSqliteDatabaseConfig; + const instance = new Client(filename, options); - return bunSqliteDrizzle(instance, drizzleConfig) as any; + res(bunSqliteDrizzle(instance, drizzleConfig) as any); + } catch (e) { + rej(e); + } + }).catch((e) => rej(e)); + }); } case 'd1': { return d1Drizzle(connection as D1Database, drizzleConfig) as any; From 936b319090b22d9429f3100182bdd48fd40c5034 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 27 Aug 2024 15:50:03 +0300 Subject: [PATCH 09/56] Removed static imports, removed promise wrapper --- drizzle-orm/src/monodriver.ts | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 50106e4e9..81e7b7396 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/extensions */ import type { RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; import type { Config as LibsqlConfig } from '@libsql/client'; import type { @@ -66,7 +67,7 @@ type MonodriverNeonHttpConfig = { type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; - 'postgres.js': PostgresJsDatabase; + 'postgres-js': PostgresJsDatabase; 'neon-serverless': NeonDatabase; 'neon-http': NeonHttpDatabase; 'vercel-postgres': VercelPgDatabase; @@ -88,7 +89,7 @@ type InitializerParams< connection: NodePGPoolConfig; } & DrizzleConfig) | ({ - client: 'postgres.js'; + client: 'postgres-js'; connection: PostgresJSOptions>; } & DrizzleConfig) | ({ @@ -140,24 +141,10 @@ type DetermineClient< TParams extends InitializerParams, > = ClientDrizzleInstanceMap[TParams['client']]; -import { drizzle as rdsPgDrizzle } from './aws-data-api/pg/index.ts'; -import { drizzle as betterSqliteDrizzle } from './better-sqlite3/index.ts'; -import { drizzle as bunSqliteDrizzle } from './bun-sqlite/index.ts'; -import { drizzle as d1Drizzle } from './d1/index.ts'; -import { drizzle as libsqlDrizzle } from './libsql/index.ts'; -import { drizzle as mysql2Drizzle } from './mysql2/index.ts'; -import { drizzle as neonHttpDrizzle } from './neon-http/index.ts'; -import { drizzle as neonDrizzle } from './neon-serverless/index.ts'; -import { drizzle as pgDrizzle } from './node-postgres/index.ts'; -import { drizzle as planetscaleDrizzle } from './planetscale-serverless/index.ts'; -import { drizzle as postgresJSDrizzle } from './postgres-js/index.ts'; -import { drizzle as tidbDrizzle } from './tidb-serverless/index.ts'; -import { drizzle as vercelDrizzle } from './vercel-postgres/index.ts'; - export const drizzle = < TSchema extends Record, TParams extends InitializerParams, ->(params: TParams): Promise> => { +>(params: TParams): DetermineClient => { const { client, connection } = params; const drizzleConfig = params as DrizzleConfig; delete ( drizzleConfig).client; @@ -166,45 +153,51 @@ export const drizzle = < switch (client) { case 'node-postgres': { const { Pool } = require('pg') as typeof import('pg'); + const { drizzle } = require('./node-postgres') as typeof import('./node-postgres'); const instance = new Pool(connection as NodePGPoolConfig); - return pgDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'aws-data-api-pg': { const { RDSDataClient } = require('@aws-sdk/client-rds-data') as typeof import('@aws-sdk/client-rds-data'); + const { drizzle } = require('./aws-data-api/pg') as typeof import('./aws-data-api/pg'); const instance = new RDSDataClient(connection); - return rdsPgDrizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; + return drizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; } case 'better-sqlite3': { const Client = require('better-sqlite3') as typeof import('better-sqlite3'); const { filename, options } = connection as BetterSQLite3DatabaseConfig; + const { drizzle } = require('./better-sqlite3') as typeof import('./better-sqlite3'); const instance = new Client(filename, options); - return betterSqliteDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'bun-sqlite': { return new Promise((res, rej) => { import('bun:sqlite').then(({ Database: Client }) => { try { const { filename, options } = connection as BunSqliteDatabaseConfig; + const { drizzle } = require('./bun-sqlite') as typeof import('./bun-sqlite'); const instance = new Client(filename, options); - res(bunSqliteDrizzle(instance, drizzleConfig) as any); + res(drizzle(instance, drizzleConfig) as any); } catch (e) { rej(e); } }).catch((e) => rej(e)); - }); + }) as any; } case 'd1': { - return d1Drizzle(connection as D1Database, drizzleConfig) as any; + const { drizzle } = require('./d1') as typeof import('./d1'); + return drizzle(connection as D1Database, drizzleConfig) as any; } case 'libsql': { const { createClient } = require('@libsql/client') as typeof import('@libsql/client'); + const { drizzle } = require('./libsql') as typeof import('./libsql'); const instance = createClient(connection as LibsqlConfig); - return libsqlDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'mysql2': { const { createConnection } = require('mysql2/promise') as typeof import('mysql2/promise'); @@ -212,50 +205,57 @@ export const drizzle = < return new Promise((res, rej) => { createConnection(connection as Mysql2Config).then((instance) => { try { - res(mysql2Drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any); + const { drizzle } = require('./mysql2') as typeof import('./mysql2'); + res(drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any); } catch (e) { rej(e); } }).catch((e) => rej(e)); - }); + }) as any; } case 'neon-http': { const { neon } = require('@neondatabase/serverless') as typeof import('@neondatabase/serverless'); const { connectionString, options } = connection as MonodriverNeonHttpConfig; + const { drizzle } = require('./neon-http') as typeof import('./neon-http'); const instance = neon(connectionString, options); - return neonHttpDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'neon-serverless': { const { Pool } = require('@neondatabase/serverless') as typeof import('@neondatabase/serverless'); + const { drizzle } = require('./neon-serverless') as typeof import('./neon-serverless'); const instance = new Pool(connection as NeonServerlessConfig); - return neonDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'planetscale': { const { Client } = require('@planetscale/database') as typeof import('@planetscale/database'); + const { drizzle } = require('./planetscale-serverless') as typeof import('./planetscale-serverless'); const instance = new Client( connection as PlanetscaleConfig, ); - return planetscaleDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } - case 'postgres.js': { + case 'postgres-js': { const client = require('postgres') as typeof import('postgres'); + const { drizzle } = require('./postgres-js') as typeof import('./postgres-js'); const instance = client(connection as PostgresJSOptions>); - return postgresJSDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'tidb-serverless': { const { connect } = require('@tidbcloud/serverless') as typeof import('@tidbcloud/serverless'); + const { drizzle } = require('./tidb-serverless') as typeof import('./tidb-serverless'); const instance = connect(connection as TiDBServerlessConfig); - return tidbDrizzle(instance, drizzleConfig) as any; + return drizzle(instance, drizzleConfig) as any; } case 'vercel-postgres': { const { sql } = require('@vercel/postgres') as typeof import('@vercel/postgres'); + const { drizzle } = require('./vercel-postgres') as typeof import('./vercel-postgres'); - return vercelDrizzle(sql, drizzleConfig) as any; + return drizzle(sql, drizzleConfig) as any; } } }; From 493dd13608d03e958038dfba3e612d7883732b57 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 27 Aug 2024 16:33:41 +0300 Subject: [PATCH 10/56] Returned async imports due to conflicts --- drizzle-orm/src/monodriver.ts | 82 +++++++++++++++-------------------- 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 81e7b7396..8729ec92d 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -73,11 +73,11 @@ type ClientDrizzleInstanceMap> = { 'vercel-postgres': VercelPgDatabase; 'aws-data-api-pg': AwsDataApiPgDatabase; planetscale: PlanetScaleDatabase; - mysql2: Promise>; + mysql2: MySql2Database; 'tidb-serverless': TiDBServerlessDatabase; libsql: LibSQLDatabase; d1: DrizzleD1Database; - 'bun-sqlite': Promise>; + 'bun-sqlite': BunSQLiteDatabase; 'better-sqlite3': BetterSQLite3Database; }; @@ -141,10 +141,10 @@ type DetermineClient< TParams extends InitializerParams, > = ClientDrizzleInstanceMap[TParams['client']]; -export const drizzle = < +export const drizzle = async < TSchema extends Record, TParams extends InitializerParams, ->(params: TParams): DetermineClient => { +>(params: TParams): Promise> => { const { client, connection } = params; const drizzleConfig = params as DrizzleConfig; delete ( drizzleConfig).client; @@ -152,85 +152,71 @@ export const drizzle = < switch (client) { case 'node-postgres': { - const { Pool } = require('pg') as typeof import('pg'); - const { drizzle } = require('./node-postgres') as typeof import('./node-postgres'); + const { Pool } = await import('pg'); + const { drizzle } = await import('./node-postgres'); const instance = new Pool(connection as NodePGPoolConfig); return drizzle(instance, drizzleConfig) as any; } case 'aws-data-api-pg': { - const { RDSDataClient } = require('@aws-sdk/client-rds-data') as typeof import('@aws-sdk/client-rds-data'); - const { drizzle } = require('./aws-data-api/pg') as typeof import('./aws-data-api/pg'); + const { RDSDataClient } = await import('@aws-sdk/client-rds-data'); + const { drizzle } = await import('./aws-data-api/pg'); const instance = new RDSDataClient(connection); return drizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; } case 'better-sqlite3': { - const Client = require('better-sqlite3') as typeof import('better-sqlite3'); + const { default: Client } = await import('better-sqlite3'); const { filename, options } = connection as BetterSQLite3DatabaseConfig; - const { drizzle } = require('./better-sqlite3') as typeof import('./better-sqlite3'); + const { drizzle } = await import('./better-sqlite3'); const instance = new Client(filename, options); return drizzle(instance, drizzleConfig) as any; } case 'bun-sqlite': { - return new Promise((res, rej) => { - import('bun:sqlite').then(({ Database: Client }) => { - try { - const { filename, options } = connection as BunSqliteDatabaseConfig; - const { drizzle } = require('./bun-sqlite') as typeof import('./bun-sqlite'); - const instance = new Client(filename, options); + const { Database: Client } = await import('bun:sqlite'); + const { filename, options } = connection as BunSqliteDatabaseConfig; + const { drizzle } = await import('./bun-sqlite'); + const instance = new Client(filename, options); - res(drizzle(instance, drizzleConfig) as any); - } catch (e) { - rej(e); - } - }).catch((e) => rej(e)); - }) as any; + return drizzle(instance, drizzleConfig) as any; } case 'd1': { - const { drizzle } = require('./d1') as typeof import('./d1'); + const { drizzle } = await import('./d1'); return drizzle(connection as D1Database, drizzleConfig) as any; } case 'libsql': { - const { createClient } = require('@libsql/client') as typeof import('@libsql/client'); - const { drizzle } = require('./libsql') as typeof import('./libsql'); + const { createClient } = await import('@libsql/client'); + const { drizzle } = await import('./libsql'); const instance = createClient(connection as LibsqlConfig); return drizzle(instance, drizzleConfig) as any; } case 'mysql2': { - const { createConnection } = require('mysql2/promise') as typeof import('mysql2/promise'); + const { createConnection } = await import('mysql2/promise'); + const instance = await createConnection(connection as Mysql2Config); + const { drizzle } = await import('./mysql2'); - return new Promise((res, rej) => { - createConnection(connection as Mysql2Config).then((instance) => { - try { - const { drizzle } = require('./mysql2') as typeof import('./mysql2'); - res(drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any); - } catch (e) { - rej(e); - } - }).catch((e) => rej(e)); - }) as any; + return drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any; } case 'neon-http': { - const { neon } = require('@neondatabase/serverless') as typeof import('@neondatabase/serverless'); + const { neon } = await import('@neondatabase/serverless'); const { connectionString, options } = connection as MonodriverNeonHttpConfig; - const { drizzle } = require('./neon-http') as typeof import('./neon-http'); + const { drizzle } = await import('./neon-http'); const instance = neon(connectionString, options); return drizzle(instance, drizzleConfig) as any; } case 'neon-serverless': { - const { Pool } = require('@neondatabase/serverless') as typeof import('@neondatabase/serverless'); - const { drizzle } = require('./neon-serverless') as typeof import('./neon-serverless'); + const { Pool } = await import('@neondatabase/serverless'); + const { drizzle } = await import('./neon-serverless'); const instance = new Pool(connection as NeonServerlessConfig); return drizzle(instance, drizzleConfig) as any; } case 'planetscale': { - const { Client } = require('@planetscale/database') as typeof import('@planetscale/database'); - const { drizzle } = require('./planetscale-serverless') as typeof import('./planetscale-serverless'); + const { Client } = await import('@planetscale/database'); + const { drizzle } = await import('./planetscale-serverless'); const instance = new Client( connection as PlanetscaleConfig, ); @@ -238,22 +224,22 @@ export const drizzle = < return drizzle(instance, drizzleConfig) as any; } case 'postgres-js': { - const client = require('postgres') as typeof import('postgres'); - const { drizzle } = require('./postgres-js') as typeof import('./postgres-js'); + const { default: client } = await import('postgres'); + const { drizzle } = await import('./postgres-js'); const instance = client(connection as PostgresJSOptions>); return drizzle(instance, drizzleConfig) as any; } case 'tidb-serverless': { - const { connect } = require('@tidbcloud/serverless') as typeof import('@tidbcloud/serverless'); - const { drizzle } = require('./tidb-serverless') as typeof import('./tidb-serverless'); + const { connect } = await import('@tidbcloud/serverless'); + const { drizzle } = await import('./tidb-serverless'); const instance = connect(connection as TiDBServerlessConfig); return drizzle(instance, drizzleConfig) as any; } case 'vercel-postgres': { - const { sql } = require('@vercel/postgres') as typeof import('@vercel/postgres'); - const { drizzle } = require('./vercel-postgres') as typeof import('./vercel-postgres'); + const { sql } = await import('@vercel/postgres'); + const { drizzle } = await import('./vercel-postgres'); return drizzle(sql, drizzleConfig) as any; } From 6c6394d7d4d9d1c945e9b4e2f5e57f06fe0e81f8 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 27 Aug 2024 18:16:08 +0300 Subject: [PATCH 11/56] More comnprehensive import errors --- drizzle-orm/src/monodriver.ts | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 8729ec92d..2f9557b01 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -141,6 +141,12 @@ type DetermineClient< TParams extends InitializerParams, > = ClientDrizzleInstanceMap[TParams['client']]; +const importError = (libName: string) => { + throw new Error( + `Drizzle init error: unable to import selected database driver library '${libName}' - make sure it is installed.`, + ); +}; + export const drizzle = async < TSchema extends Record, TParams extends InitializerParams, @@ -152,21 +158,23 @@ export const drizzle = async < switch (client) { case 'node-postgres': { - const { Pool } = await import('pg'); + const { Pool } = await import('pg').catch(() => importError('pg')); const { drizzle } = await import('./node-postgres'); const instance = new Pool(connection as NodePGPoolConfig); return drizzle(instance, drizzleConfig) as any; } case 'aws-data-api-pg': { - const { RDSDataClient } = await import('@aws-sdk/client-rds-data'); + const { RDSDataClient } = await import('@aws-sdk/client-rds-data').catch(() => + importError('@aws-sdk/client-rds-data') + ); const { drizzle } = await import('./aws-data-api/pg'); const instance = new RDSDataClient(connection); return drizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; } case 'better-sqlite3': { - const { default: Client } = await import('better-sqlite3'); + const { default: Client } = await import('better-sqlite3').catch(() => importError('better-sqlite3')); const { filename, options } = connection as BetterSQLite3DatabaseConfig; const { drizzle } = await import('./better-sqlite3'); const instance = new Client(filename, options); @@ -174,7 +182,7 @@ export const drizzle = async < return drizzle(instance, drizzleConfig) as any; } case 'bun-sqlite': { - const { Database: Client } = await import('bun:sqlite'); + const { Database: Client } = await import('bun:sqlite').catch(() => importError('bun:sqlite')); const { filename, options } = connection as BunSqliteDatabaseConfig; const { drizzle } = await import('./bun-sqlite'); const instance = new Client(filename, options); @@ -186,21 +194,21 @@ export const drizzle = async < return drizzle(connection as D1Database, drizzleConfig) as any; } case 'libsql': { - const { createClient } = await import('@libsql/client'); + const { createClient } = await import('@libsql/client').catch(() => importError('@libsql/client')); const { drizzle } = await import('./libsql'); const instance = createClient(connection as LibsqlConfig); return drizzle(instance, drizzleConfig) as any; } case 'mysql2': { - const { createConnection } = await import('mysql2/promise'); + const { createConnection } = await import('mysql2/promise').catch(() => importError('mysql2/promise')); const instance = await createConnection(connection as Mysql2Config); const { drizzle } = await import('./mysql2'); return drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any; } case 'neon-http': { - const { neon } = await import('@neondatabase/serverless'); + const { neon } = await import('@neondatabase/serverless').catch(() => importError('@neondatabase/serverless')); const { connectionString, options } = connection as MonodriverNeonHttpConfig; const { drizzle } = await import('./neon-http'); const instance = neon(connectionString, options); @@ -208,14 +216,14 @@ export const drizzle = async < return drizzle(instance, drizzleConfig) as any; } case 'neon-serverless': { - const { Pool } = await import('@neondatabase/serverless'); + const { Pool } = await import('@neondatabase/serverless').catch(() => importError('@neondatabase/serverless')); const { drizzle } = await import('./neon-serverless'); const instance = new Pool(connection as NeonServerlessConfig); return drizzle(instance, drizzleConfig) as any; } case 'planetscale': { - const { Client } = await import('@planetscale/database'); + const { Client } = await import('@planetscale/database').catch(() => importError('@planetscale/database')); const { drizzle } = await import('./planetscale-serverless'); const instance = new Client( connection as PlanetscaleConfig, @@ -224,21 +232,21 @@ export const drizzle = async < return drizzle(instance, drizzleConfig) as any; } case 'postgres-js': { - const { default: client } = await import('postgres'); + const { default: client } = await import('postgres').catch(() => importError('postgres')); const { drizzle } = await import('./postgres-js'); const instance = client(connection as PostgresJSOptions>); return drizzle(instance, drizzleConfig) as any; } case 'tidb-serverless': { - const { connect } = await import('@tidbcloud/serverless'); + const { connect } = await import('@tidbcloud/serverless').catch(() => importError('@tidbcloud/serverless')); const { drizzle } = await import('./tidb-serverless'); const instance = connect(connection as TiDBServerlessConfig); return drizzle(instance, drizzleConfig) as any; } case 'vercel-postgres': { - const { sql } = await import('@vercel/postgres'); + const { sql } = await import('@vercel/postgres').catch(() => importError('@vercel/postgres')); const { drizzle } = await import('./vercel-postgres'); return drizzle(sql, drizzleConfig) as any; From 1b580d8af79ebe0d519b49c1b97b65403f0b5e42 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 28 Aug 2024 13:08:09 +0300 Subject: [PATCH 12/56] Changed error messge --- drizzle-orm/src/monodriver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 2f9557b01..2b9a8c2b5 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -143,7 +143,7 @@ type DetermineClient< const importError = (libName: string) => { throw new Error( - `Drizzle init error: unable to import selected database driver library '${libName}' - make sure it is installed.`, + `Please install '${libName}' for Drizzle ORM to connect to database`, ); }; From b6b8258e341c01a89fdef4d42da4188443ec070e Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Thu, 29 Aug 2024 13:02:32 +0300 Subject: [PATCH 13/56] Improved params destructurization --- drizzle-orm/src/monodriver.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 2b9a8c2b5..b2b82af3f 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -151,10 +151,7 @@ export const drizzle = async < TSchema extends Record, TParams extends InitializerParams, >(params: TParams): Promise> => { - const { client, connection } = params; - const drizzleConfig = params as DrizzleConfig; - delete ( drizzleConfig).client; - delete ( drizzleConfig).connection; + const { client, connection, ...drizzleConfig } = params; switch (client) { case 'node-postgres': { From 4f7d70355138b0b44e98aea36555f9d9bda02f59 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Mon, 2 Sep 2024 05:10:29 +0300 Subject: [PATCH 14/56] Functional prototype of SQLite $count --- drizzle-orm/src/operations.ts | 1 + drizzle-orm/src/sqlite-core/db.ts | 11 ++- .../src/sqlite-core/query-builders/count.ts | 78 +++++++++++++++++++ .../sqlite-core/query-builders/count.types.ts | 8 ++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 drizzle-orm/src/sqlite-core/query-builders/count.ts create mode 100644 drizzle-orm/src/sqlite-core/query-builders/count.types.ts diff --git a/drizzle-orm/src/operations.ts b/drizzle-orm/src/operations.ts index 492bb3f2a..6fb5cbd2e 100644 --- a/drizzle-orm/src/operations.ts +++ b/drizzle-orm/src/operations.ts @@ -21,6 +21,7 @@ export type OptionalKeyOnly< : T['_']['generated'] extends object ? T['_']['generated']['type'] extends 'byDefault' ? TKey : never : never; +// TODO: SQL -> SQLWrapper export type SelectedFieldsFlat = Record< string, TColumn | SQL | SQL.Aliased diff --git a/drizzle-orm/src/sqlite-core/db.ts b/drizzle-orm/src/sqlite-core/db.ts index 65f807d08..97347977c 100644 --- a/drizzle-orm/src/sqlite-core/db.ts +++ b/drizzle-orm/src/sqlite-core/db.ts @@ -2,7 +2,7 @@ import { entityKind } from '~/entity.ts'; import type { TypedQueryBuilder } from '~/query-builders/query-builder.ts'; import type { ExtractTablesWithRelations, RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; import { SelectionProxyHandler } from '~/selection-proxy.ts'; -import type { ColumnsSelection, SQLWrapper } from '~/sql/sql.ts'; +import type { ColumnsSelection, SQL, SQLWrapper } from '~/sql/sql.ts'; import type { SQLiteAsyncDialect, SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; import { QueryBuilder, @@ -21,10 +21,12 @@ import type { import type { SQLiteTable } from '~/sqlite-core/table.ts'; import { WithSubquery } from '~/subquery.ts'; import type { DrizzleTypeError } from '~/utils.ts'; +import { SQLiteCountBuilderAsync } from './query-builders/count.ts'; import { RelationalQueryBuilder } from './query-builders/query.ts'; import { SQLiteRaw } from './query-builders/raw.ts'; import type { SelectedFields } from './query-builders/select.types.ts'; import type { WithSubqueryWithSelection } from './subquery.ts'; +import type { SQLiteViewBase } from './view-base.ts'; export class BaseSQLiteDatabase< TResultKind extends 'sync' | 'async', @@ -134,6 +136,13 @@ export class BaseSQLiteDatabase< }; } + $count( + source: TSource, + filters?: SQL, + ) { + return new SQLiteCountBuilderAsync({ source, filters, dialect: this.dialect, session: this.session }); + } + /** * Incorporates a previously defined CTE (using `$with`) into the main query. * diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.ts b/drizzle-orm/src/sqlite-core/query-builders/count.ts new file mode 100644 index 000000000..6d6f16e78 --- /dev/null +++ b/drizzle-orm/src/sqlite-core/query-builders/count.ts @@ -0,0 +1,78 @@ +import { entityKind, sql } from '~/index.ts'; +import type { SQLWrapper } from '~/sql/sql.ts'; +import { SQL } from '~/sql/sql.ts'; +import type { SQLiteDialect } from '../dialect.ts'; +import type { SQLiteSession } from '../session.ts'; +import type { SQLiteTable } from '../table.ts'; + +export class SQLiteCountBuilderAsync< + TSession extends SQLiteSession<'async' | 'sync', any, any, any>, +> extends SQL implements Promise, SQLWrapper { + private sql: SQL; + + static readonly [entityKind] = 'SQLiteCountBuilderAsync'; + [Symbol.toStringTag] = 'SQLiteCountBuilderAsync'; + + private session: TSession; + + private static buildEmbeddedCount( + source: SQLiteTable | SQL | SQLWrapper, + filters?: SQL, + ): SQL { + return sql`(select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters})`; + } + + private static buildCount( + source: SQLiteTable | SQL | SQLWrapper, + filters?: SQL, + ): SQL { + return sql`select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters}`; + } + + constructor( + readonly params: { + source: SQLiteTable | SQL | SQLWrapper; + filters?: SQL; + dialect: SQLiteDialect; + session: TSession; + }, + ) { + super(SQLiteCountBuilderAsync.buildEmbeddedCount(params.source, params.filters).queryChunks); + + this.session = params.session; + + this.sql = SQLiteCountBuilderAsync.buildCount( + params.source, + params.filters, + ); + } + + then( + onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, + ): Promise { + return Promise.resolve(this.session.values(this.sql)).then((it) => it![0]![0] as number).then( + onfulfilled, + onrejected, + ); + } + + catch( + onRejected?: ((reason: any) => never | PromiseLike) | null | undefined, + ): Promise { + return this.then(undefined, onRejected); + } + + finally(onFinally?: (() => void) | null | undefined): Promise { + return this.then( + (value) => { + onFinally?.(); + return value; + }, + (reason) => { + onFinally?.(); + throw reason; + }, + ); + } +} diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.types.ts b/drizzle-orm/src/sqlite-core/query-builders/count.types.ts new file mode 100644 index 000000000..d346c187c --- /dev/null +++ b/drizzle-orm/src/sqlite-core/query-builders/count.types.ts @@ -0,0 +1,8 @@ +import type { SQL, SQLWrapper } from '~/index'; +import type { SQLiteTable } from '../table'; +import type { SQLiteViewBase } from '../view-base'; + +export type SQLiteCountConfig = { + source: SQLiteTable | SQLiteViewBase | SQL | SQLWrapper; + filters?: SQL; +}; From e009be7a7b454668595176e23d25e4eba6b4254d Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 2 Sep 2024 14:29:41 +0300 Subject: [PATCH 15/56] Added pg, mysql; Added tests, type tests for $count --- drizzle-orm/src/mysql-core/db.ts | 11 ++- .../src/mysql-core/query-builders/count.ts | 82 +++++++++++++++++++ drizzle-orm/src/pg-core/db.ts | 11 ++- .../src/pg-core/query-builders/count.ts | 81 ++++++++++++++++++ drizzle-orm/src/sqlite-core/db.ts | 8 +- .../src/sqlite-core/query-builders/count.ts | 17 ++-- .../sqlite-core/query-builders/count.types.ts | 8 -- drizzle-orm/type-tests/mysql/count.ts | 61 ++++++++++++++ drizzle-orm/type-tests/pg/count.ts | 61 ++++++++++++++ drizzle-orm/type-tests/sqlite/count.ts | 61 ++++++++++++++ 10 files changed, 379 insertions(+), 22 deletions(-) create mode 100644 drizzle-orm/src/mysql-core/query-builders/count.ts create mode 100644 drizzle-orm/src/pg-core/query-builders/count.ts delete mode 100644 drizzle-orm/src/sqlite-core/query-builders/count.types.ts create mode 100644 drizzle-orm/type-tests/mysql/count.ts create mode 100644 drizzle-orm/type-tests/pg/count.ts create mode 100644 drizzle-orm/type-tests/sqlite/count.ts diff --git a/drizzle-orm/src/mysql-core/db.ts b/drizzle-orm/src/mysql-core/db.ts index 8df6ff343..419359022 100644 --- a/drizzle-orm/src/mysql-core/db.ts +++ b/drizzle-orm/src/mysql-core/db.ts @@ -3,10 +3,11 @@ import { entityKind } from '~/entity.ts'; import type { TypedQueryBuilder } from '~/query-builders/query-builder.ts'; import type { ExtractTablesWithRelations, RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; import { SelectionProxyHandler } from '~/selection-proxy.ts'; -import type { ColumnsSelection, SQLWrapper } from '~/sql/sql.ts'; +import type { ColumnsSelection, SQL, SQLWrapper } from '~/sql/sql.ts'; import { WithSubquery } from '~/subquery.ts'; import type { DrizzleTypeError } from '~/utils.ts'; import type { MySqlDialect } from './dialect.ts'; +import { MySqlCountBuilder } from './query-builders/count.ts'; import { MySqlDeleteBase, MySqlInsertBuilder, @@ -27,6 +28,7 @@ import type { } from './session.ts'; import type { WithSubqueryWithSelection } from './subquery.ts'; import type { MySqlTable } from './table.ts'; +import type { MySqlViewBase } from './view-base.ts'; export class MySqlDatabase< TQueryResult extends MySqlQueryResultHKT, @@ -134,6 +136,13 @@ export class MySqlDatabase< }; } + $count( + source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, + filters?: SQL, + ) { + return new MySqlCountBuilder({ source, filters, dialect: this.dialect, session: this.session }); + } + /** * Incorporates a previously defined CTE (using `$with`) into the main query. * diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts new file mode 100644 index 000000000..751ba61c7 --- /dev/null +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -0,0 +1,82 @@ +import { entityKind, sql } from '~/index.ts'; +import type { SQLWrapper } from '~/sql/sql.ts'; +import { SQL } from '~/sql/sql.ts'; +import type { MySqlDialect } from '../dialect.ts'; +import type { MySqlSession } from '../session.ts'; +import type { MySqlTable } from '../table.ts'; +import type { MySqlViewBase } from '../view-base.ts'; + +export class MySqlCountBuilder< + TSession extends MySqlSession, +> extends SQL implements Promise, SQLWrapper { + private sql: SQL; + + static readonly [entityKind] = 'MySqlCountBuilder'; + [Symbol.toStringTag] = 'MySqlCountBuilder'; + + private session: TSession; + + private static buildEmbeddedCount( + source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, + filters?: SQL, + ): SQL { + return sql`(select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters})`; + } + + private static buildCount( + source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, + filters?: SQL, + ): SQL { + return sql`select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters}`; + } + + constructor( + readonly params: { + source: MySqlTable | MySqlViewBase | SQL | SQLWrapper; + filters?: SQL; + dialect: MySqlDialect; + session: TSession; + }, + ) { + super(MySqlCountBuilder.buildEmbeddedCount(params.source, params.filters).queryChunks); + + this.session = params.session; + + this.sql = MySqlCountBuilder.buildCount( + params.source, + params.filters, + ); + } + + then( + onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, + ): Promise { + return Promise.resolve(this.session.all(this.sql)).then((it) => { + return (<[{ 'count(*)': number }]> it)[0]['count(*)']; + }) + .then( + onfulfilled, + onrejected, + ); + } + + catch( + onRejected?: ((reason: any) => never | PromiseLike) | null | undefined, + ): Promise { + return this.then(undefined, onRejected); + } + + finally(onFinally?: (() => void) | null | undefined): Promise { + return this.then( + (value) => { + onFinally?.(); + return value; + }, + (reason) => { + onFinally?.(); + throw reason; + }, + ); + } +} diff --git a/drizzle-orm/src/pg-core/db.ts b/drizzle-orm/src/pg-core/db.ts index 4e8d2f354..3c8da44f1 100644 --- a/drizzle-orm/src/pg-core/db.ts +++ b/drizzle-orm/src/pg-core/db.ts @@ -19,15 +19,17 @@ import type { PgTable } from '~/pg-core/table.ts'; import type { TypedQueryBuilder } from '~/query-builders/query-builder.ts'; import type { ExtractTablesWithRelations, RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; import { SelectionProxyHandler } from '~/selection-proxy.ts'; -import type { ColumnsSelection, SQLWrapper } from '~/sql/sql.ts'; +import type { ColumnsSelection, SQL, SQLWrapper } from '~/sql/sql.ts'; import { WithSubquery } from '~/subquery.ts'; import type { DrizzleTypeError } from '~/utils.ts'; import type { PgColumn } from './columns/index.ts'; +import { PgCountBuilder } from './query-builders/count.ts'; import { RelationalQueryBuilder } from './query-builders/query.ts'; import { PgRaw } from './query-builders/raw.ts'; import { PgRefreshMaterializedView } from './query-builders/refresh-materialized-view.ts'; import type { SelectedFields } from './query-builders/select.types.ts'; import type { WithSubqueryWithSelection } from './subquery.ts'; +import type { PgViewBase } from './view-base.ts'; import type { PgMaterializedView } from './view.ts'; export class PgDatabase< @@ -135,6 +137,13 @@ export class PgDatabase< }; } + $count( + source: PgTable | PgViewBase | SQL | SQLWrapper, + filters?: SQL, + ) { + return new PgCountBuilder({ source, filters, dialect: this.dialect, session: this.session }); + } + /** * Incorporates a previously defined CTE (using `$with`) into the main query. * diff --git a/drizzle-orm/src/pg-core/query-builders/count.ts b/drizzle-orm/src/pg-core/query-builders/count.ts new file mode 100644 index 000000000..7ccd722a0 --- /dev/null +++ b/drizzle-orm/src/pg-core/query-builders/count.ts @@ -0,0 +1,81 @@ +import { entityKind, sql } from '~/index.ts'; +import type { SQLWrapper } from '~/sql/sql.ts'; +import { SQL } from '~/sql/sql.ts'; +import type { PgDialect } from '../dialect.ts'; +import type { PgSession } from '../session.ts'; +import type { PgTable } from '../table.ts'; + +export class PgCountBuilder< + TSession extends PgSession, +> extends SQL implements Promise, SQLWrapper { + private sql: SQL; + + static readonly [entityKind] = 'PgCountBuilder'; + [Symbol.toStringTag] = 'PgCountBuilder'; + + private session: TSession; + + private static buildEmbeddedCount( + source: PgTable | SQL | SQLWrapper, + filters?: SQL, + ): SQL { + return sql`(select count(*)::int from ${source}${sql.raw(' where ').if(filters)}${filters})`; + } + + private static buildCount( + source: PgTable | SQL | SQLWrapper, + filters?: SQL, + ): SQL { + return sql`select count(*)::int from ${source}${sql.raw(' where ').if(filters)}${filters};`; + } + + constructor( + readonly params: { + source: PgTable | SQL | SQLWrapper; + filters?: SQL; + dialect: PgDialect; + session: TSession; + }, + ) { + super(PgCountBuilder.buildEmbeddedCount(params.source, params.filters).queryChunks); + + this.session = params.session; + + this.sql = PgCountBuilder.buildCount( + params.source, + params.filters, + ); + } + + then( + onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, + ): Promise { + return Promise.resolve(this.session.all(this.sql)).then((it) => { + return (<[{ count: number }]> it)[0]['count'] as number; + }) + .then( + onfulfilled, + onrejected, + ); + } + + catch( + onRejected?: ((reason: any) => never | PromiseLike) | null | undefined, + ): Promise { + return this.then(undefined, onRejected); + } + + finally(onFinally?: (() => void) | null | undefined): Promise { + return this.then( + (value) => { + onFinally?.(); + return value; + }, + (reason) => { + onFinally?.(); + throw reason; + }, + ); + } +} diff --git a/drizzle-orm/src/sqlite-core/db.ts b/drizzle-orm/src/sqlite-core/db.ts index 97347977c..75b088f6d 100644 --- a/drizzle-orm/src/sqlite-core/db.ts +++ b/drizzle-orm/src/sqlite-core/db.ts @@ -21,7 +21,7 @@ import type { import type { SQLiteTable } from '~/sqlite-core/table.ts'; import { WithSubquery } from '~/subquery.ts'; import type { DrizzleTypeError } from '~/utils.ts'; -import { SQLiteCountBuilderAsync } from './query-builders/count.ts'; +import { SQLiteCountBuilder } from './query-builders/count.ts'; import { RelationalQueryBuilder } from './query-builders/query.ts'; import { SQLiteRaw } from './query-builders/raw.ts'; import type { SelectedFields } from './query-builders/select.types.ts'; @@ -136,11 +136,11 @@ export class BaseSQLiteDatabase< }; } - $count( - source: TSource, + $count( + source: SQLiteTable | SQLiteViewBase | SQL | SQLWrapper, filters?: SQL, ) { - return new SQLiteCountBuilderAsync({ source, filters, dialect: this.dialect, session: this.session }); + return new SQLiteCountBuilder({ source, filters, dialect: this.dialect, session: this.session }); } /** diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.ts b/drizzle-orm/src/sqlite-core/query-builders/count.ts index 6d6f16e78..ed6cd9a1d 100644 --- a/drizzle-orm/src/sqlite-core/query-builders/count.ts +++ b/drizzle-orm/src/sqlite-core/query-builders/count.ts @@ -4,9 +4,10 @@ import { SQL } from '~/sql/sql.ts'; import type { SQLiteDialect } from '../dialect.ts'; import type { SQLiteSession } from '../session.ts'; import type { SQLiteTable } from '../table.ts'; +import type { SQLiteView } from '../view.ts'; -export class SQLiteCountBuilderAsync< - TSession extends SQLiteSession<'async' | 'sync', any, any, any>, +export class SQLiteCountBuilder< + TSession extends SQLiteSession, > extends SQL implements Promise, SQLWrapper { private sql: SQL; @@ -16,14 +17,14 @@ export class SQLiteCountBuilderAsync< private session: TSession; private static buildEmbeddedCount( - source: SQLiteTable | SQL | SQLWrapper, + source: SQLiteTable | SQLiteView | SQL | SQLWrapper, filters?: SQL, ): SQL { return sql`(select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters})`; } private static buildCount( - source: SQLiteTable | SQL | SQLWrapper, + source: SQLiteTable | SQLiteView | SQL | SQLWrapper, filters?: SQL, ): SQL { return sql`select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters}`; @@ -31,17 +32,17 @@ export class SQLiteCountBuilderAsync< constructor( readonly params: { - source: SQLiteTable | SQL | SQLWrapper; + source: SQLiteTable | SQLiteView | SQL | SQLWrapper; filters?: SQL; dialect: SQLiteDialect; session: TSession; }, ) { - super(SQLiteCountBuilderAsync.buildEmbeddedCount(params.source, params.filters).queryChunks); + super(SQLiteCountBuilder.buildEmbeddedCount(params.source, params.filters).queryChunks); this.session = params.session; - this.sql = SQLiteCountBuilderAsync.buildCount( + this.sql = SQLiteCountBuilder.buildCount( params.source, params.filters, ); @@ -51,7 +52,7 @@ export class SQLiteCountBuilderAsync< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.values(this.sql)).then((it) => it![0]![0] as number).then( + return Promise.resolve(this.session.values(this.sql)).then((it) => it[0]![0] as number).then( onfulfilled, onrejected, ); diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.types.ts b/drizzle-orm/src/sqlite-core/query-builders/count.types.ts deleted file mode 100644 index d346c187c..000000000 --- a/drizzle-orm/src/sqlite-core/query-builders/count.types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { SQL, SQLWrapper } from '~/index'; -import type { SQLiteTable } from '../table'; -import type { SQLiteViewBase } from '../view-base'; - -export type SQLiteCountConfig = { - source: SQLiteTable | SQLiteViewBase | SQL | SQLWrapper; - filters?: SQL; -}; diff --git a/drizzle-orm/type-tests/mysql/count.ts b/drizzle-orm/type-tests/mysql/count.ts new file mode 100644 index 000000000..d9b9ba9ff --- /dev/null +++ b/drizzle-orm/type-tests/mysql/count.ts @@ -0,0 +1,61 @@ +import { Expect } from 'type-tests/utils.ts'; +import { and, gt, ne } from '~/expressions.ts'; +import { int, mysqlTable, serial, text } from '~/mysql-core/index.ts'; +import type { Equal } from '~/utils.ts'; +import { db } from './db.ts'; + +const names = mysqlTable('names', { + id: serial('id').primaryKey(), + name: text('name'), + authorId: int('author_id'), +}); + +const separate = await db.$count(names); + +const separateFilters = await db.$count(names, and(gt(names.id, 1), ne(names.name, 'forbidden'))); + +const embedded = await db + .select({ + id: names.id, + name: names.name, + authorId: names.authorId, + count1: db.$count(names).as('count1'), + }) + .from(names); + +const embeddedFilters = await db + .select({ + id: names.id, + name: names.name, + authorId: names.authorId, + count1: db.$count(names, and(gt(names.id, 1), ne(names.name, 'forbidden'))).as('count1'), + }) + .from(names); + +Expect>; + +Expect>; + +Expect< + Equal< + { + id: number; + name: string | null; + authorId: number | null; + count1: number; + }[], + typeof embedded + > +>; + +Expect< + Equal< + { + id: number; + name: string | null; + authorId: number | null; + count1: number; + }[], + typeof embeddedFilters + > +>; diff --git a/drizzle-orm/type-tests/pg/count.ts b/drizzle-orm/type-tests/pg/count.ts new file mode 100644 index 000000000..9ed5eeaf9 --- /dev/null +++ b/drizzle-orm/type-tests/pg/count.ts @@ -0,0 +1,61 @@ +import { Expect } from 'type-tests/utils.ts'; +import { and, gt, ne } from '~/expressions.ts'; +import { integer, pgTable, serial, text } from '~/pg-core/index.ts'; +import type { Equal } from '~/utils.ts'; +import { db } from './db.ts'; + +const names = pgTable('names', { + id: serial('id').primaryKey(), + name: text('name'), + authorId: integer('author_id'), +}); + +const separate = await db.$count(names); + +const separateFilters = await db.$count(names, and(gt(names.id, 1), ne(names.name, 'forbidden'))); + +const embedded = await db + .select({ + id: names.id, + name: names.name, + authorId: names.authorId, + count1: db.$count(names).as('count1'), + }) + .from(names); + +const embeddedFilters = await db + .select({ + id: names.id, + name: names.name, + authorId: names.authorId, + count1: db.$count(names, and(gt(names.id, 1), ne(names.name, 'forbidden'))).as('count1'), + }) + .from(names); + +Expect>; + +Expect>; + +Expect< + Equal< + { + id: number; + name: string | null; + authorId: number | null; + count1: number; + }[], + typeof embedded + > +>; + +Expect< + Equal< + { + id: number; + name: string | null; + authorId: number | null; + count1: number; + }[], + typeof embeddedFilters + > +>; diff --git a/drizzle-orm/type-tests/sqlite/count.ts b/drizzle-orm/type-tests/sqlite/count.ts new file mode 100644 index 000000000..04350f000 --- /dev/null +++ b/drizzle-orm/type-tests/sqlite/count.ts @@ -0,0 +1,61 @@ +import { Expect } from 'type-tests/utils.ts'; +import { and, gt, ne } from '~/expressions.ts'; +import { integer, sqliteTable, text } from '~/sqlite-core/index.ts'; +import type { Equal } from '~/utils.ts'; +import { db } from './db.ts'; + +const names = sqliteTable('names', { + id: integer('id').primaryKey(), + name: text('name'), + authorId: integer('author_id'), +}); + +const separate = await db.$count(names); + +const separateFilters = await db.$count(names, and(gt(names.id, 1), ne(names.name, 'forbidden'))); + +const embedded = await db + .select({ + id: names.id, + name: names.name, + authorId: names.authorId, + count1: db.$count(names).as('count1'), + }) + .from(names); + +const embeddedFilters = await db + .select({ + id: names.id, + name: names.name, + authorId: names.authorId, + count1: db.$count(names, and(gt(names.id, 1), ne(names.name, 'forbidden'))).as('count1'), + }) + .from(names); + +Expect>; + +Expect>; + +Expect< + Equal< + { + id: number; + name: string | null; + authorId: number | null; + count1: number; + }[], + typeof embedded + > +>; + +Expect< + Equal< + { + id: number; + name: string | null; + authorId: number | null; + count1: number; + }[], + typeof embeddedFilters + > +>; From 672b4414e14f236d9ce01948170139e42dc053e8 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 14:28:04 +0300 Subject: [PATCH 16/56] Added missing runtime tests --- integration-tests/tests/mysql/mysql-common.ts | 209 ++++++++++++++++++ integration-tests/tests/pg/pg-common.ts | 209 ++++++++++++++++++ .../tests/sqlite/sqlite-common.ts | 209 ++++++++++++++++++ 3 files changed, 627 insertions(+) diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index 58f7a1e2c..05c69cada 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -3577,6 +3577,215 @@ export function tests(driver?: string) { await db.execute(sql`drop view ${newYorkers1}`); }); + + test('$count separate', async (ctx) => { + const { db } = ctx.mysql; + + const countTestTable = mysqlTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.$count(countTestTable); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual(4); + }); + + test('$count embedded', async (ctx) => { + const { db } = ctx.mysql; + + const countTestTable = mysqlTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.select({ + count: db.$count(countTestTable), + }).from(countTestTable); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual([ + { count: 4 }, + { count: 4 }, + { count: 4 }, + { count: 4 }, + ]); + }); + + test('$count separate reuse', async (ctx) => { + const { db } = ctx.mysql; + + const countTestTable = mysqlTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = db.$count(countTestTable); + + const count1 = await count; + + await db.insert(countTestTable).values({ id: 5, name: 'fifth' }); + + const count2 = await count; + + await db.insert(countTestTable).values({ id: 6, name: 'sixth' }); + + const count3 = await count; + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count1).toStrictEqual(4); + expect(count2).toStrictEqual(5); + expect(count3).toStrictEqual(6); + }); + + test('$count embedded reuse', async (ctx) => { + const { db } = ctx.mysql; + + const countTestTable = mysqlTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = db.select({ + count: db.$count(countTestTable), + }).from(countTestTable); + + const count1 = await count; + + await db.insert(countTestTable).values({ id: 5, name: 'fifth' }); + + const count2 = await count; + + await db.insert(countTestTable).values({ id: 6, name: 'sixth' }); + + const count3 = await count; + + await db.execute(sql`drop table ${countTestTable}`); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count1).toStrictEqual([ + { count: 4 }, + { count: 4 }, + { count: 4 }, + { count: 4 }, + ]); + expect(count2).toStrictEqual([ + { count: 5 }, + { count: 5 }, + { count: 5 }, + { count: 5 }, + { count: 5 }, + ]); + expect(count3).toStrictEqual([ + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + ]); + }); + + test('$count separate with filters', async (ctx) => { + const { db } = ctx.mysql; + + const countTestTable = mysqlTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.$count(countTestTable, gt(countTestTable.id, 1)); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual(3); + }); + + test('$count embedded with filters', async (ctx) => { + const { db } = ctx.mysql; + + const countTestTable = mysqlTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.select({ + count: db.$count(countTestTable, gt(countTestTable.id, 1)), + }).from(countTestTable); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual([ + { count: 3 }, + { count: 3 }, + { count: 3 }, + ]); + }); }); test('limit 0', async (ctx) => { diff --git a/integration-tests/tests/pg/pg-common.ts b/integration-tests/tests/pg/pg-common.ts index c48a533f9..3f3dd75cc 100644 --- a/integration-tests/tests/pg/pg-common.ts +++ b/integration-tests/tests/pg/pg-common.ts @@ -4660,5 +4660,214 @@ export function tests() { jsonbNumberField: testNumber, }]); }); + + test('$count separate', async (ctx) => { + const { db } = ctx.pg; + + const countTestTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.$count(countTestTable); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual(4); + }); + + test('$count embedded', async (ctx) => { + const { db } = ctx.pg; + + const countTestTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.select({ + count: db.$count(countTestTable), + }).from(countTestTable); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual([ + { count: 4 }, + { count: 4 }, + { count: 4 }, + { count: 4 }, + ]); + }); + + test('$count separate reuse', async (ctx) => { + const { db } = ctx.pg; + + const countTestTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = db.$count(countTestTable); + + const count1 = await count; + + await db.insert(countTestTable).values({ id: 5, name: 'fifth' }); + + const count2 = await count; + + await db.insert(countTestTable).values({ id: 6, name: 'sixth' }); + + const count3 = await count; + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count1).toStrictEqual(4); + expect(count2).toStrictEqual(5); + expect(count3).toStrictEqual(6); + }); + + test('$count embedded reuse', async (ctx) => { + const { db } = ctx.pg; + + const countTestTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = db.select({ + count: db.$count(countTestTable), + }).from(countTestTable); + + const count1 = await count; + + await db.insert(countTestTable).values({ id: 5, name: 'fifth' }); + + const count2 = await count; + + await db.insert(countTestTable).values({ id: 6, name: 'sixth' }); + + const count3 = await count; + + await db.execute(sql`drop table ${countTestTable}`); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count1).toStrictEqual([ + { count: 4 }, + { count: 4 }, + { count: 4 }, + { count: 4 }, + ]); + expect(count2).toStrictEqual([ + { count: 5 }, + { count: 5 }, + { count: 5 }, + { count: 5 }, + { count: 5 }, + ]); + expect(count3).toStrictEqual([ + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + ]); + }); + + test('$count separate with filters', async (ctx) => { + const { db } = ctx.pg; + + const countTestTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.$count(countTestTable, gt(countTestTable.id, 1)); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual(3); + }); + + test('$count embedded with filters', async (ctx) => { + const { db } = ctx.pg; + + const countTestTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull(), + }); + + await db.execute(sql`drop table if exists ${countTestTable}`); + await db.execute(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.select({ + count: db.$count(countTestTable, gt(countTestTable.id, 1)), + }).from(countTestTable); + + await db.execute(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual([ + { count: 3 }, + { count: 3 }, + { count: 3 }, + ]); + }); }); } diff --git a/integration-tests/tests/sqlite/sqlite-common.ts b/integration-tests/tests/sqlite/sqlite-common.ts index be452bcf1..ed13f5b7b 100644 --- a/integration-tests/tests/sqlite/sqlite-common.ts +++ b/integration-tests/tests/sqlite/sqlite-common.ts @@ -2679,6 +2679,215 @@ export function tests() { expect(eachUser.updatedAt!.valueOf()).toBeGreaterThan(Date.now() - msDelay); } }); + + test('$count separate', async (ctx) => { + const { db } = ctx.sqlite; + + const countTestTable = sqliteTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.run(sql`drop table if exists ${countTestTable}`); + await db.run(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.$count(countTestTable); + + await db.run(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual(4); + }); + + test('$count embedded', async (ctx) => { + const { db } = ctx.sqlite; + + const countTestTable = sqliteTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.run(sql`drop table if exists ${countTestTable}`); + await db.run(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.select({ + count: db.$count(countTestTable), + }).from(countTestTable); + + await db.run(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual([ + { count: 4 }, + { count: 4 }, + { count: 4 }, + { count: 4 }, + ]); + }); + + test('$count separate reuse', async (ctx) => { + const { db } = ctx.sqlite; + + const countTestTable = sqliteTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.run(sql`drop table if exists ${countTestTable}`); + await db.run(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = db.$count(countTestTable); + + const count1 = await count; + + await db.insert(countTestTable).values({ id: 5, name: 'fifth' }); + + const count2 = await count; + + await db.insert(countTestTable).values({ id: 6, name: 'sixth' }); + + const count3 = await count; + + await db.run(sql`drop table ${countTestTable}`); + + expect(count1).toStrictEqual(4); + expect(count2).toStrictEqual(5); + expect(count3).toStrictEqual(6); + }); + + test('$count embedded reuse', async (ctx) => { + const { db } = ctx.sqlite; + + const countTestTable = sqliteTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.run(sql`drop table if exists ${countTestTable}`); + await db.run(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = db.select({ + count: db.$count(countTestTable), + }).from(countTestTable); + + const count1 = await count; + + await db.insert(countTestTable).values({ id: 5, name: 'fifth' }); + + const count2 = await count; + + await db.insert(countTestTable).values({ id: 6, name: 'sixth' }); + + const count3 = await count; + + await db.run(sql`drop table ${countTestTable}`); + + await db.run(sql`drop table ${countTestTable}`); + + expect(count1).toStrictEqual([ + { count: 4 }, + { count: 4 }, + { count: 4 }, + { count: 4 }, + ]); + expect(count2).toStrictEqual([ + { count: 5 }, + { count: 5 }, + { count: 5 }, + { count: 5 }, + { count: 5 }, + ]); + expect(count3).toStrictEqual([ + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + { count: 6 }, + ]); + }); + + test('$count separate with filters', async (ctx) => { + const { db } = ctx.sqlite; + + const countTestTable = sqliteTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.run(sql`drop table if exists ${countTestTable}`); + await db.run(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.$count(countTestTable, gt(countTestTable.id, 1)); + + await db.run(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual(3); + }); + + test('$count embedded with filters', async (ctx) => { + const { db } = ctx.sqlite; + + const countTestTable = sqliteTable('users_distinct', { + id: int('id').notNull(), + name: text('name').notNull(), + }); + + await db.run(sql`drop table if exists ${countTestTable}`); + await db.run(sql`create table ${countTestTable} (id int, name text)`); + + await db.insert(countTestTable).values([ + { id: 1, name: 'First' }, + { id: 2, name: 'Second' }, + { id: 3, name: 'Third' }, + { id: 4, name: 'Fourth' }, + ]); + + const count = await db.select({ + count: db.$count(countTestTable, gt(countTestTable.id, 1)), + }).from(countTestTable); + + await db.run(sql`drop table ${countTestTable}`); + + expect(count).toStrictEqual([ + { count: 3 }, + { count: 3 }, + { count: 3 }, + ]); + }); }); test('table configs: unique third param', () => { From 951e5496bd3a53bd76c261e9d9af45ee5461e105 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Mon, 12 Aug 2024 12:40:27 +0300 Subject: [PATCH 17/56] LibSQL improvements Added: - handling alteting columns in libsql - new json statement for recreation table for sqlite and libsql - push updated for sqlite and libsql - tests --- .../src/cli/commands/libSqlPushUtils.ts | 327 +++++ drizzle-kit/src/cli/commands/migrate.ts | 94 +- drizzle-kit/src/cli/commands/push.ts | 123 +- .../src/cli/commands/sqlitePushUtils.ts | 300 ++-- drizzle-kit/src/cli/commands/utils.ts | 5 + drizzle-kit/src/cli/schema.ts | 12 +- drizzle-kit/src/cli/validations/cli.ts | 3 +- drizzle-kit/src/jsonStatements.ts | 200 ++- drizzle-kit/src/snapshotsDiffer.ts | 437 +++++- drizzle-kit/src/sqlgenerator.ts | 389 +++++- drizzle-kit/src/statementCombiner.ts | 387 ++++++ drizzle-kit/src/utils.ts | 13 + drizzle-kit/tests/libsql-statements.test.ts | 835 +++++++++++ drizzle-kit/tests/push/libsql.test.ts | 744 ++++++++++ drizzle-kit/tests/push/sqlite.test.ts | 1233 ++++++++++++----- drizzle-kit/tests/schemaDiffer.ts | 273 +++- .../libsql-statements-combiner.test.ts | 1209 ++++++++++++++++ .../sqilte-statements-combiner.test.ts | 778 +++++++++++ 18 files changed, 6762 insertions(+), 600 deletions(-) create mode 100644 drizzle-kit/src/cli/commands/libSqlPushUtils.ts create mode 100644 drizzle-kit/src/statementCombiner.ts create mode 100644 drizzle-kit/tests/libsql-statements.test.ts create mode 100644 drizzle-kit/tests/push/libsql.test.ts create mode 100644 drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts create mode 100644 drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts diff --git a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts new file mode 100644 index 000000000..537eee0a5 --- /dev/null +++ b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts @@ -0,0 +1,327 @@ +import chalk from 'chalk'; + +import { JsonStatement } from 'src/jsonStatements'; +import { findAddedAndRemoved, SQLiteDB } from 'src/utils'; +import { SQLiteSchemaInternal, SQLiteSchemaSquashed, SQLiteSquasher } from '../../serializer/sqliteSchema'; +import { + CreateSqliteIndexConvertor, + fromJson, + LibSQLModifyColumn, + SQLiteCreateTableConvertor, + SQLiteDropTableConvertor, + SqliteRenameTableConvertor, +} from '../../sqlgenerator'; + +export const getOldTableName = ( + tableName: string, + meta: SQLiteSchemaInternal['_meta'], +) => { + for (const key of Object.keys(meta.tables)) { + const value = meta.tables[key]; + if (`"${tableName}"` === value) { + return key.substring(1, key.length - 1); + } + } + return tableName; +}; + +export const _moveDataStatements = ( + tableName: string, + json: SQLiteSchemaSquashed, + dataLoss: boolean = false, +) => { + const statements: string[] = []; + + statements.push( + new SqliteRenameTableConvertor().convert({ + type: 'rename_table', + tableNameFrom: tableName, + tableNameTo: `__old_push_${tableName}`, + fromSchema: '', + toSchema: '', + }), + ); + + const tableColumns = Object.values(json.tables[tableName].columns); + const referenceData = Object.values(json.tables[tableName].foreignKeys); + const compositePKs = Object.values( + json.tables[tableName].compositePrimaryKeys, + ).map((it) => SQLiteSquasher.unsquashPK(it)); + + statements.push( + new SQLiteCreateTableConvertor().convert({ + type: 'sqlite_create_table', + tableName: tableName, + columns: tableColumns, + referenceData: referenceData.map((it) => SQLiteSquasher.unsquashPushFK(it)), + compositePKs, + }), + ); + + if (!dataLoss) { + const columns = Object.keys(json.tables[tableName].columns).map( + (c) => `"${c}"`, + ); + + statements.push( + `INSERT INTO \`${tableName}\`(${ + columns.join( + ', ', + ) + }) SELECT (${columns.join(', ')}) FROM \`__old_push_${tableName}\`;`, + ); + } + + statements.push( + new SQLiteDropTableConvertor().convert({ + type: 'drop_table', + tableName: `__old_push_${tableName}`, + schema: '', + }), + ); + + for (const idx of Object.values(json.tables[tableName].indexes)) { + statements.push( + new CreateSqliteIndexConvertor().convert({ + type: 'create_index', + tableName: tableName, + schema: '', + data: idx, + }), + ); + } + + return statements; +}; + +export const libSqlLogSuggestionsAndReturn = async ( + connection: SQLiteDB, + statements: JsonStatement[], + json1: SQLiteSchemaSquashed, + json2: SQLiteSchemaSquashed, + meta: SQLiteSchemaInternal['_meta'], +) => { + let shouldAskForApprove = false; + const statementsToExecute: string[] = []; + const infoToPrint: string[] = []; + + const tablesToRemove: string[] = []; + const columnsToRemove: string[] = []; + const tablesToTruncate: string[] = []; + + for (const statement of statements) { + if (statement.type === 'drop_table') { + const res = await connection.query<{ count: string }>( + `select count(*) as count from \`${statement.tableName}\``, + ); + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to delete ${ + chalk.underline( + statement.tableName, + ) + } table with ${count} items`, + ); + tablesToRemove.push(statement.tableName); + shouldAskForApprove = true; + } + const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } else if (statement.type === 'alter_table_drop_column') { + const tableName = statement.tableName; + + const res = await connection.query<{ count: string }>( + `select count(*) as count from \`${tableName}\``, + ); + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to delete ${ + chalk.underline( + statement.columnName, + ) + } column in ${tableName} table with ${count} items`, + ); + columnsToRemove.push(`${tableName}_${statement.columnName}`); + shouldAskForApprove = true; + } + + const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } else if ( + statement.type === 'sqlite_alter_table_add_column' + && statement.column.notNull + && !statement.column.default + ) { + const newTableName = statement.tableName; + const res = await connection.query<{ count: string }>( + `select count(*) as count from \`${newTableName}\``, + ); + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to add not-null ${ + chalk.underline( + statement.column.name, + ) + } column without default value, which contains ${count} items`, + ); + + tablesToTruncate.push(newTableName); + statementsToExecute.push(`delete from ${newTableName};`); + + shouldAskForApprove = true; + } + + const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } else if (statement.type === 'alter_table_alter_column_set_notnull') { + const tableName = statement.tableName; + + if ( + statement.type === 'alter_table_alter_column_set_notnull' + && typeof statement.columnDefault === 'undefined' + ) { + const res = await connection.query<{ count: string }>( + `select count(*) as count from \`${tableName}\``, + ); + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to add not-null constraint to ${ + chalk.underline( + statement.columnName, + ) + } column without default value, which contains ${count} items`, + ); + + tablesToTruncate.push(tableName); + statementsToExecute.push(`delete from \`${tableName}\``); + shouldAskForApprove = true; + } + } + + const modifyStatements = new LibSQLModifyColumn().convert(statement, json2); + + statementsToExecute.push( + ...(Array.isArray(modifyStatements) ? modifyStatements : [modifyStatements]), + ); + } else if (statement.type === 'recreate_table') { + const tableName = statement.tableName; + + const oldTableName = getOldTableName(tableName, meta); + + const prevColumnNames = Object.keys(json1.tables[oldTableName].columns); + const currentColumnNames = Object.keys(json2.tables[tableName].columns); + const { removedColumns, addedColumns } = findAddedAndRemoved( + prevColumnNames, + currentColumnNames, + ); + + if (removedColumns.length) { + for (const removedColumn of removedColumns) { + const res = await connection.query<{ count: string }>( + `select count(\`${tableName}\`.\`${removedColumn}\`) as count from \`${tableName}\``, + ); + + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to delete ${ + chalk.underline( + removedColumn, + ) + } column in ${tableName} table with ${count} items`, + ); + columnsToRemove.push(removedColumn); + shouldAskForApprove = true; + } + } + } + + if (addedColumns.length) { + for (const addedColumn of addedColumns) { + const [res] = await connection.query<{ count: string }>( + `select count(\`${tableName}\`.\`${addedColumn}\`) as count from \`${tableName}\``, + ); + + const columnConf = json2.tables[tableName].columns[addedColumn]; + + const count = Number(res.count); + if (count > 0 && columnConf.notNull && !columnConf.default) { + infoToPrint.push( + `· You're about to add not-null ${ + chalk.underline( + addedColumn, + ) + } column without default value, which contains ${count} items`, + ); + shouldAskForApprove = true; + tablesToTruncate.push(tableName); + } + } + } + + statementsToExecute.push(..._moveDataStatements(tableName, json2)); + + const tablesReferencingCurrent: string[] = []; + + for (const table of Object.values(json2.tables)) { + const tablesRefs = Object.values(json2.tables[table.name].foreignKeys) + .filter((t) => SQLiteSquasher.unsquashPushFK(t).tableTo === tableName) + .map((it) => SQLiteSquasher.unsquashPushFK(it).tableFrom); + + tablesReferencingCurrent.push(...tablesRefs); + } + + const uniqueTableRefs = [...new Set(tablesReferencingCurrent)]; + + for (const table of uniqueTableRefs) { + statementsToExecute.push(..._moveDataStatements(table, json2)); + } + } else if (statement.type === 'alter_table_alter_column_set_generated') { + const tableName = statement.tableName; + + const res = await connection.query<{ count: string }>( + `select count("${statement.columnName}") as count from \`${tableName}\``, + ); + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to delete ${ + chalk.underline( + statement.columnName, + ) + } column in ${tableName} table with ${count} items`, + ); + columnsToRemove.push(`${tableName}_${statement.columnName}`); + shouldAskForApprove = true; + } + const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } else { + const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } + } + + return { + statementsToExecute: [...new Set(statementsToExecute)], + shouldAskForApprove, + infoToPrint, + columnsToRemove: [...new Set(columnsToRemove)], + tablesToTruncate: [...new Set(tablesToTruncate)], + tablesToRemove: [...new Set(tablesToRemove)], + }; +}; diff --git a/drizzle-kit/src/cli/commands/migrate.ts b/drizzle-kit/src/cli/commands/migrate.ts index 0672fe734..664e7da02 100644 --- a/drizzle-kit/src/cli/commands/migrate.ts +++ b/drizzle-kit/src/cli/commands/migrate.ts @@ -13,6 +13,7 @@ import { import chalk from 'chalk'; import { render } from 'hanji'; import path, { join } from 'path'; +import { JsonStatement } from 'src/jsonStatements'; import { SingleStoreSchema, singlestoreSchema, squashSingleStoreScheme } from 'src/serializer/singlestoreSchema'; import { TypeOf } from 'zod'; import type { CommonSchema } from '../../schemaValidator'; @@ -20,6 +21,7 @@ import { MySqlSchema, mysqlSchema, squashMysqlScheme } from '../../serializer/my import { PgSchema, pgSchema, squashPgScheme } from '../../serializer/pgSchema'; import { SQLiteSchema, sqliteSchema, squashSqliteScheme } from '../../serializer/sqliteSchema'; import { + applyLibSQLSnapshotsDiff, applyMysqlSnapshotsDiff, applyPgSnapshotsDiff, applySingleStoreSnapshotsDiff, @@ -36,7 +38,7 @@ import { } from '../../snapshotsDiffer'; import { assertV1OutFolder, Journal, prepareMigrationFolder } from '../../utils'; import { prepareMigrationMetadata } from '../../utils/words'; -import { Prefix } from '../validations/common'; +import { Driver, Prefix } from '../validations/common'; import { withStyle } from '../validations/outputs'; import { isRenamePromptItem, @@ -490,6 +492,7 @@ export const prepareAndMigrateSingleStore = async (config: GenerateConfig) => { export const prepareAndMigrateSqlite = async (config: GenerateConfig) => { const outFolder = config.out; const schemaPath = config.schema; + const driver = config.driver; try { assertV1OutFolder(outFolder); @@ -521,14 +524,34 @@ export const prepareAndMigrateSqlite = async (config: GenerateConfig) => { const squashedPrev = squashSqliteScheme(validatedPrev); const squashedCur = squashSqliteScheme(validatedCur); - const { sqlStatements, _meta } = await applySqliteSnapshotsDiff( - squashedPrev, - squashedCur, - tablesResolver, - columnsResolver, - validatedPrev, - validatedCur, - ); + let sqlStatements: string[]; + let _meta: + | { + schemas: {}; + tables: {}; + columns: {}; + } + | undefined; + + if (driver === 'turso') { + ({ sqlStatements, _meta } = await applyLibSQLSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + )); + } else { + ({ sqlStatements, _meta } = await applySqliteSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + )); + } writeResult({ cur, @@ -549,6 +572,57 @@ export const prepareAndMigrateSqlite = async (config: GenerateConfig) => { export const prepareSQLitePush = async ( schemaPath: string | string[], snapshot: SQLiteSchema, + driver?: Driver, +) => { + const { prev, cur } = await prepareSQLiteDbPushSnapshot(snapshot, schemaPath); + + const validatedPrev = sqliteSchema.parse(prev); + const validatedCur = sqliteSchema.parse(cur); + + const squashedPrev = squashSqliteScheme(validatedPrev, 'push'); + const squashedCur = squashSqliteScheme(validatedCur, 'push'); + + let sqlStatements: string[]; + let statements: JsonStatement[]; + let _meta: { + schemas: {}; + tables: {}; + columns: {}; + } | undefined; + if (driver === 'turso') { + ({ sqlStatements, statements, _meta } = await applyLibSQLSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + 'push', + )); + } else { + ({ sqlStatements, statements, _meta } = await applySqliteSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + 'push', + )); + } + + return { + sqlStatements, + statements, + squashedPrev, + squashedCur, + meta: _meta, + }; +}; + +export const prepareLibSQLPush = async ( + schemaPath: string | string[], + snapshot: SQLiteSchema, ) => { const { prev, cur } = await prepareSQLiteDbPushSnapshot(snapshot, schemaPath); @@ -558,7 +632,7 @@ export const prepareSQLitePush = async ( const squashedPrev = squashSqliteScheme(validatedPrev, 'push'); const squashedCur = squashSqliteScheme(validatedCur, 'push'); - const { sqlStatements, statements, _meta } = await applySqliteSnapshotsDiff( + const { sqlStatements, statements, _meta } = await applyLibSQLSnapshotsDiff( squashedPrev, squashedCur, tablesResolver, diff --git a/drizzle-kit/src/cli/commands/push.ts b/drizzle-kit/src/cli/commands/push.ts index d54f8c6eb..e8aaf2e2b 100644 --- a/drizzle-kit/src/cli/commands/push.ts +++ b/drizzle-kit/src/cli/commands/push.ts @@ -7,6 +7,7 @@ import { withStyle } from '../validations/outputs'; import type { PostgresCredentials } from '../validations/postgres'; import { SingleStoreCredentials } from '../validations/singlestore'; import type { SqliteCredentials } from '../validations/sqlite'; +import { libSqlLogSuggestionsAndReturn } from './libSqlPushUtils'; import { filterStatements as mySqlFilterStatements, logSuggestionsAndReturn as mySqlLogSuggestionsAndReturn, @@ -76,8 +77,6 @@ export const mysqlPush = async ( }); if (verbose) { - console.log(); - // console.log(chalk.gray('Verbose logs:')); console.log( withStyle.warning('You are about to execute current statements:'), ); @@ -439,8 +438,128 @@ export const sqlitePush = async ( } = await sqliteSuggestions( db, statements.statements, + statements.squashedPrev, statements.squashedCur, + statements.meta!, + ); + + if (verbose && statementsToExecute.length > 0) { + console.log(); + console.log( + withStyle.warning('You are about to execute current statements:'), + ); + console.log(); + console.log(statementsToExecute.map((s) => chalk.blue(s)).join('\n')); + console.log(); + } + + if (!force && strict) { + if (!shouldAskForApprove) { + const { status, data } = await render( + new Select(['No, abort', `Yes, I want to execute all statements`]), + ); + if (data?.index === 0) { + render(`[${chalk.red('x')}] All changes were aborted`); + process.exit(0); + } + } + } + + if (!force && shouldAskForApprove) { + console.log(withStyle.warning('Found data-loss statements:')); + console.log(infoToPrint.join('\n')); + console.log(); + console.log( + chalk.red.bold( + 'THIS ACTION WILL CAUSE DATA LOSS AND CANNOT BE REVERTED\n', + ), + ); + + console.log(chalk.white('Do you still want to push changes?')); + + const { status, data } = await render( + new Select([ + 'No, abort', + `Yes, I want to${ + tablesToRemove.length > 0 + ? ` remove ${tablesToRemove.length} ${tablesToRemove.length > 1 ? 'tables' : 'table'},` + : ' ' + }${ + columnsToRemove.length > 0 + ? ` remove ${columnsToRemove.length} ${columnsToRemove.length > 1 ? 'columns' : 'column'},` + : ' ' + }${ + tablesToTruncate.length > 0 + ? ` truncate ${tablesToTruncate.length} ${tablesToTruncate.length > 1 ? 'tables' : 'table'}` + : '' + }` + .trimEnd() + .replace(/(^,)|(,$)/g, '') + .replace(/ +(?= )/g, ''), + ]), + ); + if (data?.index === 0) { + render(`[${chalk.red('x')}] All changes were aborted`); + process.exit(0); + } + } + + if (statementsToExecute.length === 0) { + render(`\n[${chalk.blue('i')}] No changes detected`); + } else { + if (!('driver' in credentials)) { + await db.query('begin'); + try { + for (const dStmnt of statementsToExecute) { + await db.query(dStmnt); + } + await db.query('commit'); + } catch (e) { + console.error(e); + await db.query('rollback'); + process.exit(1); + } + } else if (credentials.driver === 'turso') { + await db.batch!(statementsToExecute.map((it) => ({ query: it }))); + } + render(`[${chalk.green('✓')}] Changes applied`); + } + } +}; + +export const libSQLPush = async ( + schemaPath: string | string[], + verbose: boolean, + strict: boolean, + credentials: SqliteCredentials, + tablesFilter: string[], + force: boolean, +) => { + const { connectToSQLite } = await import('../connections'); + const { sqlitePushIntrospect } = await import('./sqliteIntrospect'); + + const db = await connectToSQLite(credentials); + const { schema } = await sqlitePushIntrospect(db, tablesFilter); + + const { prepareLibSQLPush } = await import('./migrate'); + + const statements = await prepareLibSQLPush(schemaPath, schema); + + if (statements.sqlStatements.length === 0) { + render(`\n[${chalk.blue('i')}] No changes detected`); + } else { + const { + shouldAskForApprove, + statementsToExecute, + columnsToRemove, + tablesToRemove, + tablesToTruncate, + infoToPrint, + } = await libSqlLogSuggestionsAndReturn( + db, + statements.statements, statements.squashedPrev, + statements.squashedCur, statements.meta!, ); diff --git a/drizzle-kit/src/cli/commands/sqlitePushUtils.ts b/drizzle-kit/src/cli/commands/sqlitePushUtils.ts index 451f035a7..e04546157 100644 --- a/drizzle-kit/src/cli/commands/sqlitePushUtils.ts +++ b/drizzle-kit/src/cli/commands/sqlitePushUtils.ts @@ -10,7 +10,7 @@ import { } from '../../sqlgenerator'; import type { JsonStatement } from '../../jsonStatements'; -import type { DB, SQLiteDB } from '../../utils'; +import { findAddedAndRemoved, type SQLiteDB } from '../../utils'; export const _moveDataStatements = ( tableName: string, @@ -51,8 +51,16 @@ export const _moveDataStatements = ( // move data if (!dataLoss) { + const columns = Object.keys(json.tables[tableName].columns).map( + (c) => `"${c}"`, + ); + statements.push( - `INSERT INTO "${tableName}" SELECT * FROM "__old_push_${tableName}";`, + `INSERT INTO \`${tableName}\`(${ + columns.join( + ', ', + ) + }) SELECT (${columns.join(', ')}) FROM \`__old_push_${tableName}\`;`, ); } // drop table with name __old_${tablename} @@ -120,8 +128,6 @@ export const logSuggestionsAndReturn = async ( const schemasToRemove: string[] = []; const tablesToTruncate: string[] = []; - const tablesContext: Record = {}; - for (const statement of statements) { if (statement.type === 'drop_table') { const res = await connection.query<{ count: string }>( @@ -139,248 +145,144 @@ export const logSuggestionsAndReturn = async ( tablesToRemove.push(statement.tableName); shouldAskForApprove = true; } - const stmnt = fromJson([statement], 'sqlite')[0]; - statementsToExecute.push(stmnt); - } else if (statement.type === 'alter_table_drop_column') { - const newTableName = getOldTableName(statement.tableName, meta); - const columnIsPartOfPk = Object.values( - json1.tables[newTableName].compositePrimaryKeys, - ).find((c) => SQLiteSquasher.unsquashPK(c).includes(statement.columnName)); - - const columnIsPartOfIndex = Object.values( - json1.tables[newTableName].indexes, - ).find((c) => SQLiteSquasher.unsquashIdx(c).columns.includes(statement.columnName)); - - const columnIsPk = json1.tables[newTableName].columns[statement.columnName].primaryKey; - - const columnIsPartOfFk = Object.values( - json1.tables[newTableName].foreignKeys, - ).find((t) => - SQLiteSquasher.unsquashPushFK(t).columnsFrom.includes( - statement.columnName, - ) + const fromJsonStatement = fromJson([statement], 'sqlite', 'push'); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), ); + } else if (statement.type === 'alter_table_drop_column') { + const tableName = statement.tableName; + const columnName = statement.columnName; const res = await connection.query<{ count: string }>( - `select count(*) as count from \`${newTableName}\``, + `select count(\`${tableName}\`.\`${columnName}\`) as count from \`${tableName}\``, ); const count = Number(res[0].count); if (count > 0) { infoToPrint.push( `· You're about to delete ${ chalk.underline( - statement.columnName, + columnName, ) - } column in ${newTableName} table with ${count} items`, + } column in ${tableName} table with ${count} items`, ); - columnsToRemove.push(`${newTableName}_${statement.columnName}`); + columnsToRemove.push(`${tableName}_${statement.columnName}`); shouldAskForApprove = true; } - if ( - columnIsPk - || columnIsPartOfPk - || columnIsPartOfIndex - || columnIsPartOfFk - ) { - tablesContext[newTableName] = [ - ..._moveDataStatements(statement.tableName, json2, true), - ]; - // check table that have fk to this table - - const tablesReferncingCurrent: string[] = []; - - for (const table of Object.values(json1.tables)) { - const tablesRefs = Object.values(json1.tables[table.name].foreignKeys) - .filter( - (t) => SQLiteSquasher.unsquashPushFK(t).tableTo === newTableName, + const fromJsonStatement = fromJson([statement], 'sqlite', 'push'); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } else if ( + statement.type === 'sqlite_alter_table_add_column' + && (statement.column.notNull && !statement.column.default) + ) { + const tableName = statement.tableName; + const columnName = statement.column.name; + const res = await connection.query<{ count: string }>( + `select count(*) as count from \`${tableName}\``, + ); + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to add not-null ${ + chalk.underline( + columnName, ) - .map((t) => SQLiteSquasher.unsquashPushFK(t).tableFrom); - - tablesReferncingCurrent.push(...tablesRefs); - } - - const uniqueTableRefs = [...new Set(tablesReferncingCurrent)]; - - for (const table of uniqueTableRefs) { - if (typeof tablesContext[table] === 'undefined') { - tablesContext[table] = [..._moveDataStatements(table, json2)]; - } - } - } else { - if (typeof tablesContext[newTableName] === 'undefined') { - const stmnt = fromJson([statement], 'sqlite')[0]; - statementsToExecute.push(stmnt); - } - } - } else if (statement.type === 'sqlite_alter_table_add_column') { - const newTableName = getOldTableName(statement.tableName, meta); - if (statement.column.notNull && !statement.column.default) { - const res = await connection.query<{ count: string }>( - `select count(*) as count from \`${newTableName}\``, + } column without default value, which contains ${count} items`, ); - const count = Number(res[0].count); - if (count > 0) { - infoToPrint.push( - `· You're about to add not-null ${ - chalk.underline( - statement.column.name, - ) - } column without default value, which contains ${count} items`, - ); - tablesToTruncate.push(newTableName); - statementsToExecute.push(`delete from ${newTableName};`); + tablesToTruncate.push(tableName); + statementsToExecute.push(`delete from ${tableName};`); - shouldAskForApprove = true; - } + shouldAskForApprove = true; } - if (statement.column.primaryKey) { - tablesContext[newTableName] = [ - ..._moveDataStatements(statement.tableName, json2, true), - ]; - const tablesReferncingCurrent: string[] = []; - - for (const table of Object.values(json1.tables)) { - const tablesRefs = Object.values(json1.tables[table.name].foreignKeys) - .filter( - (t) => SQLiteSquasher.unsquashPushFK(t).tableTo === newTableName, - ) - .map((t) => SQLiteSquasher.unsquashPushFK(t).tableFrom); - tablesReferncingCurrent.push(...tablesRefs); - } - - const uniqueTableRefs = [...new Set(tablesReferncingCurrent)]; + const fromJsonStatement = fromJson([statement], 'sqlite', 'push'); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); + } else if (statement.type === 'recreate_table') { + const tableName = statement.tableName; + const oldTableName = getOldTableName(tableName, meta); + + const prevColumnNames = Object.keys(json1.tables[oldTableName].columns); + const currentColumnNames = Object.keys(json2.tables[tableName].columns); + const { removedColumns, addedColumns } = findAddedAndRemoved( + prevColumnNames, + currentColumnNames, + ); - for (const table of uniqueTableRefs) { - if (typeof tablesContext[table] === 'undefined') { - tablesContext[table] = [..._moveDataStatements(table, json2)]; - } - } - } else { - if (typeof tablesContext[newTableName] === 'undefined') { - const stmnt = fromJson([statement], 'sqlite')[0]; - statementsToExecute.push(stmnt); - } - } - } else if ( - statement.type === 'alter_table_alter_column_set_type' - || statement.type === 'alter_table_alter_column_set_default' - || statement.type === 'alter_table_alter_column_drop_default' - || statement.type === 'alter_table_alter_column_set_notnull' - || statement.type === 'alter_table_alter_column_drop_notnull' - || statement.type === 'alter_table_alter_column_drop_autoincrement' - || statement.type === 'alter_table_alter_column_set_autoincrement' - || statement.type === 'alter_table_alter_column_drop_pk' - || statement.type === 'alter_table_alter_column_set_pk' - ) { - if ( - !( - statement.type === 'alter_table_alter_column_set_notnull' - && statement.columnPk - ) - ) { - const newTableName = getOldTableName(statement.tableName, meta); - if ( - statement.type === 'alter_table_alter_column_set_notnull' - && typeof statement.columnDefault === 'undefined' - ) { + if (removedColumns.length) { + for (const removedColumn of removedColumns) { const res = await connection.query<{ count: string }>( - `select count(*) as count from \`${newTableName}\``, + `select count(\`${tableName}\`.\`${removedColumn}\`) as count from \`${tableName}\``, ); + const count = Number(res[0].count); if (count > 0) { infoToPrint.push( - `· You're about to add not-null constraint to ${ + `· You're about to delete ${ chalk.underline( - statement.columnName, + removedColumn, ) - } column without default value, which contains ${count} items`, + } column in ${tableName} table with ${count} items`, ); - - tablesToTruncate.push(newTableName); + columnsToRemove.push(removedColumn); shouldAskForApprove = true; } - tablesContext[newTableName] = _moveDataStatements( - statement.tableName, - json1, - true, + } + } + + if (addedColumns.length) { + for (const addedColumn of addedColumns) { + const [res] = await connection.query<{ count: string }>( + `select count(*) as count from \`${tableName}\``, ); - } else { - if (typeof tablesContext[newTableName] === 'undefined') { - tablesContext[newTableName] = _moveDataStatements( - statement.tableName, - json1, + + const columnConf = json2.tables[tableName].columns[addedColumn]; + + const count = Number(res.count); + if (count > 0 && columnConf.notNull && !columnConf.default) { + infoToPrint.push( + `· You're about to add not-null ${ + chalk.underline( + addedColumn, + ) + } column without default value to table, which contains ${count} items`, ); + shouldAskForApprove = true; + tablesToTruncate.push(tableName); } } + } - const tablesReferncingCurrent: string[] = []; + statementsToExecute.push(..._moveDataStatements(tableName, json2)); - for (const table of Object.values(json1.tables)) { - const tablesRefs = Object.values(json1.tables[table.name].foreignKeys) - .filter( - (t) => SQLiteSquasher.unsquashPushFK(t).tableTo === newTableName, - ) - .map((t) => { - return getNewTableName( - SQLiteSquasher.unsquashPushFK(t).tableFrom, - meta, - ); - }); - - tablesReferncingCurrent.push(...tablesRefs); - } + const tablesReferencingCurrent: string[] = []; - const uniqueTableRefs = [...new Set(tablesReferncingCurrent)]; + for (const table of Object.values(json2.tables)) { + const tablesRefs = Object.values(json2.tables[table.name].foreignKeys) + .filter((t) => SQLiteSquasher.unsquashPushFK(t).tableTo === tableName) + .map((it) => SQLiteSquasher.unsquashPushFK(it).tableFrom); - for (const table of uniqueTableRefs) { - if (typeof tablesContext[table] === 'undefined') { - tablesContext[table] = [..._moveDataStatements(table, json1)]; - } - } + tablesReferencingCurrent.push(...tablesRefs); } - } else if ( - statement.type === 'create_reference' - || statement.type === 'delete_reference' - || statement.type === 'alter_reference' - ) { - const fk = SQLiteSquasher.unsquashPushFK(statement.data); - if (typeof tablesContext[statement.tableName] === 'undefined') { - tablesContext[statement.tableName] = _moveDataStatements( - statement.tableName, - json2, - ); - } - } else if ( - statement.type === 'create_composite_pk' - || statement.type === 'alter_composite_pk' - || statement.type === 'delete_composite_pk' - || statement.type === 'create_unique_constraint' - || statement.type === 'delete_unique_constraint' - ) { - const newTableName = getOldTableName(statement.tableName, meta); - if (typeof tablesContext[newTableName] === 'undefined') { - tablesContext[newTableName] = _moveDataStatements( - statement.tableName, - json2, - ); + const uniqueTableRefs = [...new Set(tablesReferencingCurrent)]; + + for (const table of uniqueTableRefs) { + statementsToExecute.push(..._moveDataStatements(table, json2)); } } else { - const stmnt = fromJson([statement], 'sqlite'); - if (typeof stmnt !== 'undefined') { - statementsToExecute.push(...stmnt); - } + const fromJsonStatement = fromJson([statement], 'sqlite', 'push'); + statementsToExecute.push( + ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), + ); } } - for (const context of Object.values(tablesContext)) { - statementsToExecute.push(...context); - } - return { statementsToExecute, shouldAskForApprove, diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index 8f51d0c18..d8c704e7a 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -127,6 +127,7 @@ export type GenerateConfig = { prefix: Prefix; custom: boolean; bundle: boolean; + driver?: Driver; }; export const prepareGenerateConfig = async ( @@ -173,6 +174,7 @@ export const prepareGenerateConfig = async ( schema: schema, out: out || 'drizzle', bundle: driver === 'expo', + driver, }; }; @@ -215,6 +217,7 @@ export const preparePushConfig = async ( | { dialect: 'sqlite'; credentials: SqliteCredentials; + driver?: Driver; } | { dialect: 'singlestore'; @@ -235,6 +238,7 @@ export const preparePushConfig = async ( : options, ); + raw.driver ||= options.driver; raw.verbose ||= options.verbose; // if provided in cli to debug raw.strict ||= options.strict; // if provided in cli only @@ -352,6 +356,7 @@ export const preparePushConfig = async ( credentials: parsed.data, tablesFilter, schemasFilter, + driver: config.driver, }; } diff --git a/drizzle-kit/src/cli/schema.ts b/drizzle-kit/src/cli/schema.ts index 4da8af0ac..bf825d3e3 100644 --- a/drizzle-kit/src/cli/schema.ts +++ b/drizzle-kit/src/cli/schema.ts @@ -294,7 +294,7 @@ export const push = command({ schemasFilter, force, ); - } else if (dialect === 'sqlite') { + } else if (dialect === 'sqlite' && !('driver' in credentials)) { const { sqlitePush } = await import('./commands/push'); await sqlitePush( schemaPath, @@ -304,6 +304,16 @@ export const push = command({ tablesFilter, force, ); + } else if (dialect === 'sqlite' && ('driver' in credentials && credentials.driver === 'turso')) { + const { libSQLPush } = await import('./commands/push'); + await libSQLPush( + schemaPath, + verbose, + strict, + credentials, + tablesFilter, + force, + ); } else { assertUnreachable(dialect); } diff --git a/drizzle-kit/src/cli/validations/cli.ts b/drizzle-kit/src/cli/validations/cli.ts index c4bbbe530..9f982b058 100644 --- a/drizzle-kit/src/cli/validations/cli.ts +++ b/drizzle-kit/src/cli/validations/cli.ts @@ -1,6 +1,6 @@ import { boolean, intersection, literal, object, string, TypeOf, union } from 'zod'; import { dialect } from '../../schemaValidator'; -import { casing, prefix } from './common'; +import { casing, driver, prefix } from './common'; export const cliConfigGenerate = object({ dialect: dialect.optional(), @@ -25,6 +25,7 @@ export const pushParams = object({ extensionsFilters: literal('postgis').array().optional(), verbose: boolean().optional(), strict: boolean().optional(), + driver: driver, }).passthrough(); export type PushParams = TypeOf; diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index c099a9a69..dc9511e4b 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -1,11 +1,14 @@ import chalk from 'chalk'; -import { table } from 'console'; +import { getNewTableName } from './cli/commands/sqlitePushUtils'; import { warning } from './cli/views'; -import { CommonSquashedSchema, Dialect } from './schemaValidator'; +import { CommonSquashedSchema } from './schemaValidator'; import { MySqlKitInternals, MySqlSchema, MySqlSquasher } from './serializer/mysqlSchema'; import { Index, PgSchema, PgSquasher } from './serializer/pgSchema'; import { SingleStoreKitInternals, SingleStoreSchema, SingleStoreSquasher } from './serializer/singlestoreSchema'; -import { SQLiteKitInternals, SQLiteSquasher } from './serializer/sqliteSchema'; +import { + SQLiteKitInternals, SQLiteSchemaInternal, + SQLiteSchemaSquashed, SQLiteSquasher +} from './serializer/sqliteSchema'; import { AlteredColumn, Column, Sequence, Table } from './snapshotsDiffer'; export interface JsonSqliteCreateTableStatement { @@ -36,6 +39,23 @@ export interface JsonCreateTableStatement { internals?: MySqlKitInternals | SingleStoreKitInternals; } +export interface JsonRecreateTableStatement { + type: 'recreate_table'; + tableName: string; + columns: Column[]; + referenceData: { + name: string; + tableFrom: string; + columnsFrom: string[]; + tableTo: string; + columnsTo: string[]; + onUpdate?: string | undefined; + onDelete?: string | undefined; + }[]; + compositePKs: string[][]; + uniqueConstraints?: string[]; +} + export interface JsonDropTableStatement { type: 'drop_table'; tableName: string; @@ -174,6 +194,10 @@ export interface JsonReferenceStatement { data: string; schema: string; tableName: string; + isMulticolumn?: boolean; + columnNotNull?: boolean; + columnDefault?: string; + columnType?: string; // fromTable: string; // fromColumns: string[]; // toTable: string; @@ -520,6 +544,7 @@ export type JsonAlterColumnStatement = | JsonAlterColumnDropIdentityStatement; export type JsonStatement = + | JsonRecreateTableStatement | JsonAlterColumnStatement | JsonCreateTableStatement | JsonDropTableStatement @@ -1996,6 +2021,7 @@ export const prepareSqliteAlterColumns = ( columns: AlteredColumn[], // TODO: remove? json2: CommonSquashedSchema, + driver?: 'turso', ): JsonAlterColumnStatement[] => { let statements: JsonAlterColumnStatement[] = []; let dropPkStatements: JsonAlterColumnDropPrimaryKeyStatement[] = []; @@ -2023,6 +2049,55 @@ export const prepareSqliteAlterColumns = ( `${tableName}_${columnName}` ]; + if (column.autoincrement?.type === 'added') { + statements.push({ + type: 'alter_table_alter_column_set_autoincrement', + tableName, + columnName, + schema, + newDataType: columnType, + columnDefault, + columnOnUpdate, + columnNotNull, + columnAutoIncrement, + columnPk, + }); + } + + if (column.autoincrement?.type === 'changed') { + const type = column.autoincrement.new + ? 'alter_table_alter_column_set_autoincrement' + : 'alter_table_alter_column_drop_autoincrement'; + + statements.push({ + type, + tableName, + columnName, + schema, + newDataType: columnType, + columnDefault, + columnOnUpdate, + columnNotNull, + columnAutoIncrement, + columnPk, + }); + } + + if (column.autoincrement?.type === 'deleted') { + statements.push({ + type: 'alter_table_alter_column_drop_autoincrement', + tableName, + columnName, + schema, + newDataType: columnType, + columnDefault, + columnOnUpdate, + columnNotNull, + columnAutoIncrement, + columnPk, + }); + } + if (typeof column.name !== 'string') { statements.push({ type: 'alter_table_rename_column', @@ -2330,6 +2405,54 @@ export const prepareCreateReferencesJson = ( }; }); }; +export const prepareLibSQLCreateReferencesJson = ( + tableName: string, + schema: string, + foreignKeys: Record, + json2: SQLiteSchemaSquashed, + action?: 'push', +): JsonCreateReferenceStatement[] => { + return Object.values(foreignKeys).map((fkData) => { + const { columnsFrom, tableFrom, columnsTo } = action === 'push' + ? SQLiteSquasher.unsquashPushFK(fkData) + : SQLiteSquasher.unsquashFK(fkData); + + // When trying to alter table in lib sql it is necessary to pass all config for column like "NOT NULL", "DEFAULT", etc. + // If it is multicolumn reference it is not possible to pass this data for all columns + // Pass multicolumn flag for sql statements to not generate migration + let isMulticolumn = false; + + if (columnsFrom.length > 1 || columnsTo.length > 1) { + isMulticolumn = true; + + return { + type: 'create_reference', + tableName, + data: fkData, + schema, + isMulticolumn, + }; + } + + const columnFrom = columnsFrom[0]; + + const { + notNull: columnNotNull, + default: columnDefault, + type: columnType, + } = json2.tables[tableFrom].columns[columnFrom]; + + return { + type: 'create_reference', + tableName, + data: fkData, + schema, + columnNotNull, + columnDefault, + columnType, + }; + }); +}; export const prepareDropReferencesJson = ( tableName: string, @@ -2345,6 +2468,77 @@ export const prepareDropReferencesJson = ( }; }); }; +export const prepareLibSQLDropReferencesJson = ( + tableName: string, + schema: string, + foreignKeys: Record, + json2: SQLiteSchemaSquashed, + meta: SQLiteSchemaInternal['_meta'], + action?: 'push', +): JsonDeleteReferenceStatement[] => { + const statements = Object.values(foreignKeys).map((fkData) => { + const { columnsFrom, tableFrom, columnsTo, name, tableTo, onDelete, onUpdate } = action === 'push' + ? SQLiteSquasher.unsquashPushFK(fkData) + : SQLiteSquasher.unsquashFK(fkData); + + // If all columns from where were references were deleted -> skip this logic + // Drop colums will cover this scenario + const keys = Object.keys(json2.tables[tableName].columns); + const filtered = columnsFrom.filter((it) => keys.includes(it)); + const fullDrop = filtered.length === 0; + if (fullDrop) return; + + // When trying to alter table in lib sql it is necessary to pass all config for column like "NOT NULL", "DEFAULT", etc. + // If it is multicolumn reference it is not possible to pass this data for all columns + // Pass multicolumn flag for sql statements to not generate migration + let isMulticolumn = false; + + if (columnsFrom.length > 1 || columnsTo.length > 1) { + isMulticolumn = true; + + return { + type: 'delete_reference', + tableName, + data: fkData, + schema, + isMulticolumn, + }; + } + + const columnFrom = columnsFrom[0]; + const newTableName = getNewTableName(tableFrom, meta); + + const { + notNull: columnNotNull, + default: columnDefault, + type: columnType, + } = json2.tables[newTableName].columns[columnFrom]; + + const fkToSquash = { + columnsFrom, + columnsTo, + name, + tableFrom: newTableName, + tableTo, + onDelete, + onUpdate, + }; + const foreignKey = action === 'push' + ? SQLiteSquasher.squashPushFK(fkToSquash) + : SQLiteSquasher.squashFK(fkToSquash); + return { + type: 'delete_reference', + tableName, + data: foreignKey, + schema, + columnNotNull, + columnDefault, + columnType, + }; + }); + + return statements.filter((it) => it) as JsonDeleteReferenceStatement[]; +}; // alter should create 2 statements. It's important to make only 1 sql per statement(for breakpoints) export const prepareAlterReferencesJson = ( diff --git a/drizzle-kit/src/snapshotsDiffer.ts b/drizzle-kit/src/snapshotsDiffer.ts index 5b6c782c2..af3677abd 100644 --- a/drizzle-kit/src/snapshotsDiffer.ts +++ b/drizzle-kit/src/snapshotsDiffer.ts @@ -5,13 +5,12 @@ import { enum as enumType, literal, never, - number, object, record, string, TypeOf, union, - ZodTypeAny, + ZodTypeAny } from 'zod'; import { applyJsonDiff, diffColumns, diffSchemasOrTables } from './jsonDiffer'; import { fromJson } from './sqlgenerator'; @@ -57,13 +56,15 @@ import { prepareDeleteCompositePrimaryKeyPg, prepareDeleteCompositePrimaryKeySingleStore, prepareDeleteCompositePrimaryKeySqlite, - prepareDeleteSchemasJson as prepareDropSchemasJson, prepareDeleteUniqueConstraintPg as prepareDeleteUniqueConstraint, prepareDropEnumJson, prepareDropIndexesJson, prepareDropReferencesJson, + prepareDeleteSchemasJson as prepareDropSchemasJson, prepareDropSequenceJson, prepareDropTableJson, + prepareLibSQLCreateReferencesJson, + prepareLibSQLDropReferencesJson, prepareMoveEnumJson, prepareMoveSequenceJson, prepareMySqlCreateTableJson, @@ -83,9 +84,10 @@ import { import { Named, NamedWithSchema } from './cli/commands/migrate'; import { mapEntries, mapKeys, mapValues } from './global'; import { MySqlSchema, MySqlSchemaSquashed, MySqlSquasher } from './serializer/mysqlSchema'; -import { PgSchema, PgSchemaSquashed, PgSquasher, sequenceSchema, sequenceSquashed } from './serializer/pgSchema'; +import { PgSchema, PgSchemaSquashed, sequenceSquashed } from './serializer/pgSchema'; import { SingleStoreSchema, SingleStoreSchemaSquashed, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { SQLiteSchema, SQLiteSchemaSquashed, SQLiteSquasher } from './serializer/sqliteSchema'; +import { libSQLCombineStatements, sqliteCombineStatements } from './statementCombiner'; import { copy, prepareMigrationMeta } from './utils'; const makeChanged = (schema: T) => { @@ -2469,7 +2471,8 @@ export const applySqliteSnapshotsDiff = async ( jsonStatements.push(...jsonAlteredUniqueConstraints); - const sqlStatements = fromJson(jsonStatements, 'sqlite'); + const combinedJsonStatements = sqliteCombineStatements(jsonStatements, json2, action); + const sqlStatements = fromJson(combinedJsonStatements, 'sqlite'); const uniqueSqlStatements: string[] = []; sqlStatements.forEach((ss) => { @@ -2485,7 +2488,429 @@ export const applySqliteSnapshotsDiff = async ( const _meta = prepareMigrationMeta([], rTables, rColumns); return { - statements: jsonStatements, + statements: combinedJsonStatements, + sqlStatements: uniqueSqlStatements, + _meta, + }; +}; + +export const applyLibSQLSnapshotsDiff = async ( + json1: SQLiteSchemaSquashed, + json2: SQLiteSchemaSquashed, + tablesResolver: ( + input: ResolverInput, + ) => Promise>, + columnsResolver: ( + input: ColumnsResolverInput, + ) => Promise>, + prevFull: SQLiteSchema, + curFull: SQLiteSchema, + action?: 'push', +): Promise<{ + statements: JsonStatement[]; + sqlStatements: string[]; + _meta: + | { + schemas: {}; + tables: {}; + columns: {}; + } + | undefined; +}> => { + const tablesDiff = diffSchemasOrTables(json1.tables, json2.tables); + const { + created: createdTables, + deleted: deletedTables, + renamed: renamedTables, + } = await tablesResolver({ + created: tablesDiff.added, + deleted: tablesDiff.deleted, + }); + + const tablesPatchedSnap1 = copy(json1); + tablesPatchedSnap1.tables = mapEntries(tablesPatchedSnap1.tables, (_, it) => { + const { name } = nameChangeFor(it, renamedTables); + it.name = name; + return [name, it]; + }); + + const res = diffColumns(tablesPatchedSnap1.tables, json2.tables); + + const columnRenames = [] as { + table: string; + renames: { from: Column; to: Column }[]; + }[]; + + const columnCreates = [] as { + table: string; + columns: Column[]; + }[]; + + const columnDeletes = [] as { + table: string; + columns: Column[]; + }[]; + + for (let entry of Object.values(res)) { + const { renamed, created, deleted } = await columnsResolver({ + tableName: entry.name, + schema: entry.schema, + deleted: entry.columns.deleted, + created: entry.columns.added, + }); + + if (created.length > 0) { + columnCreates.push({ + table: entry.name, + columns: created, + }); + } + + if (deleted.length > 0) { + columnDeletes.push({ + table: entry.name, + columns: deleted, + }); + } + + if (renamed.length > 0) { + columnRenames.push({ + table: entry.name, + renames: renamed, + }); + } + } + + const columnRenamesDict = columnRenames.reduce( + (acc, it) => { + acc[it.table] = it.renames; + return acc; + }, + {} as Record< + string, + { + from: Named; + to: Named; + }[] + >, + ); + + const columnsPatchedSnap1 = copy(tablesPatchedSnap1); + columnsPatchedSnap1.tables = mapEntries( + columnsPatchedSnap1.tables, + (tableKey, tableValue) => { + const patchedColumns = mapKeys( + tableValue.columns, + (columnKey, column) => { + const rens = columnRenamesDict[tableValue.name] || []; + const newName = columnChangeFor(columnKey, rens); + column.name = newName; + return newName; + }, + ); + + tableValue.columns = patchedColumns; + return [tableKey, tableValue]; + }, + ); + + const diffResult = applyJsonDiff(columnsPatchedSnap1, json2); + + const typedResult = diffResultSchemeSQLite.parse(diffResult); + + // Map array of objects to map + const tablesMap: { + [key: string]: (typeof typedResult.alteredTablesWithColumns)[number]; + } = {}; + + typedResult.alteredTablesWithColumns.forEach((obj) => { + tablesMap[obj.name] = obj; + }); + + const jsonCreateTables = createdTables.map((it) => { + return prepareSQLiteCreateTable(it, action); + }); + + const jsonCreateIndexesForCreatedTables = createdTables + .map((it) => { + return prepareCreateIndexesJson( + it.name, + it.schema, + it.indexes, + curFull.internal, + ); + }) + .flat(); + + const jsonDropTables = deletedTables.map((it) => { + return prepareDropTableJson(it); + }); + + const jsonRenameTables = renamedTables.map((it) => { + return prepareRenameTableJson(it.from, it.to); + }); + + const jsonRenameColumnsStatements: JsonRenameColumnStatement[] = columnRenames + .map((it) => prepareRenameColumns(it.table, '', it.renames)) + .flat(); + + const jsonDropColumnsStatemets: JsonDropColumnStatement[] = columnDeletes + .map((it) => _prepareDropColumns(it.table, '', it.columns)) + .flat(); + + const jsonAddColumnsStatemets: JsonSqliteAddColumnStatement[] = columnCreates + .map((it) => { + return _prepareSqliteAddColumns( + it.table, + it.columns, + tablesMap[it.table] && tablesMap[it.table].addedForeignKeys + ? Object.values(tablesMap[it.table].addedForeignKeys) + : [], + ); + }) + .flat(); + + const rColumns = jsonRenameColumnsStatements.map((it) => { + const tableName = it.tableName; + const schema = it.schema; + return { + from: { schema, table: tableName, column: it.oldColumnName }, + to: { schema, table: tableName, column: it.newColumnName }, + }; + }); + + const rTables = renamedTables.map((it) => { + return { from: it.from, to: it.to }; + }); + + const _meta = prepareMigrationMeta([], rTables, rColumns); + + const allAltered = typedResult.alteredTablesWithColumns; + + const jsonAddedCompositePKs: JsonCreateCompositePK[] = []; + const jsonDeletedCompositePKs: JsonDeleteCompositePK[] = []; + const jsonAlteredCompositePKs: JsonAlterCompositePK[] = []; + + const jsonAddedUniqueConstraints: JsonCreateUniqueConstraint[] = []; + const jsonDeletedUniqueConstraints: JsonDeleteUniqueConstraint[] = []; + const jsonAlteredUniqueConstraints: JsonAlterUniqueConstraint[] = []; + + allAltered.forEach((it) => { + // This part is needed to make sure that same columns in a table are not triggered for change + // there is a case where orm and kit are responsible for pk name generation and one of them is not sorting name + // We double-check that pk with same set of columns are both in added and deleted diffs + let addedColumns: string[] = []; + for (const addedPkName of Object.keys(it.addedCompositePKs)) { + const addedPkColumns = it.addedCompositePKs[addedPkName]; + addedColumns = SQLiteSquasher.unsquashPK(addedPkColumns); + } + + let deletedColumns: string[] = []; + for (const deletedPkName of Object.keys(it.deletedCompositePKs)) { + const deletedPkColumns = it.deletedCompositePKs[deletedPkName]; + deletedColumns = SQLiteSquasher.unsquashPK(deletedPkColumns); + } + + // Don't need to sort, but need to add tests for it + // addedColumns.sort(); + // deletedColumns.sort(); + + const doPerformDeleteAndCreate = JSON.stringify(addedColumns) !== JSON.stringify(deletedColumns); + + let addedCompositePKs: JsonCreateCompositePK[] = []; + let deletedCompositePKs: JsonDeleteCompositePK[] = []; + let alteredCompositePKs: JsonAlterCompositePK[] = []; + if (doPerformDeleteAndCreate) { + addedCompositePKs = prepareAddCompositePrimaryKeySqlite( + it.name, + it.addedCompositePKs, + ); + deletedCompositePKs = prepareDeleteCompositePrimaryKeySqlite( + it.name, + it.deletedCompositePKs, + ); + } + alteredCompositePKs = prepareAlterCompositePrimaryKeySqlite( + it.name, + it.alteredCompositePKs, + ); + + // add logic for unique constraints + let addedUniqueConstraints: JsonCreateUniqueConstraint[] = []; + let deletedUniqueConstraints: JsonDeleteUniqueConstraint[] = []; + let alteredUniqueConstraints: JsonAlterUniqueConstraint[] = []; + + addedUniqueConstraints = prepareAddUniqueConstraint( + it.name, + it.schema, + it.addedUniqueConstraints, + ); + + deletedUniqueConstraints = prepareDeleteUniqueConstraint( + it.name, + it.schema, + it.deletedUniqueConstraints, + ); + if (it.alteredUniqueConstraints) { + const added: Record = {}; + const deleted: Record = {}; + for (const k of Object.keys(it.alteredUniqueConstraints)) { + added[k] = it.alteredUniqueConstraints[k].__new; + deleted[k] = it.alteredUniqueConstraints[k].__old; + } + addedUniqueConstraints.push( + ...prepareAddUniqueConstraint(it.name, it.schema, added), + ); + deletedUniqueConstraints.push( + ...prepareDeleteUniqueConstraint(it.name, it.schema, deleted), + ); + } + + jsonAddedCompositePKs.push(...addedCompositePKs); + jsonDeletedCompositePKs.push(...deletedCompositePKs); + jsonAlteredCompositePKs.push(...alteredCompositePKs); + + jsonAddedUniqueConstraints.push(...addedUniqueConstraints); + jsonDeletedUniqueConstraints.push(...deletedUniqueConstraints); + jsonAlteredUniqueConstraints.push(...alteredUniqueConstraints); + }); + + const jsonTableAlternations = allAltered + .map((it) => { + return prepareSqliteAlterColumns(it.name, it.schema, it.altered, json2); + }) + .flat(); + + const jsonCreateIndexesForAllAlteredTables = allAltered + .map((it) => { + return prepareCreateIndexesJson( + it.name, + it.schema, + it.addedIndexes || {}, + curFull.internal, + ); + }) + .flat(); + + const jsonDropIndexesForAllAlteredTables = allAltered + .map((it) => { + return prepareDropIndexesJson( + it.name, + it.schema, + it.deletedIndexes || {}, + ); + }) + .flat(); + + allAltered.forEach((it) => { + const droppedIndexes = Object.keys(it.alteredIndexes).reduce( + (current, item: string) => { + current[item] = it.alteredIndexes[item].__old; + return current; + }, + {} as Record, + ); + const createdIndexes = Object.keys(it.alteredIndexes).reduce( + (current, item: string) => { + current[item] = it.alteredIndexes[item].__new; + return current; + }, + {} as Record, + ); + + jsonCreateIndexesForAllAlteredTables.push( + ...prepareCreateIndexesJson( + it.name, + it.schema, + createdIndexes || {}, + curFull.internal, + ), + ); + jsonDropIndexesForAllAlteredTables.push( + ...prepareDropIndexesJson(it.name, it.schema, droppedIndexes || {}), + ); + }); + + const jsonReferencesForAllAlteredTables: JsonReferenceStatement[] = allAltered + .map((it) => { + const forAdded = prepareLibSQLCreateReferencesJson( + it.name, + it.schema, + it.addedForeignKeys, + json2, + action, + ); + + const forAltered = prepareLibSQLDropReferencesJson( + it.name, + it.schema, + it.deletedForeignKeys, + json2, + _meta, + action, + ); + + const alteredFKs = prepareAlterReferencesJson(it.name, it.schema, it.alteredForeignKeys); + + return [...forAdded, ...forAltered, ...alteredFKs]; + }) + .flat(); + + const jsonCreatedReferencesForAlteredTables = jsonReferencesForAllAlteredTables.filter( + (t) => t.type === 'create_reference', + ); + const jsonDroppedReferencesForAlteredTables = jsonReferencesForAllAlteredTables.filter( + (t) => t.type === 'delete_reference', + ); + + const jsonStatements: JsonStatement[] = []; + jsonStatements.push(...jsonCreateTables); + + jsonStatements.push(...jsonDropTables); + jsonStatements.push(...jsonRenameTables); + jsonStatements.push(...jsonRenameColumnsStatements); + + jsonStatements.push(...jsonDroppedReferencesForAlteredTables); + + // Will need to drop indexes before changing any columns in table + // Then should go column alternations and then index creation + jsonStatements.push(...jsonDropIndexesForAllAlteredTables); + + jsonStatements.push(...jsonDeletedCompositePKs); + jsonStatements.push(...jsonTableAlternations); + jsonStatements.push(...jsonAddedCompositePKs); + jsonStatements.push(...jsonAddColumnsStatemets); + + jsonStatements.push(...jsonCreateIndexesForCreatedTables); + jsonStatements.push(...jsonCreateIndexesForAllAlteredTables); + + jsonStatements.push(...jsonCreatedReferencesForAlteredTables); + + jsonStatements.push(...jsonDropColumnsStatemets); + + jsonStatements.push(...jsonAlteredCompositePKs); + + jsonStatements.push(...jsonAlteredUniqueConstraints); + + const combinedJsonStatements = libSQLCombineStatements(jsonStatements, json2, action); + + const sqlStatements = fromJson( + combinedJsonStatements, + 'sqlite', + action, + 'turso', + json2, + ); + + const uniqueSqlStatements: string[] = []; + sqlStatements.forEach((ss) => { + if (!uniqueSqlStatements.includes(ss)) { + uniqueSqlStatements.push(ss); + } + }); + + return { + statements: combinedJsonStatements, sqlStatements: uniqueSqlStatements, _meta, }; diff --git a/drizzle-kit/src/sqlgenerator.ts b/drizzle-kit/src/sqlgenerator.ts index 07b24b6c9..e3f5c2b20 100644 --- a/drizzle-kit/src/sqlgenerator.ts +++ b/drizzle-kit/src/sqlgenerator.ts @@ -1,4 +1,5 @@ import { BREAKPOINT } from './cli/commands/migrate'; +import { Driver } from './cli/validations/common'; import { JsonAddColumnStatement, JsonAddValueToEnumStatement, @@ -42,6 +43,7 @@ import { JsonDropTableStatement, JsonMoveSequenceStatement, JsonPgCreateIndexStatement, + JsonRecreateTableStatement, JsonRenameColumnStatement, JsonRenameSchema, JsonRenameSequenceStatement, @@ -54,7 +56,7 @@ import { Dialect } from './schemaValidator'; import { MySqlSquasher } from './serializer/mysqlSchema'; import { PgSquasher } from './serializer/pgSchema'; import { SingleStoreSquasher } from './serializer/singlestoreSchema'; -import { SQLiteSquasher } from './serializer/sqliteSchema'; +import { SQLiteSchemaSquashed, SQLiteSquasher } from './serializer/sqliteSchema'; export const pgNativeTypes = new Set([ 'uuid', @@ -127,8 +129,16 @@ const isPgNativeType = (it: string) => { }; abstract class Convertor { - abstract can(statement: JsonStatement, dialect: Dialect): boolean; - abstract convert(statement: JsonStatement): string | string[]; + abstract can( + statement: JsonStatement, + dialect: Dialect, + driver?: Driver, + ): boolean; + abstract convert( + statement: JsonStatement, + json2?: SQLiteSchemaSquashed, + action?: 'push', + ): string | string[]; } class PgCreateTableConvertor extends Convertor { @@ -998,7 +1008,7 @@ class SQLiteAlterTableRenameColumnConvertor extends Convertor { convert(statement: JsonRenameColumnStatement) { const { tableName, oldColumnName, newColumnName } = statement; - return `ALTER TABLE \`${tableName}\` RENAME COLUMN \`${oldColumnName}\` TO \`${newColumnName}\`;`; + return `ALTER TABLE \`${tableName}\` RENAME COLUMN "${oldColumnName}" TO "${newColumnName}";`; } } @@ -1233,10 +1243,11 @@ class PgAlterTableAlterColumnSetTypeConvertor extends Convertor { } class SQLiteAlterTableAlterColumnSetTypeConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { return ( statement.type === 'alter_table_alter_column_set_type' && dialect === 'sqlite' + && !driver ); } @@ -1675,6 +1686,119 @@ class MySqlAlterTableDropPk extends Convertor { } } +type LibSQLModifyColumnStatement = + | JsonAlterColumnTypeStatement + | JsonAlterColumnDropNotNullStatement + | JsonAlterColumnSetNotNullStatement + | JsonAlterColumnSetDefaultStatement + | JsonAlterColumnDropDefaultStatement + | JsonAlterColumnSetGeneratedStatement + | JsonAlterColumnDropGeneratedStatement; + +export class LibSQLModifyColumn extends Convertor { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + (statement.type === 'alter_table_alter_column_set_type' + || statement.type === 'alter_table_alter_column_drop_notnull' + || statement.type === 'alter_table_alter_column_set_notnull' + || statement.type === 'alter_table_alter_column_set_default' + || statement.type === 'alter_table_alter_column_drop_default') + && dialect === 'sqlite' + && driver === 'turso' + ); + } + + convert(statement: LibSQLModifyColumnStatement, json2: SQLiteSchemaSquashed) { + const { tableName, columnName } = statement; + + let columnType = ``; + let columnDefault: any = ''; + let columnNotNull = ''; + + switch (statement.type) { + case 'alter_table_alter_column_set_type': + columnType = ` ${statement.newDataType}`; + + columnDefault = statement.columnDefault + ? ` DEFAULT ${statement.columnDefault}` + : ''; + + columnNotNull = statement.columnNotNull ? ` NOT NULL` : ''; + + break; + case 'alter_table_alter_column_drop_notnull': + columnType = ` ${statement.newDataType}`; + + columnDefault = statement.columnDefault + ? ` DEFAULT ${statement.columnDefault}` + : ''; + + columnNotNull = ''; + break; + case 'alter_table_alter_column_set_notnull': + columnType = ` ${statement.newDataType}`; + + columnDefault = statement.columnDefault + ? ` DEFAULT ${statement.columnDefault}` + : ''; + + columnNotNull = ` NOT NULL`; + break; + case 'alter_table_alter_column_set_default': + columnType = ` ${statement.newDataType}`; + + columnDefault = ` DEFAULT ${statement.newDefaultValue}`; + + columnNotNull = statement.columnNotNull ? ` NOT NULL` : ''; + break; + case 'alter_table_alter_column_drop_default': + columnType = ` ${statement.newDataType}`; + + columnDefault = ''; + + columnNotNull = statement.columnNotNull ? ` NOT NULL` : ''; + break; + case 'alter_table_alter_column_drop_generated': + columnType = ` ${statement.newDataType}`; + + columnDefault = ''; + + columnNotNull = statement.columnNotNull ? ` NOT NULL` : ''; + break; + case 'alter_table_alter_column_set_generated': + return [ + new SQLiteAlterTableDropColumnConvertor().convert({ + type: 'alter_table_drop_column', + tableName: statement.tableName, + columnName: statement.columnName, + schema: '', + }), + new SQLiteAlterTableAddColumnConvertor().convert({ + tableName, + column: { + name: columnName, + type: statement.newDataType, + notNull: statement.columnNotNull, + default: statement.columnDefault, + onUpdate: statement.columnOnUpdate, + autoincrement: statement.columnAutoIncrement, + primaryKey: statement.columnPk, + generated: statement.columnGenerated, + }, + type: 'sqlite_alter_table_add_column', + }), + ]; + } + + // Seems like getting value from simple json2 shanpshot makes dates be dates + columnDefault = columnDefault instanceof Date + ? columnDefault.toISOString() + : columnDefault; + + return `ALTER TABLE \`${tableName}\` ALTER COLUMN "${columnName}" TO "${columnName}"${columnType}${columnNotNull}${columnDefault};`; + } +} + type MySqlModifyColumnStatement = | JsonAlterColumnDropNotNullStatement | JsonAlterColumnSetNotNullStatement @@ -2281,7 +2405,6 @@ class PgAlterTableCreateCompositePrimaryKeyConvertor extends Convertor { }");`; } } - class PgAlterTableDeleteCompositePrimaryKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'delete_composite_pk' && dialect === 'postgresql'; @@ -2542,10 +2665,11 @@ class PgAlterTableAlterColumnSetNotNullConvertor extends Convertor { } class SqliteAlterTableAlterColumnSetNotNullConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { return ( statement.type === 'alter_table_alter_column_set_notnull' && dialect === 'sqlite' + && !driver ); } @@ -2562,10 +2686,11 @@ class SqliteAlterTableAlterColumnSetNotNullConvertor extends Convertor { } class SqliteAlterTableAlterColumnSetAutoincrementConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { return ( statement.type === 'alter_table_alter_column_set_autoincrement' && dialect === 'sqlite' + && !driver ); } @@ -2582,10 +2707,11 @@ class SqliteAlterTableAlterColumnSetAutoincrementConvertor extends Convertor { } class SqliteAlterTableAlterColumnDropAutoincrementConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { return ( statement.type === 'alter_table_alter_column_drop_autoincrement' && dialect === 'sqlite' + && !driver ); } @@ -2621,10 +2747,11 @@ class PgAlterTableAlterColumnDropNotNullConvertor extends Convertor { } class SqliteAlterTableAlterColumnDropNotNullConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { return ( statement.type === 'alter_table_alter_column_drop_notnull' && dialect === 'sqlite' + && !driver ); } @@ -2683,8 +2810,10 @@ class PgCreateForeignKeyConvertor extends Convertor { } class SqliteCreateForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'create_reference' && dialect === 'sqlite'; + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + statement.type === 'create_reference' && dialect === 'sqlite' && !driver + ); } convert(statement: JsonCreateReferenceStatement): string { @@ -2698,6 +2827,50 @@ class SqliteCreateForeignKeyConvertor extends Convertor { } } +class LibSQLCreateForeignKeyConvertor extends Convertor { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + statement.type === 'create_reference' + && dialect === 'sqlite' + && driver === 'turso' + ); + } + + convert( + statement: JsonCreateReferenceStatement, + json2?: SQLiteSchemaSquashed, + action?: 'push', + ): string { + const { columnsFrom, columnsTo, tableFrom, onDelete, onUpdate, tableTo } = action === 'push' + ? SQLiteSquasher.unsquashPushFK(statement.data) + : SQLiteSquasher.unsquashFK(statement.data); + const { columnDefault, columnNotNull, columnType, isMulticolumn } = statement; + + if (isMulticolumn) { + return ( + '/*\n LibSQL does not support "Creating foreign key on multiple columns" out of the box, we do not generate automatic migration for that, so it has to be done manually' + + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' + + '\n https://www.sqlite.org/lang_altertable.html' + + "\n\n Due to that we don't generate migration automatically and it has to be done manually" + + '\n*/' + ); + } + + const onDeleteStatement = onDelete ? ` ON DELETE ${onDelete}` : ''; + const onUpdateStatement = onUpdate ? ` ON UPDATE ${onUpdate}` : ''; + const columnsDefaultValue = columnDefault + ? ` DEFAULT ${columnDefault}` + : ''; + const columnNotNullValue = columnNotNull ? ` NOT NULL` : ''; + const columnTypeValue = columnType ? ` ${columnType}` : ''; + + const columnFrom = columnsFrom[0]; + const columnTo = columnsTo[0]; + + return `ALTER TABLE \`${tableFrom}\` ALTER COLUMN "${columnFrom}" TO "${columnFrom}"${columnTypeValue}${columnNotNullValue}${columnsDefaultValue} REFERENCES ${tableTo}(${columnTo})${onDeleteStatement}${onUpdateStatement};`; + } +} + class MySqlCreateForeignKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'create_reference' && dialect === 'mysql'; @@ -2803,8 +2976,10 @@ class PgDeleteForeignKeyConvertor extends Convertor { } class SqliteDeleteForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'delete_reference' && dialect === 'sqlite'; + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + statement.type === 'delete_reference' && dialect === 'sqlite' && !driver + ); } convert(statement: JsonDeleteReferenceStatement): string { @@ -2818,6 +2993,48 @@ class SqliteDeleteForeignKeyConvertor extends Convertor { } } +class LibSQLDeleteForeignKeyConvertor extends Convertor { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + statement.type === 'delete_reference' + && dialect === 'sqlite' + && driver === 'turso' + ); + } + + convert( + statement: JsonDeleteReferenceStatement, + json2?: SQLiteSchemaSquashed, + action?: 'push', + ): string { + const { columnsFrom, tableFrom } = action === 'push' + ? SQLiteSquasher.unsquashPushFK(statement.data) + : SQLiteSquasher.unsquashFK(statement.data); + + const { columnDefault, columnNotNull, columnType, isMulticolumn } = statement; + + if (isMulticolumn) { + return ( + '/*\n LibSQL does not support "Creating foreign key on multiple columns" out of the box, we do not generate automatic migration for that, so it has to be done manually' + + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' + + '\n https://www.sqlite.org/lang_altertable.html' + + "\n\n Due to that we don't generate migration automatically and it has to be done manually" + + '\n*/' + ); + } + + const columnsDefaultValue = columnDefault + ? ` DEFAULT ${columnDefault}` + : ''; + const columnNotNullValue = columnNotNull ? ` NOT NULL` : ''; + const columnTypeValue = columnType ? ` ${columnType}` : ''; + + const columnFrom = columnsFrom[0]; + + return `ALTER TABLE \`${tableFrom}\` ALTER COLUMN "${columnFrom}" TO "${columnFrom}"${columnTypeValue}${columnNotNullValue}${columnsDefaultValue};`; + } +} + class MySqlDeleteForeignKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'delete_reference' && dialect === 'mysql'; @@ -3092,11 +3309,123 @@ class SingleStoreDropIndexConvertor extends Convertor { } } +class SQLiteRecreateTableConvertor extends Convertor { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + statement.type === 'recreate_table' && dialect === 'sqlite' && !driver + ); + } + + convert(statement: JsonRecreateTableStatement): string | string[] { + const { tableName, columns, compositePKs, referenceData } = statement; + + const columnNames = columns.map((it) => `"${it.name}"`).join(', '); + + const sqlStatements: string[] = []; + + // rename table + sqlStatements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: tableName, + tableNameTo: `__old__generate_${tableName}`, + toSchema: '', + type: 'rename_table', + }), + ); + + // create new table + sqlStatements.push( + new SQLiteCreateTableConvertor().convert({ + type: 'sqlite_create_table', + tableName, + columns, + referenceData, + compositePKs, + }), + ); + + // migrate data + sqlStatements.push( + `INSERT INTO \`${tableName}\`(${columnNames}) SELECT ${columnNames} FROM \`__old__generate_${tableName}\`;`, + ); + + // migrate data + sqlStatements.push( + new SQLiteDropTableConvertor().convert({ + type: 'drop_table', + tableName: `__old__generate_${tableName}`, + schema: '', + }), + ); + + return sqlStatements; + } +} + +class LibSQLRecreateTableConvertor extends Convertor { + can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + return ( + statement.type === 'recreate_table' + && dialect === 'sqlite' + && driver === 'turso' + ); + } + + convert(statement: JsonRecreateTableStatement): string[] { + const { tableName, columns, compositePKs, referenceData } = statement; + + const columnNames = columns.map((it) => `"${it.name}"`).join(', '); + + const sqlStatements: string[] = []; + + // rename table + sqlStatements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: tableName, + tableNameTo: `__old__generate_${tableName}`, + toSchema: '', + type: 'rename_table', + }), + ); + + // create new table + sqlStatements.push( + new SQLiteCreateTableConvertor().convert({ + type: 'sqlite_create_table', + tableName, + columns, + referenceData, + compositePKs, + }), + ); + + // migrate data + sqlStatements.push( + `INSERT INTO \`${tableName}\`(${columnNames}) SELECT ${columnNames} FROM \`__old__generate_${tableName}\`;`, + ); + + // migrate data + sqlStatements.push( + new SQLiteDropTableConvertor().convert({ + type: 'drop_table', + tableName: `__old__generate_${tableName}`, + schema: '', + }), + ); + + return sqlStatements; + } +} + const convertors: Convertor[] = []; convertors.push(new PgCreateTableConvertor()); convertors.push(new MySqlCreateTableConvertor()); convertors.push(new SingleStoreCreateTableConvertor()); convertors.push(new SQLiteCreateTableConvertor()); +convertors.push(new SQLiteRecreateTableConvertor()); +convertors.push(new LibSQLRecreateTableConvertor()); convertors.push(new CreateTypeEnumConvertor()); @@ -3175,6 +3504,7 @@ convertors.push(new SqliteAlterTableAlterColumnAlterGeneratedConvertor()); convertors.push(new SqliteAlterTableAlterColumnSetExpressionConvertor()); convertors.push(new MySqlModifyColumn()); +convertors.push(new LibSQLModifyColumn()); // convertors.push(new MySqlAlterTableAlterColumnSetDefaultConvertor()); // convertors.push(new MySqlAlterTableAlterColumnDropDefaultConvertor()); @@ -3199,7 +3529,9 @@ convertors.push(new PgAlterTableRemoveFromSchemaConvertor()); convertors.push(new SQLiteAlterTableAlterColumnSetTypeConvertor()); convertors.push(new SqliteAlterForeignKeyConvertor()); convertors.push(new SqliteDeleteForeignKeyConvertor()); +convertors.push(new LibSQLDeleteForeignKeyConvertor()); convertors.push(new SqliteCreateForeignKeyConvertor()); +convertors.push(new LibSQLCreateForeignKeyConvertor()); convertors.push(new SQLiteAlterTableAddUniqueConstraintConvertor()); convertors.push(new SQLiteAlterTableDropUniqueConstraintConvertor()); @@ -3236,26 +3568,43 @@ convertors.push(new SingleStoreAlterTableCreateCompositePrimaryKeyConvertor()); convertors.push(new SingleStoreAlterTableAddPk()); convertors.push(new SingleStoreAlterTableAlterCompositePrimaryKeyConvertor()); -export const fromJson = (statements: JsonStatement[], dialect: Dialect) => { +// overloads for turso driver +export function fromJson( + statements: JsonStatement[], + dialect: Exclude, +): string[]; +export function fromJson( + statements: JsonStatement[], + dialect: 'sqlite', + action?: 'push', + driver?: Driver, + json2?: SQLiteSchemaSquashed, +): string[]; + +export function fromJson( + statements: JsonStatement[], + dialect: Dialect, + action?: 'push', + driver?: Driver, + json2?: SQLiteSchemaSquashed, +) { const result = statements .flatMap((statement) => { const filtered = convertors.filter((it) => { - // console.log(statement, dialect) - return it.can(statement, dialect); + return it.can(statement, dialect, driver); }); const convertor = filtered.length === 1 ? filtered[0] : undefined; if (!convertor) { - // console.log("no convertor:", statement.type, dialect); return ''; } - return convertor.convert(statement); + return convertor.convert(statement, json2, action); }) .filter((it) => it !== ''); return result; -}; +} // blog.yo1.dog/updating-enum-values-in-postgresql-the-safe-and-easy-way/ // test case for enum altering diff --git a/drizzle-kit/src/statementCombiner.ts b/drizzle-kit/src/statementCombiner.ts new file mode 100644 index 000000000..21cc86b38 --- /dev/null +++ b/drizzle-kit/src/statementCombiner.ts @@ -0,0 +1,387 @@ +import { + JsonCreateIndexStatement, + JsonRecreateTableStatement, + JsonStatement, + prepareCreateIndexesJson, +} from './jsonStatements'; +import { SQLiteSchemaSquashed, SQLiteSquasher } from './serializer/sqliteSchema'; + +export const prepareLibSQLRecreateTable = ( + table: SQLiteSchemaSquashed['tables'][keyof SQLiteSchemaSquashed['tables']], + action?: 'push', +): (JsonRecreateTableStatement | JsonCreateIndexStatement)[] => { + const { name, columns, uniqueConstraints, indexes } = table; + + const composites: string[][] = Object.values(table.compositePrimaryKeys).map( + (it) => SQLiteSquasher.unsquashPK(it), + ); + + const references: string[] = Object.values(table.foreignKeys); + const fks = references.map((it) => + action === 'push' ? SQLiteSquasher.unsquashPushFK(it) : SQLiteSquasher.unsquashFK(it) + ); + + const statements: (JsonRecreateTableStatement | JsonCreateIndexStatement)[] = [ + { + type: 'recreate_table', + tableName: name, + columns: Object.values(columns), + compositePKs: composites, + referenceData: fks, + uniqueConstraints: Object.values(uniqueConstraints), + }, + ]; + + if (Object.keys(indexes).length) { + statements.push(...prepareCreateIndexesJson(name, '', indexes)); + } + return statements; +}; + +export const prepareSQLiteRecreateTable = ( + table: SQLiteSchemaSquashed['tables'][keyof SQLiteSchemaSquashed['tables']], + action?: 'push', +): JsonStatement[] => { + const { name, columns, uniqueConstraints, indexes } = table; + + const composites: string[][] = Object.values(table.compositePrimaryKeys).map( + (it) => SQLiteSquasher.unsquashPK(it), + ); + + const references: string[] = Object.values(table.foreignKeys); + const fks = references.map((it) => + action === 'push' ? SQLiteSquasher.unsquashPushFK(it) : SQLiteSquasher.unsquashFK(it) + ); + + const statements: JsonStatement[] = [ + { + type: 'recreate_table', + tableName: name, + columns: Object.values(columns), + compositePKs: composites, + referenceData: fks, + uniqueConstraints: Object.values(uniqueConstraints), + }, + ]; + + if (Object.keys(indexes).length) { + statements.push(...prepareCreateIndexesJson(name, '', indexes)); + } + return statements; +}; + +export const libSQLCombineStatements = ( + statements: JsonStatement[], + json2: SQLiteSchemaSquashed, + action?: 'push', +) => { + // const tablesContext: Record = {}; + const newStatements: Record = {}; + for (const statement of statements) { + if ( + statement.type === 'alter_table_alter_column_drop_autoincrement' + || statement.type === 'alter_table_alter_column_set_autoincrement' + || statement.type === 'alter_table_alter_column_drop_pk' + || statement.type === 'alter_table_alter_column_set_pk' + || statement.type === 'create_composite_pk' + || statement.type === 'alter_composite_pk' + || statement.type === 'delete_composite_pk' + ) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + if ( + statement.type === 'alter_table_alter_column_set_type' + || statement.type === 'alter_table_alter_column_drop_notnull' + || statement.type === 'alter_table_alter_column_set_notnull' + || statement.type === 'alter_table_alter_column_set_default' + || statement.type === 'alter_table_alter_column_drop_default' + ) { + const { tableName, columnName, columnPk } = statement; + + const columnIsPartOfUniqueIndex = Object.values( + json2.tables[tableName].indexes, + ).some((it) => { + const unsquashIndex = SQLiteSquasher.unsquashIdx(it); + + return ( + unsquashIndex.columns.includes(columnName) && unsquashIndex.isUnique + ); + }); + + const columnIsPartOfForeignKey = Object.values( + json2.tables[tableName].foreignKeys, + ).some((it) => { + const unsquashFk = action === 'push' ? SQLiteSquasher.unsquashPushFK(it) : SQLiteSquasher.unsquashFK(it); + + return ( + unsquashFk.columnsFrom.includes(columnName) + ); + }); + + const statementsForTable = newStatements[tableName]; + + if ( + !statementsForTable && (columnIsPartOfUniqueIndex || columnIsPartOfForeignKey || columnPk) + ) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + continue; + } + + if ( + statementsForTable && (columnIsPartOfUniqueIndex || columnIsPartOfForeignKey || columnPk) + ) { + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + } + continue; + } + if ( + statementsForTable && !(columnIsPartOfUniqueIndex || columnIsPartOfForeignKey || columnPk) + ) { + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + newStatements[tableName].push(statement); + } + continue; + } + + newStatements[tableName] = [statement]; + + continue; + } + + if (statement.type === 'create_reference' && statement.isMulticolumn) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + if (statement.type === 'delete_reference') { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + if (statement.type === 'sqlite_alter_table_add_column' && statement.column.primaryKey) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + const tableName = statement.type === 'rename_table' + ? statement.tableNameTo + : (statement as { tableName: string }).tableName; + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = [statement]; + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + newStatements[tableName].push(statement); + } + } + + const combinedStatements = Object.values(newStatements).flat(); + const renamedTables = combinedStatements.filter((it) => it.type === 'rename_table'); + const renamedColumns = combinedStatements.filter((it) => it.type === 'alter_table_rename_column'); + + const rest = combinedStatements.filter((it) => it.type !== 'rename_table' && it.type !== 'alter_table_rename_column'); + + return [...renamedTables, ...renamedColumns, ...rest]; +}; + +export const sqliteCombineStatements = ( + statements: JsonStatement[], + json2: SQLiteSchemaSquashed, + action?: 'push', +) => { + // const tablesContext: Record = {}; + const newStatements: Record = {}; + for (const statement of statements) { + if ( + statement.type === 'alter_table_alter_column_set_type' + || statement.type === 'alter_table_alter_column_set_default' + || statement.type === 'alter_table_alter_column_drop_default' + || statement.type === 'alter_table_alter_column_set_notnull' + || statement.type === 'alter_table_alter_column_drop_notnull' + || statement.type === 'alter_table_alter_column_drop_autoincrement' + || statement.type === 'alter_table_alter_column_set_autoincrement' + || statement.type === 'alter_table_alter_column_drop_pk' + || statement.type === 'alter_table_alter_column_set_pk' + || statement.type === 'create_reference' + || statement.type === 'delete_reference' + || statement.type === 'alter_reference' + || statement.type === 'create_composite_pk' + || statement.type === 'alter_composite_pk' + || statement.type === 'delete_composite_pk' + || statement.type === 'create_unique_constraint' + || statement.type === 'delete_unique_constraint' + ) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + if (statement.type === 'sqlite_alter_table_add_column' && statement.column.primaryKey) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + const tableName = statement.type === 'rename_table' + ? statement.tableNameTo + : (statement as { tableName: string }).tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = [statement]; + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + newStatements[tableName].push(statement); + } + } + + const combinedStatements = Object.values(newStatements).flat(); + + const renamedTables = combinedStatements.filter((it) => it.type === 'rename_table'); + const renamedColumns = combinedStatements.filter((it) => it.type === 'alter_table_rename_column'); + + const rest = combinedStatements.filter((it) => it.type !== 'rename_table' && it.type !== 'alter_table_rename_column'); + + return [...renamedTables, ...renamedColumns, ...rest]; +}; diff --git a/drizzle-kit/src/utils.ts b/drizzle-kit/src/utils.ts index 7b363a9d3..37d26f21e 100644 --- a/drizzle-kit/src/utils.ts +++ b/drizzle-kit/src/utils.ts @@ -9,6 +9,7 @@ import { assertUnreachable, snapshotVersion } from './global'; import type { Dialect } from './schemaValidator'; import { backwardCompatibleMysqlSchema } from './serializer/mysqlSchema'; import { backwardCompatiblePgSchema } from './serializer/pgSchema'; +import { backwardCompatibleSingleStoreSchema } from './serializer/singlestoreSchema'; import { backwardCompatibleSqliteSchema } from './serializer/sqliteSchema'; import type { ProxyParams } from './serializer/studio'; @@ -117,6 +118,8 @@ const validatorForDialect = (dialect: Dialect) => { return { validator: backwardCompatibleSqliteSchema, version: 6 }; case 'mysql': return { validator: backwardCompatibleMysqlSchema, version: 5 }; + case 'singlestore': + return { validator: backwardCompatibleSingleStoreSchema, version: 1 }; } }; @@ -341,3 +344,13 @@ export const normalisePGliteUrl = ( export function isPgArrayType(sqlType: string) { return sqlType.match(/.*\[\d*\].*|.*\[\].*/g) !== null; } + +export function findAddedAndRemoved(columnNames1: string[], columnNames2: string[]) { + const set1 = new Set(columnNames1); + const set2 = new Set(columnNames2); + + const addedColumns = columnNames2.filter((it) => !set1.has(it)); + const removedColumns = columnNames1.filter((it) => !set2.has(it)); + + return { addedColumns, removedColumns }; +} diff --git a/drizzle-kit/tests/libsql-statements.test.ts b/drizzle-kit/tests/libsql-statements.test.ts new file mode 100644 index 000000000..adf354458 --- /dev/null +++ b/drizzle-kit/tests/libsql-statements.test.ts @@ -0,0 +1,835 @@ +import { foreignKey, int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import { JsonRecreateTableStatement } from 'src/jsonStatements'; +import { expect, test } from 'vitest'; +import { diffTestSchemasLibSQL } from './schemaDiffer'; + +test('drop autoincrement', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + }), + }; + + const { statements } = await diffTestSchemasLibSQL(schema1, schema2, []); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [{ + autoincrement: false, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); +}); + +test('set autoincrement', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + }), + }; + + const { statements } = await diffTestSchemasLibSQL(schema1, schema2, []); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [{ + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); +}); + +test('set not null', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_set_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text NOT NULL;`, + ); +}); + +test('drop not null', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_drop_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`, + ); +}); + +test('set default. set not null. add column', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull().default('name'), + age: int('age').notNull(), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(3); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_set_default', + tableName: 'users', + columnName: 'name', + newDefaultValue: "'name'", + schema: '', + newDataType: 'text', + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + }); + expect(statements[1]).toStrictEqual({ + type: 'alter_table_alter_column_set_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: "'name'", + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + }); + expect(statements[2]).toStrictEqual({ + type: 'sqlite_alter_table_add_column', + tableName: 'users', + referenceData: undefined, + column: { + name: 'age', + type: 'integer', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text NOT NULL DEFAULT 'name';`, + ); + expect(sqlStatements[1]).toBe( + `ALTER TABLE \`users\` ADD \`age\` integer NOT NULL;`, + ); +}); + +test('drop default. drop not null', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull().default('name'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(2); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_drop_default', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }); + expect(statements[1]).toStrictEqual({ + type: 'alter_table_alter_column_drop_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`, + ); +}); + +test('set data type. set default', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: int('name').default(123), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(2); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_set_type', + tableName: 'users', + columnName: 'name', + newDataType: 'integer', + oldDataType: 'text', + schema: '', + columnDefault: 123, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }); + expect(statements[1]).toStrictEqual({ + type: 'alter_table_alter_column_set_default', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'integer', + newDefaultValue: 123, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" integer DEFAULT 123;`, + ); +}); + +test('add foriegn key', async (t) => { + const schema = { + table: sqliteTable('table', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + tableId: int('table_id'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + tableId: int('table_id').references(() => schema.table.id), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'create_reference', + tableName: 'users', + data: 'users_table_id_table_id_fk;users;table_id;table;id;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "table_id" TO "table_id" integer REFERENCES table(id) ON DELETE no action ON UPDATE no action;`, + ); +}); + +test('drop foriegn key', async (t) => { + const schema = { + table: sqliteTable('table', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + tableId: int('table_id').references(() => schema.table.id, { + onDelete: 'cascade', + }), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + tableId: int('table_id'), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'table_id', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, + ); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`table_id\` integer +);\n`); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`users\`("id", "table_id") SELECT "id", "table_id" FROM \`__old__generate_users\`;`, + ); + expect(sqlStatements[3]).toBe( + `DROP TABLE \`__old__generate_users\`;`, + ); +}); + +test('alter foriegn key', async (t) => { + const tableRef = sqliteTable('table', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }); + const tableRef2 = sqliteTable('table2', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + tableId: int('table_id').references(() => tableRef.id, { + onDelete: 'cascade', + }), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + tableId: int('table_id').references(() => tableRef2.id), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'table_id', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [ + { + columnsFrom: [ + 'table_id', + ], + columnsTo: [ + 'id', + ], + name: 'users_table_id_table2_id_fk', + onDelete: 'no action', + onUpdate: 'no action', + tableFrom: 'users', + tableTo: 'table2', + }, + ], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE `users` RENAME TO `__old__generate_users`;', + ); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`table_id\` integer, +\tFOREIGN KEY (\`table_id\`) REFERENCES \`table2\`(\`id\`) ON UPDATE no action ON DELETE no action +);\n`); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`users\`("id", "table_id") SELECT "id", "table_id" FROM \`__old__generate_users\`;`, + ); + expect(sqlStatements[3]).toBe( + `DROP TABLE \`__old__generate_users\`;`, + ); +}); + +test('add foriegn key for multiple columns', async (t) => { + const tableRef = sqliteTable('table', { + id: int('id').primaryKey({ autoIncrement: true }), + age: int('age'), + age1: int('age_1'), + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + column: int('column'), + column1: int('column_1'), + }), + tableRef, + }; + + const schema2 = { + tableRef, + users: sqliteTable( + 'users', + { + id: int('id').primaryKey({ autoIncrement: true }), + column: int('column'), + column1: int('column_1'), + }, + (table) => ({ + foreignKey: foreignKey({ + columns: [table.column, table.column1], + foreignColumns: [tableRef.age, tableRef.age1], + }), + }), + ), + }; + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'column', + notNull: false, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'column_1', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [ + { + columnsFrom: [ + 'column', + 'column_1', + ], + columnsTo: [ + 'age', + 'age_1', + ], + name: 'users_column_column_1_table_age_age_1_fk', + onDelete: 'no action', + onUpdate: 'no action', + tableFrom: 'users', + tableTo: 'table', + }, + ], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + } as JsonRecreateTableStatement); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`column\` integer, +\t\`column_1\` integer, +\tFOREIGN KEY (\`column\`,\`column_1\`) REFERENCES \`table\`(\`age\`,\`age_1\`) ON UPDATE no action ON DELETE no action +); +`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`__old__generate_users\`;`, + ); + expect(sqlStatements[3]).toBe( + `DROP TABLE \`__old__generate_users\`;`, + ); +}); + +test('drop foriegn key for multiple columns', async (t) => { + const tableRef = sqliteTable('table', { + id: int('id').primaryKey({ autoIncrement: true }), + age: int('age'), + age1: int('age_1'), + }); + + const schema1 = { + users: sqliteTable( + 'users', + { + id: int('id').primaryKey({ autoIncrement: true }), + column: int('column'), + column1: int('column_1'), + }, + (table) => ({ + foreignKey: foreignKey({ + columns: [table.column, table.column1], + foreignColumns: [tableRef.age, tableRef.age1], + }), + }), + ), + tableRef, + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + column: int('column'), + column1: int('column_1'), + }), + tableRef, + }; + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'column', + notNull: false, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'column_1', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`column\` integer, +\t\`column_1\` integer +); +`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`__old__generate_users\`;`, + ); + expect(sqlStatements[3]).toBe( + `DROP TABLE \`__old__generate_users\`;`, + ); +}); + +test('drop foriegn key for multiple columns', async (t) => { + const tableRef = sqliteTable('table', { + id: int('id').primaryKey({ autoIncrement: true }), + age: int('age'), + age1: int('age_1'), + }); + + const schema1 = { + users: sqliteTable( + 'users', + { + id: int('id').primaryKey({ autoIncrement: true }), + column: int('column'), + column1: int('column_1'), + }, + (table) => ({ + foreignKey: foreignKey({ + columns: [table.column, table.column1], + foreignColumns: [tableRef.age, tableRef.age1], + }), + }), + ), + tableRef, + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + column: int('column'), + column1: int('column_1'), + }), + tableRef, + }; + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'column', + notNull: false, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'column_1', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`column\` integer, +\t\`column_1\` integer +); +`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`__old__generate_users\`;`, + ); + expect(sqlStatements[3]).toBe( + `DROP TABLE \`__old__generate_users\`;`, + ); +}); diff --git a/drizzle-kit/tests/push/libsql.test.ts b/drizzle-kit/tests/push/libsql.test.ts new file mode 100644 index 000000000..205f31f0b --- /dev/null +++ b/drizzle-kit/tests/push/libsql.test.ts @@ -0,0 +1,744 @@ +import { createClient } from '@libsql/client'; +import chalk from 'chalk'; +import { + blob, + foreignKey, + getTableConfig, + int, + integer, + numeric, + real, + sqliteTable, + text, + uniqueIndex, +} from 'drizzle-orm/sqlite-core'; +import { diffTestSchemasPushLibSQL } from 'tests/schemaDiffer'; +import { expect, test } from 'vitest'; + +test('nothing changed in schema', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const users = sqliteTable('users', { + id: integer('id').primaryKey().notNull(), + name: text('name').notNull(), + email: text('email'), + textJson: text('text_json', { mode: 'json' }), + blobJon: blob('blob_json', { mode: 'json' }), + blobBigInt: blob('blob_bigint', { mode: 'bigint' }), + numeric: numeric('numeric'), + createdAt: integer('created_at', { mode: 'timestamp' }), + createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }), + real: real('real'), + text: text('text', { length: 255 }), + role: text('role', { enum: ['admin', 'user'] }).default('user'), + isConfirmed: integer('is_confirmed', { + mode: 'boolean', + }), + }); + + const schema1 = { + users, + + customers: sqliteTable('customers', { + id: integer('id').primaryKey(), + address: text('address').notNull(), + isConfirmed: integer('is_confirmed', { mode: 'boolean' }), + registrationDate: integer('registration_date', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()), + userId: integer('user_id') + .references(() => users.id) + .notNull(), + }), + + posts: sqliteTable('posts', { + id: integer('id').primaryKey(), + content: text('content'), + authorId: integer('author_id'), + }), + }; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL(turso, schema1, schema1, [], false); + expect(sqlStatements.length).toBe(0); + expect(statements.length).toBe(0); + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); +}); + +test('added, dropped index', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const users = sqliteTable('users', { + id: integer('id').primaryKey().notNull(), + name: text('name').notNull(), + email: text('email'), + textJson: text('text_json', { mode: 'json' }), + blobJon: blob('blob_json', { mode: 'json' }), + blobBigInt: blob('blob_bigint', { mode: 'bigint' }), + numeric: numeric('numeric'), + createdAt: integer('created_at', { mode: 'timestamp' }), + createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }), + real: real('real'), + text: text('text', { length: 255 }), + role: text('role', { enum: ['admin', 'user'] }).default('user'), + isConfirmed: integer('is_confirmed', { + mode: 'boolean', + }), + }); + + const schema1 = { + users, + customers: sqliteTable( + 'customers', + { + id: integer('id').primaryKey(), + address: text('address').notNull(), + isConfirmed: integer('is_confirmed', { mode: 'boolean' }), + registrationDate: integer('registration_date', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()), + userId: integer('user_id').notNull(), + }, + (table) => ({ + uniqueIndex: uniqueIndex('customers_address_unique').on(table.address), + }), + ), + + posts: sqliteTable('posts', { + id: integer('id').primaryKey(), + content: text('content'), + authorId: integer('author_id'), + }), + }; + + const schema2 = { + users, + customers: sqliteTable( + 'customers', + { + id: integer('id').primaryKey(), + address: text('address').notNull(), + isConfirmed: integer('is_confirmed', { mode: 'boolean' }), + registrationDate: integer('registration_date', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()), + userId: integer('user_id').notNull(), + }, + (table) => ({ + uniqueIndex: uniqueIndex('customers_is_confirmed_unique').on( + table.isConfirmed, + ), + }), + ), + + posts: sqliteTable('posts', { + id: integer('id').primaryKey(), + content: text('content'), + authorId: integer('author_id'), + }), + }; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL(turso, schema1, schema2, [], false); + + expect(statements.length).toBe(2); + expect(statements[0]).toStrictEqual({ + type: 'drop_index', + tableName: 'customers', + data: 'customers_address_unique;address;true;', + schema: '', + }); + expect(statements[1]).toStrictEqual({ + type: 'create_index', + tableName: 'customers', + data: 'customers_is_confirmed_unique;is_confirmed;true;', + schema: '', + internal: { indexes: {} }, + }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe( + `DROP INDEX IF EXISTS \`customers_address_unique\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE UNIQUE INDEX \`customers_is_confirmed_unique\` ON \`customers\` (\`is_confirmed\`);`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('added column not null and without default to table with data', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + age: integer('age').notNull(), + }), + }; + + const table = getTableConfig(schema1.companies); + const seedStatements = [ + `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ("drizzle");`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ("turso");`, + ]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'sqlite_alter_table_add_column', + tableName: 'companies', + column: { + name: 'age', + type: 'integer', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + referenceData: undefined, + }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe(`delete from companies;`); + expect(sqlStatements[1]).toBe( + `ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to add not-null ${ + chalk.underline( + 'age', + ) + } column without default value, which contains 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(1); + expect(tablesToTruncate![0]).toBe('companies'); +}); + +test('added column not null and without default to table without data', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + age: integer('age').notNull(), + }), + }; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL(turso, schema1, schema2, [], false); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'sqlite_alter_table_add_column', + tableName: 'companies', + column: { + name: 'age', + type: 'integer', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + referenceData: undefined, + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`, + ); + + expect(infoToPrint!.length).toBe(0); + expect(columnsToRemove!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('drop autoincrement. drop column with data', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: false }), + }), + }; + + const table = getTableConfig(schema1.companies); + const seedStatements = [ + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, "drizzle");`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, "turso");`, + ]; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'recreate_table', + tableName: 'companies', + columns: [ + { + name: 'id', + type: 'integer', + autoincrement: false, + notNull: true, + primaryKey: true, + generated: undefined, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`companies\` ( +\t\`id\` integer PRIMARY KEY NOT NULL +);\n`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + ); + + expect(columnsToRemove!.length).toBe(1); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to delete ${ + chalk.underline( + 'name', + ) + } column in companies table with 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('change autoincrement. table is part of foreign key', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const companies1 = sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: true }), + }); + const users1 = sqliteTable('users', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').unique(), + companyId: text('company_id').references(() => companies1.id), + }); + const schema1 = { + companies: companies1, + users: users1, + }; + + const companies2 = sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: false }), + }); + const users2 = sqliteTable('users', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').unique(), + companyId: text('company_id').references(() => companies1.id), + }); + const schema2 = { + companies: companies2, + users: users2, + }; + + const { name: usersTableName } = getTableConfig(users1); + const { name: companiesTableName } = getTableConfig(companies1); + const seedStatements = [ + `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ("drizzle");`, + `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ("turso");`, + `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ("1");`, + `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ("2");`, + ]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'recreate_table', + tableName: 'companies', + columns: [ + { + name: 'id', + type: 'integer', + autoincrement: false, + notNull: true, + primaryKey: true, + generated: undefined, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(9); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`companies\` ( +\t\`id\` integer PRIMARY KEY NOT NULL +); +`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + ); + expect(sqlStatements[3]).toBe(`DROP TABLE \`__old_push_companies\`;`); + expect(sqlStatements[4]).toBe( + `ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`, + ); + expect(sqlStatements[5]).toBe( + `CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`name\` text, +\t\`company_id\` text, +\tFOREIGN KEY (\`company_id\`) REFERENCES \`companies\`(\`id\`) ON UPDATE no action ON DELETE no action +); +`, + ); + expect(sqlStatements[6]).toBe( + `INSERT INTO \`users\`("id", "name", "company_id") SELECT ("id", "name", "company_id") FROM \`__old_push_users\`;`, + ); + expect(sqlStatements[7]).toBe(`DROP TABLE \`__old_push_users\`;`); + expect(sqlStatements[8]).toBe( + `CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('create table with custom name references', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }); + + const schema1 = { + users, + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }, + (t) => ({ + fk: foreignKey({ + columns: [t.id], + foreignColumns: [users.id], + name: 'custom_name_fk', + }), + }), + ), + }; + + const schema2 = { + users, + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }, + (t) => ({ + fk: foreignKey({ + columns: [t.id], + foreignColumns: [users.id], + name: 'custom_name_fk', + }), + }), + ), + }; + + const { sqlStatements } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + ); + + expect(sqlStatements!.length).toBe(0); +}); + +test('drop not null, add not null', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }), + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + userId: int('user_id'), + }, + ), + }; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + columnAutoIncrement: false, + columnDefault: undefined, + columnName: 'name', + columnNotNull: false, + columnOnUpdate: undefined, + columnPk: false, + newDataType: 'text', + schema: '', + tableName: 'users', + type: 'alter_table_alter_column_drop_notnull', + }); + expect(statements![1]).toStrictEqual({ + columnAutoIncrement: false, + columnDefault: undefined, + columnName: 'name', + columnNotNull: true, + columnOnUpdate: undefined, + columnPk: false, + newDataType: 'text', + schema: '', + tableName: 'posts', + type: 'alter_table_alter_column_set_notnull', + }); + expect(sqlStatements!.length).toBe(2); + expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`); + expect(sqlStatements![1]).toBe(`ALTER TABLE \`posts\` ALTER COLUMN "name" TO "name" text NOT NULL;`); + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('drop table with data', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }), + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const schema2 = { + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const seedStatements = [ + `INSERT INTO \`users\` ("name") VALUES ("drizzle")`, + ]; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + schema: undefined, + tableName: 'users', + type: 'drop_table', + }); + + expect(sqlStatements!.length).toBe(1); + expect(sqlStatements![0]).toBe(`DROP TABLE \`users\`;`); + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe(`· You're about to delete ${chalk.underline('users')} table with 1 items`); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(1); + expect(tablesToRemove![0]).toBe('users'); + expect(tablesToTruncate!.length).toBe(0); +}); diff --git a/drizzle-kit/tests/push/sqlite.test.ts b/drizzle-kit/tests/push/sqlite.test.ts index cf468d3ec..d0c3cb05e 100644 --- a/drizzle-kit/tests/push/sqlite.test.ts +++ b/drizzle-kit/tests/push/sqlite.test.ts @@ -1,384 +1,529 @@ import Database from 'better-sqlite3'; -import { SQL, sql } from 'drizzle-orm'; -import { blob, foreignKey, int, integer, numeric, real, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import chalk from 'chalk'; +import { + blob, + foreignKey, + getTableConfig, + int, + integer, + numeric, + real, + sqliteTable, + text, + uniqueIndex, +} from 'drizzle-orm/sqlite-core'; import { diffTestSchemasPushSqlite } from 'tests/schemaDiffer'; import { expect, test } from 'vitest'; -import { DialectSuite, run } from './common'; - -const sqliteSuite: DialectSuite = { - addBasicIndexes: function(context?: any): Promise { - return {} as any; - }, - changeIndexFields: function(context?: any): Promise { - return {} as any; - }, - dropIndex: function(context?: any): Promise { - return {} as any; - }, - - async allTypes() { - const sqlite = new Database(':memory:'); - - const Users = sqliteTable('users', { - id: integer('id').primaryKey().notNull(), - name: text('name').notNull(), - email: text('email'), - textJson: text('text_json', { mode: 'json' }), - blobJon: blob('blob_json', { mode: 'json' }), - blobBigInt: blob('blob_bigint', { mode: 'bigint' }), - numeric: numeric('numeric'), - createdAt: integer('created_at', { mode: 'timestamp' }), - createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }), - real: real('real'), - text: text('text', { length: 255 }), - role: text('role', { enum: ['admin', 'user'] }).default('user'), - isConfirmed: integer('is_confirmed', { - mode: 'boolean', - }), - }); - const schema1 = { - Users, +test('nothing changed in schema', async (t) => { + const client = new Database(':memory:'); + + const users = sqliteTable('users', { + id: integer('id').primaryKey().notNull(), + name: text('name').notNull(), + email: text('email'), + textJson: text('text_json', { mode: 'json' }), + blobJon: blob('blob_json', { mode: 'json' }), + blobBigInt: blob('blob_bigint', { mode: 'bigint' }), + numeric: numeric('numeric'), + createdAt: integer('created_at', { mode: 'timestamp' }), + createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }), + real: real('real'), + text: text('text', { length: 255 }), + role: text('role', { enum: ['admin', 'user'] }).default('user'), + isConfirmed: integer('is_confirmed', { + mode: 'boolean', + }), + }); + + const schema1 = { + users, - Customers: sqliteTable('customers', { + customers: sqliteTable('customers', { + id: integer('id').primaryKey(), + address: text('address').notNull(), + isConfirmed: integer('is_confirmed', { mode: 'boolean' }), + registrationDate: integer('registration_date', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()), + userId: integer('user_id') + .references(() => users.id) + .notNull(), + }), + + posts: sqliteTable('posts', { + id: integer('id').primaryKey(), + content: text('content'), + authorId: integer('author_id'), + }), + }; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite(client, schema1, schema1, [], false); + expect(sqlStatements.length).toBe(0); + expect(statements.length).toBe(0); + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); +}); + +test('dropped, added unique index', async (t) => { + const client = new Database(':memory:'); + + const users = sqliteTable('users', { + id: integer('id').primaryKey().notNull(), + name: text('name').notNull(), + email: text('email'), + textJson: text('text_json', { mode: 'json' }), + blobJon: blob('blob_json', { mode: 'json' }), + blobBigInt: blob('blob_bigint', { mode: 'bigint' }), + numeric: numeric('numeric'), + createdAt: integer('created_at', { mode: 'timestamp' }), + createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }), + real: real('real'), + text: text('text', { length: 255 }), + role: text('role', { enum: ['admin', 'user'] }).default('user'), + isConfirmed: integer('is_confirmed', { + mode: 'boolean', + }), + }); + + const schema1 = { + users, + + customers: sqliteTable( + 'customers', + { id: integer('id').primaryKey(), - address: text('address').notNull(), + address: text('address').notNull().unique(), isConfirmed: integer('is_confirmed', { mode: 'boolean' }), registrationDate: integer('registration_date', { mode: 'timestamp_ms' }) .notNull() .$defaultFn(() => new Date()), - userId: integer('user_id') - .references(() => Users.id) - .notNull(), + userId: integer('user_id').notNull(), + }, + (table) => ({ + uniqueIndex: uniqueIndex('customers_address_unique').on(table.address), }), + ), - Posts: sqliteTable('posts', { - id: integer('id').primaryKey(), - content: text('content'), - authorId: integer('author_id'), - }), - }; - - const { statements } = await diffTestSchemasPushSqlite( - sqlite, - schema1, - schema1, - [], - false, - ); - expect(statements.length).toBe(0); - }, - indexesToBeNotTriggered: function(context?: any): Promise { - return {} as any; - }, - indexesTestCase1: function(context?: any): Promise { - return {} as any; - }, - async case1(): Promise { - const sqlite = new Database(':memory:'); - - const schema1 = { - users: sqliteTable('users', { - id: text('id').notNull().primaryKey(), - firstName: text('first_name').notNull(), - lastName: text('last_name').notNull(), - username: text('username').notNull().unique(), - email: text('email').notNull().unique(), - password: text('password').notNull(), - avatarUrl: text('avatar_url').notNull(), - postsCount: integer('posts_count').notNull().default(0), - followersCount: integer('followers_count').notNull().default(0), - followingsCount: integer('followings_count').notNull().default(0), - createdAt: integer('created_at').notNull(), - }), - }; - - const schema2 = { - users: sqliteTable('users', { - id: text('id').notNull().primaryKey(), - firstName: text('first_name').notNull(), - lastName: text('last_name').notNull(), - username: text('username').notNull().unique(), - email: text('email').notNull().unique(), - password: text('password').notNull(), - avatarUrl: text('avatar_url').notNull(), - followersCount: integer('followers_count').notNull().default(0), - followingsCount: integer('followings_count').notNull().default(0), - createdAt: integer('created_at').notNull(), - }), - }; - - const { statements } = await diffTestSchemasPushSqlite( - sqlite, - schema1, - schema2, - [], - false, - ); - expect(statements.length).toBe(1); - expect(statements[0]).toStrictEqual({ - type: 'alter_table_drop_column', - tableName: 'users', - columnName: 'posts_count', - schema: '', - }); - }, - addNotNull: function(context?: any): Promise { - return {} as any; - }, - addNotNullWithDataNoRollback: function(context?: any): Promise { - return {} as any; - }, - addBasicSequences: function(context?: any): Promise { - return {} as any; - }, - // --- - addGeneratedColumn: async function(context?: any): Promise { - const sqlite = new Database(':memory:'); - - const from = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - }), - }; - const to = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name').generatedAlwaysAs( - (): SQL => sql`${to.users.name} || 'hello'`, - { mode: 'stored' }, - ), - }), - }; - - const { statements, sqlStatements } = await diffTestSchemasPushSqlite( - sqlite, - from, - to, - [], - ); - - expect(statements).toStrictEqual([]); - expect(sqlStatements).toStrictEqual([]); - }, - addGeneratedToColumn: async function(context?: any): Promise { - const sqlite = new Database(':memory:'); - - const from = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name').notNull(), - generatedName1: text('gen_name1'), - }), - }; - const to = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name') - .notNull() - .generatedAlwaysAs((): SQL => sql`${to.users.name} || 'hello'`, { - mode: 'stored', - }), - generatedName1: text('gen_name1').generatedAlwaysAs( - (): SQL => sql`${to.users.name} || 'hello'`, - { mode: 'virtual' }, - ), - }), - }; + posts: sqliteTable('posts', { + id: integer('id').primaryKey(), + content: text('content'), + authorId: integer('author_id'), + }), + }; - const { statements, sqlStatements } = await diffTestSchemasPushSqlite( - sqlite, - from, - to, - [], - ); + const schema2 = { + users, - expect(statements).toStrictEqual([ + customers: sqliteTable( + 'customers', { - columnAutoIncrement: false, - columnDefault: undefined, - columnGenerated: { - as: '("name" || \'hello\')', - type: 'virtual', - }, - columnName: 'gen_name1', - columnNotNull: false, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - schema: '', - tableName: 'users', - type: 'alter_table_alter_column_set_generated', + id: integer('id').primaryKey(), + address: text('address').notNull(), + isConfirmed: integer('is_confirmed', { mode: 'boolean' }), + registrationDate: integer('registration_date', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()), + userId: integer('user_id').notNull(), }, - ]); - expect(sqlStatements).toStrictEqual([ - 'ALTER TABLE `users` DROP COLUMN `gen_name1`;', - 'ALTER TABLE `users` ADD `gen_name1` text GENERATED ALWAYS AS ("name" || \'hello\') VIRTUAL;', - ]); - - for (const st of sqlStatements) { - sqlite.exec(st); - } - }, - dropGeneratedConstraint: async function(context?: any): Promise { - const sqlite = new Database(':memory:'); - - const from = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name').generatedAlwaysAs( - (): SQL => sql`${to.users.name} || 'hello'`, - { mode: 'stored' }, + (table) => ({ + uniqueIndex: uniqueIndex('customers_is_confirmed_unique').on( + table.isConfirmed, ), - generatedName1: text('gen_name1').generatedAlwaysAs( - (): SQL => sql`${to.users.name} || 'hello'`, - { mode: 'virtual' }, - ), - }), - }; - const to = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name'), - generatedName1: text('gen_name1'), }), - }; + ), - const { statements, sqlStatements } = await diffTestSchemasPushSqlite( - sqlite, - from, - to, - [], - ); + posts: sqliteTable('posts', { + id: integer('id').primaryKey(), + content: text('content'), + authorId: integer('author_id'), + }), + }; - expect(statements).toStrictEqual([ - { - columnAutoIncrement: false, - columnDefault: undefined, - columnGenerated: undefined, - columnName: 'gen_name', - columnNotNull: false, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - schema: '', - tableName: 'users', - type: 'alter_table_alter_column_drop_generated', - }, + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite(client, schema1, schema2, [], false); + expect(statements.length).toBe(2); + expect(statements[0]).toStrictEqual({ + type: 'drop_index', + tableName: 'customers', + data: 'customers_address_unique;address;true;', + schema: '', + }); + expect(statements[1]).toStrictEqual({ + type: 'create_index', + tableName: 'customers', + data: 'customers_is_confirmed_unique;is_confirmed;true;', + schema: '', + internal: { + indexes: {}, + }, + }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe( + `DROP INDEX IF EXISTS \`customers_address_unique\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE UNIQUE INDEX \`customers_is_confirmed_unique\` ON \`customers\` (\`is_confirmed\`);`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('added column not null and without default to table with data', async (t) => { + const client = new Database(':memory:'); + + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + age: integer('age').notNull(), + }), + }; + + const table = getTableConfig(schema1.companies); + const seedStatements = [ + `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('drizzle');`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('turso');`, + ]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'sqlite_alter_table_add_column', + tableName: 'companies', + column: { + name: 'age', + type: 'integer', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + referenceData: undefined, + }); + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe(`delete from companies;`); + expect(sqlStatements[1]).toBe( + `ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to add not-null ${ + chalk.underline( + 'age', + ) + } column without default value, which contains 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(1); + expect(tablesToTruncate![0]).toBe('companies'); +}); + +test('added column not null and without default to table without data', async (t) => { + const turso = new Database(':memory:'); + + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey(), + name: text('name').notNull(), + age: integer('age').notNull(), + }), + }; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite(turso, schema1, schema2, [], false); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'sqlite_alter_table_add_column', + tableName: 'companies', + column: { + name: 'age', + type: 'integer', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + referenceData: undefined, + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`, + ); + + expect(infoToPrint!.length).toBe(0); + expect(columnsToRemove!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('drop autoincrement. drop column with data', async (t) => { + const turso = new Database(':memory:'); + + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: false }), + }), + }; + + const table = getTableConfig(schema1.companies); + const seedStatements = [ + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, 'drizzle');`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, 'turso');`, + ]; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + turso, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'recreate_table', + tableName: 'companies', + columns: [ { - columnAutoIncrement: false, - columnDefault: undefined, - columnGenerated: undefined, - columnName: 'gen_name1', - columnNotNull: false, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - schema: '', - tableName: 'users', - type: 'alter_table_alter_column_drop_generated', + name: 'id', + type: 'integer', + autoincrement: false, + notNull: true, + primaryKey: true, + generated: undefined, }, - ]); - expect(sqlStatements).toStrictEqual([ - 'ALTER TABLE `users` DROP COLUMN `gen_name`;', - 'ALTER TABLE `users` ADD `gen_name` text;', - 'ALTER TABLE `users` DROP COLUMN `gen_name1`;', - 'ALTER TABLE `users` ADD `gen_name1` text;', - ]); - - for (const st of sqlStatements) { - sqlite.exec(st); - } - }, - alterGeneratedConstraint: async function(context?: any): Promise { - const sqlite = new Database(':memory:'); - - const from = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name').generatedAlwaysAs( - (): SQL => sql`${to.users.name} || 'hello'`, - { mode: 'stored' }, - ), - generatedName1: text('gen_name1').generatedAlwaysAs( - (): SQL => sql`${to.users.name} || 'hello'`, - { mode: 'virtual' }, - ), - }), - }; - const to = { - users: sqliteTable('users', { - id: int('id'), - id2: int('id2'), - name: text('name'), - generatedName: text('gen_name').generatedAlwaysAs( - (): SQL => sql`${to.users.name}`, - { mode: 'stored' }, - ), - generatedName1: text('gen_name1').generatedAlwaysAs( - (): SQL => sql`${to.users.name}`, - { mode: 'virtual' }, - ), - }), - }; + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`companies\` ( +\t\`id\` integer PRIMARY KEY NOT NULL +);\n`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + ); + + expect(columnsToRemove!.length).toBe(1); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to delete ${ + chalk.underline( + 'name', + ) + } column in companies table with 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); - const { statements, sqlStatements } = await diffTestSchemasPushSqlite( - sqlite, - from, - to, - [], - ); +test('change autoincrement. table is part of foreign key', async (t) => { + const client = new Database(':memory:'); - expect(statements).toStrictEqual([ + const companies1 = sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: true }), + }); + const users1 = sqliteTable('users', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').unique(), + companyId: text('company_id').references(() => companies1.id), + }); + const schema1 = { + companies: companies1, + users: users1, + }; + + const companies2 = sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: false }), + }); + const users2 = sqliteTable('users', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name').unique(), + companyId: text('company_id').references(() => companies1.id), + }); + const schema2 = { + companies: companies2, + users: users2, + }; + + const { name: usersTableName } = getTableConfig(users1); + const { name: companiesTableName } = getTableConfig(companies1); + const seedStatements = [ + `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('drizzle');`, + `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('turso');`, + `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ('1');`, + `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ('2');`, + ]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'recreate_table', + tableName: 'companies', + columns: [ { - columnAutoIncrement: false, - columnDefault: undefined, - columnGenerated: { - as: '("name")', - type: 'virtual', - }, - columnName: 'gen_name1', - columnNotNull: false, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - schema: '', - tableName: 'users', - type: 'alter_table_alter_column_alter_generated', + name: 'id', + type: 'integer', + autoincrement: false, + notNull: true, + primaryKey: true, + generated: undefined, }, - ]); - expect(sqlStatements).toStrictEqual([ - 'ALTER TABLE `users` DROP COLUMN `gen_name1`;', - 'ALTER TABLE `users` ADD `gen_name1` text GENERATED ALWAYS AS ("name") VIRTUAL;', - ]); - - for (const st of sqlStatements) { - sqlite.exec(st); - } - }, - createTableWithGeneratedConstraint: function(context?: any): Promise { - return {} as any; - }, -}; - -run(sqliteSuite); + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(9); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`companies\` ( +\t\`id\` integer PRIMARY KEY NOT NULL +); +`, + ); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + ); + expect(sqlStatements[3]).toBe(`DROP TABLE \`__old_push_companies\`;`); + expect(sqlStatements[4]).toBe( + `ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`, + ); + expect(sqlStatements[5]).toBe( + `CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`name\` text, +\t\`company_id\` text, +\tFOREIGN KEY (\`company_id\`) REFERENCES \`companies\`(\`id\`) ON UPDATE no action ON DELETE no action +); +`, + ); + expect(sqlStatements[6]).toBe( + `INSERT INTO \`users\`("id", "name", "company_id") SELECT ("id", "name", "company_id") FROM \`__old_push_users\`;`, + ); + expect(sqlStatements[7]).toBe(`DROP TABLE \`__old_push_users\`;`); + expect(sqlStatements[8]).toBe( + `CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); test('create table with custom name references', async (t) => { - const sqlite = new Database(':memory:'); + const client = new Database(':memory:'); const users = sqliteTable('users', { id: int('id').primaryKey({ autoIncrement: true }), @@ -424,7 +569,7 @@ test('create table with custom name references', async (t) => { }; const { sqlStatements } = await diffTestSchemasPushSqlite( - sqlite, + client, schema1, schema2, [], @@ -432,3 +577,383 @@ test('create table with custom name references', async (t) => { expect(sqlStatements!.length).toBe(0); }); + +test('drop not null, add not null', async (t) => { + const client = new Database(':memory:'); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }), + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + posts: sqliteTable( + 'posts', + { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + userId: int('user_id'), + }, + ), + }; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + [], + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + primaryKey: false, + type: 'text', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + expect(statements![1]).toStrictEqual({ + columns: [ + { + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: true, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + generated: undefined, + name: 'user_id', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'posts', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(8); + expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`name\` text +);\n`); + expect(sqlStatements![2]).toBe( + `INSERT INTO \`users\`("id", "name") SELECT ("id", "name") FROM \`__old_push_users\`;`, + ); + expect(sqlStatements![3]).toBe(`DROP TABLE \`__old_push_users\`;`); + + expect(sqlStatements![4]).toBe(`ALTER TABLE \`posts\` RENAME TO \`__old_push_posts\`;`); + expect(sqlStatements![5]).toBe(`CREATE TABLE \`posts\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`name\` text NOT NULL, +\t\`user_id\` integer +);\n`); + expect(sqlStatements![6]).toBe( + `INSERT INTO \`posts\`("id", "name", "user_id") SELECT ("id", "name", "user_id") FROM \`__old_push_posts\`;`, + ); + expect(sqlStatements![7]).toBe(`DROP TABLE \`__old_push_posts\`;`); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('rename table and change data type', async (t) => { + const client = new Database(':memory:'); + + const schema1 = { + users: sqliteTable('old_users', { + id: int('id').primaryKey({ autoIncrement: true }), + age: text('age'), + }), + }; + + const schema2 = { + users: sqliteTable('new_users', { + id: int('id').primaryKey({ autoIncrement: true }), + age: integer('age'), + }), + }; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + ['public.old_users->public.new_users'], + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + fromSchema: undefined, + tableNameFrom: 'old_users', + tableNameTo: 'new_users', + toSchema: undefined, + type: 'rename_table', + }); + expect(statements![1]).toStrictEqual({ + columns: [ + { + autoincrement: true, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'new_users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(5); + expect(sqlStatements![0]).toBe(`ALTER TABLE \`old_users\` RENAME TO \`new_users\`;`); + expect(sqlStatements![1]).toBe(`ALTER TABLE \`new_users\` RENAME TO \`__old_push_new_users\`;`); + expect(sqlStatements![2]).toBe(`CREATE TABLE \`new_users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`age\` integer +);\n`); + expect(sqlStatements![3]).toBe( + `INSERT INTO \`new_users\`("id", "age") SELECT ("id", "age") FROM \`__old_push_new_users\`;`, + ); + expect(sqlStatements![4]).toBe(`DROP TABLE \`__old_push_new_users\`;`); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('rename table and change data type', async (t) => { + const client = new Database(':memory:'); + + const schema1 = { + users: sqliteTable('old_users', { + id: int('id').primaryKey({ autoIncrement: true }), + age: text('age'), + }), + }; + + const schema2 = { + users: sqliteTable('new_users', { + id: int('id').primaryKey({ autoIncrement: true }), + age: integer('age'), + }), + }; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + ['public.old_users->public.new_users'], + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + fromSchema: undefined, + tableNameFrom: 'old_users', + tableNameTo: 'new_users', + toSchema: undefined, + type: 'rename_table', + }); + expect(statements![1]).toStrictEqual({ + columns: [ + { + autoincrement: true, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'new_users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(5); + expect(sqlStatements![0]).toBe(`ALTER TABLE \`old_users\` RENAME TO \`new_users\`;`); + expect(sqlStatements![1]).toBe(`ALTER TABLE \`new_users\` RENAME TO \`__old_push_new_users\`;`); + expect(sqlStatements![2]).toBe(`CREATE TABLE \`new_users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`age\` integer +);\n`); + expect(sqlStatements![3]).toBe( + `INSERT INTO \`new_users\`("id", "age") SELECT ("id", "age") FROM \`__old_push_new_users\`;`, + ); + expect(sqlStatements![4]).toBe(`DROP TABLE \`__old_push_new_users\`;`); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('rename column and change data type', async (t) => { + const client = new Database(':memory:'); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + age: integer('age'), + }), + }; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + ['public.users.name->public.users.age'], + ); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: true, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(4); + expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`users\` ( +\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, +\t\`age\` integer +);\n`); + expect(sqlStatements![2]).toBe( + `INSERT INTO \`users\`("id", "age") SELECT ("id", "age") FROM \`__old_push_users\`;`, + ); + expect(sqlStatements![3]).toBe(`DROP TABLE \`__old_push_users\`;`); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); diff --git a/drizzle-kit/tests/schemaDiffer.ts b/drizzle-kit/tests/schemaDiffer.ts index 58f6a8f64..07da0ed05 100644 --- a/drizzle-kit/tests/schemaDiffer.ts +++ b/drizzle-kit/tests/schemaDiffer.ts @@ -1,4 +1,5 @@ import { PGlite } from '@electric-sql/pglite'; +import { Client } from '@libsql/client/.'; import { Database } from 'better-sqlite3'; import { is } from 'drizzle-orm'; import { MySqlSchema, MySqlTable } from 'drizzle-orm/mysql-core'; @@ -8,6 +9,7 @@ import { SingleStoreTable } from 'drizzle-orm/singlestore-core'; import { SQLiteTable } from 'drizzle-orm/sqlite-core'; import * as fs from 'fs'; import { Connection } from 'mysql2/promise'; +import { libSqlLogSuggestionsAndReturn } from 'src/cli/commands/libSqlPushUtils'; import { columnsResolver, enumsResolver, @@ -37,6 +39,7 @@ import { prepareFromSqliteImports } from 'src/serializer/sqliteImports'; import { sqliteSchema, squashSqliteScheme } from 'src/serializer/sqliteSchema'; import { fromDatabase as fromSqliteDatabase, generateSqliteSnapshot } from 'src/serializer/sqliteSerializer'; import { + applyLibSQLSnapshotsDiff, applyMysqlSnapshotsDiff, applyPgSnapshotsDiff, applySingleStoreSnapshotsDiff, @@ -959,11 +962,18 @@ export const diffTestSchemasPushSqlite = async ( right: SqliteSchema, renamesArr: string[], cli: boolean = false, + seedStatements: string[] = [], ) => { const { sqlStatements } = await applySqliteDiffs(left, 'push'); + for (const st of sqlStatements) { client.exec(st); } + + for (const st of seedStatements) { + client.exec(st); + } + // do introspect into PgSchemaInternal const introspectedSchema = await fromSqliteDatabase( { @@ -977,9 +987,9 @@ export const diffTestSchemasPushSqlite = async ( undefined, ); - const leftTables = Object.values(right).filter((it) => is(it, SQLiteTable)) as SQLiteTable[]; + const rightTables = Object.values(right).filter((it) => is(it, SQLiteTable)) as SQLiteTable[]; - const serialized2 = generateSqliteSnapshot(leftTables); + const serialized2 = generateSqliteSnapshot(rightTables); const { version: v1, dialect: d1, ...rest1 } = introspectedSchema; const { version: v2, dialect: d2, ...rest2 } = serialized2; @@ -1016,7 +1026,15 @@ export const diffTestSchemasPushSqlite = async ( 'push', ); - const { statementsToExecute } = await logSuggestionsAndReturn( + const { + statementsToExecute, + columnsToRemove, + infoToPrint, + schemasToRemove, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await logSuggestionsAndReturn( { query: async (sql: string, params: any[] = []) => { return client.prepare(sql).bind(params).all() as T[]; @@ -1031,7 +1049,16 @@ export const diffTestSchemasPushSqlite = async ( _meta!, ); - return { sqlStatements: statementsToExecute, statements }; + return { + sqlStatements: statementsToExecute, + statements, + columnsToRemove, + infoToPrint, + schemasToRemove, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + }; } else { const { sqlStatements, statements } = await applySqliteSnapshotsDiff( sn1, @@ -1046,6 +1073,136 @@ export const diffTestSchemasPushSqlite = async ( } }; +export async function diffTestSchemasPushLibSQL( + client: Client, + left: SqliteSchema, + right: SqliteSchema, + renamesArr: string[], + cli: boolean = false, + seedStatements: string[] = [], +) { + const { sqlStatements } = await applyLibSQLDiffs(left, 'push'); + + for (const st of sqlStatements) { + await client.execute(st); + } + + for (const st of seedStatements) { + await client.execute(st); + } + + const introspectedSchema = await fromSqliteDatabase( + { + query: async (sql: string, params?: any[]) => { + const res = await client.execute({ sql, args: params || [] }); + return res.rows as T[]; + }, + run: async (query: string) => { + await client.execute(query); + }, + batch: async ( + queries: { query: string; values?: any[] | undefined }[], + ) => { + await client.batch( + queries.map((it) => ({ sql: it.query, args: it.values ?? [] })), + ); + }, + }, + undefined, + ); + + const leftTables = Object.values(right).filter((it) => is(it, SQLiteTable)) as SQLiteTable[]; + + const serialized2 = generateSqliteSnapshot(leftTables); + + const { version: v1, dialect: d1, ...rest1 } = introspectedSchema; + const { version: v2, dialect: d2, ...rest2 } = serialized2; + + const sch1 = { + version: '6', + dialect: 'sqlite', + id: '0', + prevId: '0', + ...rest1, + } as const; + + const sch2 = { + version: '6', + dialect: 'sqlite', + id: '0', + prevId: '0', + ...rest2, + } as const; + + const sn1 = squashSqliteScheme(sch1, 'push'); + const sn2 = squashSqliteScheme(sch2, 'push'); + + const renames = new Set(renamesArr); + + if (!cli) { + const { sqlStatements, statements, _meta } = await applyLibSQLSnapshotsDiff( + sn1, + sn2, + testTablesResolver(renames), + testColumnsResolver(renames), + sch1, + sch2, + 'push', + ); + + const { + statementsToExecute, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await libSqlLogSuggestionsAndReturn( + { + query: async (sql: string, params?: any[]) => { + const res = await client.execute({ sql, args: params || [] }); + return res.rows as T[]; + }, + run: async (query: string) => { + await client.execute(query); + }, + batch: async ( + queries: { query: string; values?: any[] | undefined }[], + ) => { + await client.batch( + queries.map((it) => ({ sql: it.query, args: it.values ?? [] })), + ); + }, + }, + statements, + sn1, + sn2, + _meta!, + ); + + return { + sqlStatements: statementsToExecute, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + }; + } else { + const { sqlStatements, statements } = await applyLibSQLSnapshotsDiff( + sn1, + sn2, + tablesResolver, + columnsResolver, + sch1, + sch2, + 'push', + ); + return { sqlStatements, statements }; + } +} + export const applySqliteDiffs = async ( sn: SqliteSchema, action?: 'push' | undefined, @@ -1094,6 +1251,54 @@ export const applySqliteDiffs = async ( return { sqlStatements, statements }; }; +export const applyLibSQLDiffs = async ( + sn: SqliteSchema, + action?: 'push' | undefined, +) => { + const dryRun = { + version: '6', + dialect: 'sqlite', + id: '0', + prevId: '0', + tables: {}, + enums: {}, + schemas: {}, + _meta: { + schemas: {}, + tables: {}, + columns: {}, + }, + } as const; + + const tables = Object.values(sn).filter((it) => is(it, SQLiteTable)) as SQLiteTable[]; + + const serialized1 = generateSqliteSnapshot(tables); + + const { version: v1, dialect: d1, ...rest1 } = serialized1; + + const sch1 = { + version: '6', + dialect: 'sqlite', + id: '0', + prevId: '0', + ...rest1, + } as const; + + const sn1 = squashSqliteScheme(sch1, action); + + const { sqlStatements, statements } = await applyLibSQLSnapshotsDiff( + dryRun, + sn1, + testTablesResolver(new Set()), + testColumnsResolver(new Set()), + dryRun, + sch1, + action, + ); + + return { sqlStatements, statements }; +}; + export const diffTestSchemasSqlite = async ( left: SqliteSchema, right: SqliteSchema, @@ -1154,6 +1359,66 @@ export const diffTestSchemasSqlite = async ( return { sqlStatements, statements }; }; +export const diffTestSchemasLibSQL = async ( + left: SqliteSchema, + right: SqliteSchema, + renamesArr: string[], + cli: boolean = false, +) => { + const leftTables = Object.values(left).filter((it) => is(it, SQLiteTable)) as SQLiteTable[]; + + const rightTables = Object.values(right).filter((it) => is(it, SQLiteTable)) as SQLiteTable[]; + + const serialized1 = generateSqliteSnapshot(leftTables); + const serialized2 = generateSqliteSnapshot(rightTables); + + const { version: v1, dialect: d1, ...rest1 } = serialized1; + const { version: v2, dialect: d2, ...rest2 } = serialized2; + + const sch1 = { + version: '6', + dialect: 'sqlite', + id: '0', + prevId: '0', + ...rest1, + } as const; + + const sch2 = { + version: '6', + dialect: 'sqlite', + id: '0', + prevId: '0', + ...rest2, + } as const; + + const sn1 = squashSqliteScheme(sch1); + const sn2 = squashSqliteScheme(sch2); + + const renames = new Set(renamesArr); + + if (!cli) { + const { sqlStatements, statements } = await applyLibSQLSnapshotsDiff( + sn1, + sn2, + testTablesResolver(renames), + testColumnsResolver(renames), + sch1, + sch2, + ); + return { sqlStatements, statements }; + } + + const { sqlStatements, statements } = await applyLibSQLSnapshotsDiff( + sn1, + sn2, + tablesResolver, + columnsResolver, + sch1, + sch2, + ); + return { sqlStatements, statements }; +}; + // --- Introspect to file helpers --- export const introspectPgToFile = async ( diff --git a/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts new file mode 100644 index 000000000..012ea1eac --- /dev/null +++ b/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts @@ -0,0 +1,1209 @@ +import { JsonStatement } from 'src/jsonStatements'; +import { SQLiteSchemaSquashed } from 'src/serializer/sqliteSchema'; +import { libSQLCombineStatements } from 'src/statementCombiner'; +import { expect, test } from 'vitest'; + +/** + * ! before: + * + * user: { + * id INT; + * first_name INT; + * iq INT; + * PRIMARY KEY (id, iq) + * INDEXES: { + * UNIQUE id; + * } + * } + * + * ! after: + * + * new_user: { + * id INT; + * first_name INT; + * iq INT; + * PRIMARY KEY (id, iq) + * INDEXES: {} + * } + * + * rename table and drop unique index + * expect to get "rename_table" statement and then "recreate_table" + */ +test(`rename table and drop index`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'rename_table', + fromSchema: '', + toSchema: '', + tableNameFrom: 'user', + tableNameTo: 'new_user', + }, + { + type: 'drop_index', + tableName: 'new_user', + data: 'user_first_name_unique;first_name;true;', + schema: '', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + first_name: { + name: 'first_name', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + iq: { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: { + user_first_name_unique: 'user_first_name_unique;first_name;true;', + }, + foreignKeys: {}, + compositePrimaryKeys: { + user_id_iq_pk: 'id,iq', + }, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + new_user: { + name: 'new_user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + first_name: { + name: 'first_name', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + iq: { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: { + new_user_id_iq_pk: 'id,iq', + }, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'rename_table', + fromSchema: '', + toSchema: '', + tableNameFrom: 'user', + tableNameTo: 'new_user', + }, + { + type: 'drop_index', + tableName: 'new_user', + data: 'user_first_name_unique;first_name;true;', + schema: '', + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +/** + * ! before: + * + * autoincrement1: { + * id INT PRIMARY KEY; + * } + * + * autoincrement2: { + * id INT PRIMARY KEY AUTOINCREMENT; + * } + * + * dropNotNull: { + * id INT NOT NULL; + * } + * + * ! after: + * + * autoincrement1: { + * id INT PRIMARY KEY AUTOINCREMENT; + * } + * + * autoincrement2: { + * id INT PRI { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_set_autoincrement', + tableName: 'autoincrement1', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: true, + columnPk: true, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_drop_autoincrement', + tableName: 'autoincrement2', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: true, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_drop_notnull', + tableName: 'dropNotNull', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + autoincrement1: { + name: 'autoincrement1', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + autoincrement2: { + name: 'autoincrement2', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: false, + autoincrement: true, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + dropNotNull: { + name: 'dropNotNull', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + autoincrement1: { + name: 'autoincrement1', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: true, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + autoincrement2: { + name: 'autoincrement2', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + dropNotNull: { + name: 'dropNotNull', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'recreate_table', + tableName: 'autoincrement1', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: true, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + type: 'recreate_table', + tableName: 'autoincrement2', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + type: 'alter_table_alter_column_drop_notnull', + tableName: 'dropNotNull', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +/** + * ! before: + * + * pk1: { + * id INT; + * } + * + * pk2: { + * id INT PRIMARY KEY; + * } + * + * ref_table: { + * id INT; + * } + * + * create_reference: { + * id INT; + * } + * + * ! after: + * + * pk1: { + * id INT PRIMARY KEY; + * } + * + * pk2: { + * id INT; + * } + * + * ref_table: { + * id INT; + * } + * + * create_reference: { + * id INT -> ref_table INT; + * } + * + * drop primary key for pk2 + * set primary key for pk1 + * "create_reference" reference on "ref_table" + * + * expect to: + * - "recreate_table" statement for pk1 + * - "recreate_table" statement for pk2 + * - "create_reference" statement for create_reference + */ +test(`drop and set primary key. create reference`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_set_pk', + tableName: 'pk1', + schema: '', + columnName: 'id', + }, + { + type: 'alter_table_alter_column_set_notnull', + tableName: 'pk1', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: true, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_drop_pk', + tableName: 'pk2', + columnName: 'id', + schema: '', + }, + { + type: 'alter_table_alter_column_drop_notnull', + tableName: 'pk2', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + { + type: 'create_reference', + tableName: 'create_reference', + data: 'create_reference_id_ref_table_id_fk;create_reference;id;ref_table;id;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'int', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + create_reference: { + name: 'create_reference', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + pk1: { + name: 'pk1', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + pk2: { + name: 'pk2', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + ref_table: { + name: 'ref_table', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + create_reference: { + name: 'create_reference', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + create_reference_id_ref_table_id_fk: + 'create_reference_id_ref_table_id_fk;create_reference;id;ref_table;id;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + pk1: { + name: 'pk1', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + pk2: { + name: 'pk2', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + ref_table: { + name: 'ref_table', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'recreate_table', + tableName: 'pk1', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + type: 'recreate_table', + tableName: 'pk2', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + type: 'create_reference', + tableName: 'create_reference', + data: 'create_reference_id_ref_table_id_fk;create_reference;id;ref_table;id;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'int', + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +/** + * ! before: + * + * fk1: { + * fk_id INT; + * fk_id1 INT; + * } + * + * fk2: { + * fk2_id INT; -> composite reference on ref_table id INT + * fk2_id1 INT; -> composite reference on ref_table id1 INT + * } + * + * ref_table: { + * id INT; + * id1 INT; + * } + * + * ! after: + * + * fk1: { + * fk_id INT; -> composite reference on ref_table id INT + * fk_id1 INT; -> composite reference on ref_table id1 INT + * } + * + * fk2: { + * fk2_id INT; + * fk2_id1 INT; + * } + * + * ref_table: { + * id INT; + * id1 INT; + * } + * + * set multi column reference for fk1 + * drop multi column reference for fk2 + * + * expect to: + * - "recreate_table" statement for fk1 + * - "recreate_table" statement for fk2 + */ +test(`set and drop multiple columns reference`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'delete_reference', + tableName: 'fk1', + data: 'fk1_fk_id_fk_id1_ref_table_id_id1_fk;fk1;fk_id,fk_id1;ref_table;id,id1;no action;no action', + schema: '', + isMulticolumn: true, + }, + { + type: 'create_reference', + tableName: 'fk2', + data: 'fk2_fk2_id_fk2_id1_ref_table_id_id1_fk;fk2;fk2_id,fk2_id1;ref_table;id,id1;no action;no action', + schema: '', + isMulticolumn: true, + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + fk1: { + name: 'fk1', + columns: { + fk_id: { + name: 'fk_id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + fk_id1: { + name: 'fk_id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + fk1_fk_id_fk_id1_ref_table_id_id1_fk: + 'fk1_fk_id_fk_id1_ref_table_id_id1_fk;fk1;fk_id,fk_id1;ref_table;id,id1;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + fk2: { + name: 'fk2', + columns: { + fk2_id: { + name: 'fk2_id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + fk2_id1: { + name: 'fk2_id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + ref_table: { + name: 'ref_table', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + id1: { + name: 'id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + fk1: { + name: 'fk1', + columns: { + fk_id: { + name: 'fk_id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + fk_id1: { + name: 'fk_id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + fk2: { + name: 'fk2', + columns: { + fk2_id: { + name: 'fk2_id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + fk2_id1: { + name: 'fk2_id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + fk2_fk2_id_fk2_id1_ref_table_id_id1_fk: + 'fk2_fk2_id_fk2_id1_ref_table_id_id1_fk;fk2;fk2_id,fk2_id1;ref_table;id,id1;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + ref_table: { + name: 'ref_table', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + id1: { + name: 'id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'recreate_table', + tableName: 'fk1', + columns: [ + { + name: 'fk_id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'fk_id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + type: 'recreate_table', + tableName: 'fk2', + columns: [ + { + name: 'fk2_id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'fk2_id1', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [ + { + name: 'fk2_fk2_id_fk2_id1_ref_table_id_id1_fk', + tableFrom: 'fk2', + tableTo: 'ref_table', + columnsFrom: ['fk2_id', 'fk2_id1'], + columnsTo: ['id', 'id1'], + onDelete: 'no action', + onUpdate: 'no action', + }, + ], + uniqueConstraints: [], + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +/** + * ! before: + * + * pk: { + * pk TEXT PRIMARY KEY; + * } + * + * simple: { + * simple TEXT; + * } + * + * unique: { + * unique INT UNIQUE; + * } + * + * ! after: + * + * pk: { + * pk INT PRIMARY KEY; + * } + * + * simple: { + * simple INT; + * } + * + * unique: { + * unique TEXT UNIQUE; + * } + * + * set new type for primary key column + * set new type for unique column + * set new type for column without pk or unique + * + * expect to: + * - "recreate_table" statement for pk + * - "recreate_table" statement for unique + * - "alter_table_alter_column_set_type" statement for simple + * - "create_index" statement for unique + */ +test(`set new type for primary key, unique and normal column`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_set_type', + tableName: 'pk', + columnName: 'pk', + newDataType: 'int', + oldDataType: 'text', + schema: '', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: true, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_set_type', + tableName: 'simple', + columnName: 'simple', + newDataType: 'int', + oldDataType: 'text', + schema: '', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_set_type', + tableName: 'unique', + columnName: 'unique', + newDataType: 'text', + oldDataType: 'int', + schema: '', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + pk: { + name: 'pk', + columns: { + pk: { + name: 'pk', + type: 'text', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + simple: { + name: 'simple', + columns: { + simple: { + name: 'simple', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + unique: { + name: 'unique', + columns: { + unique: { + name: 'unique', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: { + unique_unique_unique: 'unique_unique_unique;unique;true;', + }, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + pk: { + name: 'pk', + columns: { + pk: { + name: 'pk', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + simple: { + name: 'simple', + columns: { + simple: { + name: 'simple', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + unique: { + name: 'unique', + columns: { + unique: { + name: 'unique', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: { + unique_unique_unique: 'unique_unique_unique;unique;true;', + }, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'recreate_table', + tableName: 'pk', + columns: [ + { + name: 'pk', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + type: 'alter_table_alter_column_set_type', + tableName: 'simple', + columnName: 'simple', + newDataType: 'int', + oldDataType: 'text', + schema: '', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }, + { + type: 'recreate_table', + tableName: 'unique', + columns: [ + { + name: 'unique', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + { + data: 'unique_unique_unique;unique;true;', + internal: undefined, + schema: '', + tableName: 'unique', + type: 'create_index', + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); diff --git a/drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts new file mode 100644 index 000000000..517343d9b --- /dev/null +++ b/drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts @@ -0,0 +1,778 @@ +import { JsonStatement } from 'src/jsonStatements'; +import { SQLiteSchemaSquashed } from 'src/serializer/sqliteSchema'; +import { sqliteCombineStatements } from 'src/statementCombiner'; +import { expect, test } from 'vitest'; + +test(`renamed column and altered this column type`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, + { + type: 'alter_table_alter_column_set_type', + tableName: 'user', + columnName: 'lastName123', + newDataType: 'int', + oldDataType: 'text', + schema: '', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + columnIsUnique: false, + } as unknown as JsonStatement, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName123', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'recreate_table', + tableName: 'user', + columns: [ + { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + { + name: 'lastName123', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'test', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + ]; + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`renamed column and droped column "test"`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, + { + type: 'alter_table_drop_column', + tableName: 'user', + columnName: 'test', + schema: '', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName123', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, + { + type: 'alter_table_drop_column', + tableName: 'user', + columnName: 'test', + schema: '', + }, + ]; + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`droped column that is part of composite pk`, async (t) => { + const statements: JsonStatement[] = [ + { type: 'delete_composite_pk', tableName: 'user', data: 'id,iq' }, + { + type: 'alter_table_alter_column_set_pk', + tableName: 'user', + schema: '', + columnName: 'id', + }, + { + type: 'alter_table_drop_column', + tableName: 'user', + columnName: 'iq', + schema: '', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + first_nam: { + name: 'first_nam', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + iq: { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: { + user_id_iq_pk: 'id,iq', + }, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: false, + autoincrement: false, + }, + first_nam: { + name: 'first_nam', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements: JsonStatement[] = [ + { + type: 'recreate_table', + tableName: 'user', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: true, + notNull: false, + autoincrement: false, + }, + { + name: 'first_nam', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + ]; + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`drop column "ref"."name", rename column "ref"."age". dropped primary key "user"."id". Set not null to "user"."iq"`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'ref', + oldColumnName: 'age', + newColumnName: 'age1', + schema: '', + }, + { + type: 'alter_table_alter_column_drop_pk', + tableName: 'user', + columnName: 'id', + schema: '', + }, + { + type: 'alter_table_alter_column_drop_autoincrement', + tableName: 'user', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_drop_notnull', + tableName: 'user', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + { + type: 'alter_table_alter_column_set_notnull', + tableName: 'user', + columnName: 'iq', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + { + type: 'alter_table_drop_column', + tableName: 'ref', + columnName: 'text', + schema: '', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: true, + }, + user_iq: { + name: 'user_iq', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + name: { + name: 'name', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + age: { + name: 'age', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_user_iq_user_iq_fk: 'ref_user_iq_user_iq_fk;ref;user_iq;user;iq;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: true, + }, + first_name: { + name: 'first_name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + iq: { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + user_iq: { + name: 'user_iq', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + age1: { + name: 'age1', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_user_iq_user_iq_fk: 'ref_user_iq_user_iq_fk;ref;user_iq;user;iq;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + first_name: { + name: 'first_name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + iq: { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'ref', + oldColumnName: 'age', + newColumnName: 'age1', + schema: '', + }, + { + type: 'alter_table_drop_column', + tableName: 'ref', + columnName: 'text', + schema: '', + }, + { + type: 'recreate_table', + tableName: 'user', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'first_name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [], + uniqueConstraints: [], + }, + ]; + + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`create reference on exising column (table includes unique index). expect to recreate column and recreate index`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'create_reference', + tableName: 'unique', + data: 'unique_ref_pk_pk_pk_fk;unique;ref_pk;pk;pk;no action;no action', + schema: '', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + pk: { + name: 'pk', + columns: { + pk: { + name: 'pk', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + unique: { + name: 'unique', + columns: { + unique: { + name: 'unique', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ref_pk: { + name: 'ref_pk', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: { + unique_unique_unique: 'unique_unique_unique;unique;true;', + }, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + pk: { + name: 'pk', + columns: { + pk: { + name: 'pk', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + unique: { + name: 'unique', + columns: { + unique: { + name: 'unique', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ref_pk: { + name: 'ref_pk', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: { + unique_unique_unique: 'unique_unique_unique;unique;true;', + }, + foreignKeys: { + unique_ref_pk_pk_pk_fk: 'unique_ref_pk_pk_pk_fk;unique;ref_pk;pk;pk;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements: JsonStatement[] = [ + { + type: 'recreate_table', + tableName: 'unique', + columns: [ + { + name: 'unique', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'ref_pk', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + referenceData: [ + { + name: 'unique_ref_pk_pk_pk_fk', + tableFrom: 'unique', + tableTo: 'pk', + columnsFrom: ['ref_pk'], + columnsTo: ['pk'], + onDelete: 'no action', + onUpdate: 'no action', + }, + ], + uniqueConstraints: [], + }, + { + data: 'unique_unique_unique;unique;true;', + internal: undefined, + schema: '', + tableName: 'unique', + type: 'create_index', + }, + ]; + + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); From 0b768d4cee58295e9a2a2f9e7d94b4a8de99f0da Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Mon, 12 Aug 2024 18:14:08 +0300 Subject: [PATCH 18/56] - updated tests - added handling create reference and create column for sqlite and libsql --- drizzle-kit/src/cli/validations/cli.ts | 2 +- drizzle-kit/src/jsonStatements.ts | 1 - drizzle-kit/src/sqlgenerator.ts | 34 +- drizzle-kit/src/statementCombiner.ts | 83 ++- drizzle-kit/tests/cli-generate.test.ts | 10 + drizzle-kit/tests/cli-push.test.ts | 2 + drizzle-kit/tests/libsql-statements.test.ts | 41 ++ drizzle-kit/tests/sqlite-columns.test.ts | 271 ++++++--- drizzle-kit/tests/sqlite-tables.test.ts | 14 +- .../libsql-statements-combiner.test.ts | 552 +++++++++++++++++- ....ts => sqlite-statements-combiner.test.ts} | 392 +++++++++++++ 11 files changed, 1265 insertions(+), 137 deletions(-) rename drizzle-kit/tests/statements-combiner/{sqilte-statements-combiner.test.ts => sqlite-statements-combiner.test.ts} (66%) diff --git a/drizzle-kit/src/cli/validations/cli.ts b/drizzle-kit/src/cli/validations/cli.ts index 9f982b058..9d580fbe4 100644 --- a/drizzle-kit/src/cli/validations/cli.ts +++ b/drizzle-kit/src/cli/validations/cli.ts @@ -25,7 +25,7 @@ export const pushParams = object({ extensionsFilters: literal('postgis').array().optional(), verbose: boolean().optional(), strict: boolean().optional(), - driver: driver, + driver: driver.optional(), }).passthrough(); export type PushParams = TypeOf; diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index dc9511e4b..99f2ad6f0 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -2021,7 +2021,6 @@ export const prepareSqliteAlterColumns = ( columns: AlteredColumn[], // TODO: remove? json2: CommonSquashedSchema, - driver?: 'turso', ): JsonAlterColumnStatement[] => { let statements: JsonAlterColumnStatement[] = []; let dropPkStatements: JsonAlterColumnDropPrimaryKeyStatement[] = []; diff --git a/drizzle-kit/src/sqlgenerator.ts b/drizzle-kit/src/sqlgenerator.ts index e3f5c2b20..cdb2595c4 100644 --- a/drizzle-kit/src/sqlgenerator.ts +++ b/drizzle-kit/src/sqlgenerator.ts @@ -1691,9 +1691,7 @@ type LibSQLModifyColumnStatement = | JsonAlterColumnDropNotNullStatement | JsonAlterColumnSetNotNullStatement | JsonAlterColumnSetDefaultStatement - | JsonAlterColumnDropDefaultStatement - | JsonAlterColumnSetGeneratedStatement - | JsonAlterColumnDropGeneratedStatement; + | JsonAlterColumnDropDefaultStatement; export class LibSQLModifyColumn extends Convertor { can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { @@ -1758,36 +1756,6 @@ export class LibSQLModifyColumn extends Convertor { columnNotNull = statement.columnNotNull ? ` NOT NULL` : ''; break; - case 'alter_table_alter_column_drop_generated': - columnType = ` ${statement.newDataType}`; - - columnDefault = ''; - - columnNotNull = statement.columnNotNull ? ` NOT NULL` : ''; - break; - case 'alter_table_alter_column_set_generated': - return [ - new SQLiteAlterTableDropColumnConvertor().convert({ - type: 'alter_table_drop_column', - tableName: statement.tableName, - columnName: statement.columnName, - schema: '', - }), - new SQLiteAlterTableAddColumnConvertor().convert({ - tableName, - column: { - name: columnName, - type: statement.newDataType, - notNull: statement.columnNotNull, - default: statement.columnDefault, - onUpdate: statement.columnOnUpdate, - autoincrement: statement.columnAutoIncrement, - primaryKey: statement.columnPk, - generated: statement.columnGenerated, - }, - type: 'sqlite_alter_table_add_column', - }), - ]; } // Seems like getting value from simple json2 shanpshot makes dates be dates diff --git a/drizzle-kit/src/statementCombiner.ts b/drizzle-kit/src/statementCombiner.ts index 21cc86b38..549457cf1 100644 --- a/drizzle-kit/src/statementCombiner.ts +++ b/drizzle-kit/src/statementCombiner.ts @@ -180,29 +180,54 @@ export const libSQLCombineStatements = ( continue; } - if (statement.type === 'create_reference' && statement.isMulticolumn) { + if (statement.type === 'create_reference') { const tableName = statement.tableName; + const data = action === 'push' + ? SQLiteSquasher.unsquashPushFK(statement.data) + : SQLiteSquasher.unsquashFK(statement.data); + const statementsForTable = newStatements[tableName]; if (!statementsForTable) { - newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); + newStatements[tableName] = statement.isMulticolumn + ? prepareLibSQLRecreateTable(json2.tables[tableName], action) + : newStatements[tableName] = [statement]; + continue; } - if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { - const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); - const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + // if add column with reference -> skip create_reference statement + if ( + !statement.isMulticolumn + && statementsForTable.some((st) => + st.type === 'sqlite_alter_table_add_column' && st.column.name === data.columnsFrom[0] + ) + ) { + continue; + } - if (wasRename) { - newStatements[tableName].push(...preparedStatements); - } else { - newStatements[tableName] = preparedStatements; + if (statement.isMulticolumn) { + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; } continue; } + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + newStatements[tableName].push(statement); + } + continue; } @@ -300,7 +325,6 @@ export const sqliteCombineStatements = ( || statement.type === 'alter_table_alter_column_set_autoincrement' || statement.type === 'alter_table_alter_column_drop_pk' || statement.type === 'alter_table_alter_column_set_pk' - || statement.type === 'create_reference' || statement.type === 'delete_reference' || statement.type === 'alter_reference' || statement.type === 'create_composite_pk' @@ -360,6 +384,45 @@ export const sqliteCombineStatements = ( continue; } + if (statement.type === 'create_reference') { + const tableName = statement.tableName; + + const data = action === 'push' + ? SQLiteSquasher.unsquashPushFK(statement.data) + : SQLiteSquasher.unsquashFK(statement.data); + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareSQLiteRecreateTable(json2.tables[tableName], action); + continue; + } + + // if add column with reference -> skip create_reference statement + if ( + data.columnsFrom.length === 1 + && statementsForTable.some((st) => + st.type === 'sqlite_alter_table_add_column' && st.column.name === data.columnsFrom[0] + ) + ) { + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareLibSQLRecreateTable(json2.tables[tableName], action); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + const tableName = statement.type === 'rename_table' ? statement.tableNameTo : (statement as { tableName: string }).tableName; diff --git a/drizzle-kit/tests/cli-generate.test.ts b/drizzle-kit/tests/cli-generate.test.ts index 3e5c0fc22..6eece0249 100644 --- a/drizzle-kit/tests/cli-generate.test.ts +++ b/drizzle-kit/tests/cli-generate.test.ts @@ -38,6 +38,7 @@ test('generate #1', async (t) => { schema: 'schema.ts', out: 'drizzle', bundle: false, + driver: undefined, }); }); @@ -57,11 +58,13 @@ test('generate #2', async (t) => { schema: 'schema.ts', out: 'out', bundle: false, + driver: undefined, }); }); test('generate #3', async (t) => { const res = await brotest(generate, ''); + if (res.type !== 'handler') assert.fail(res.type, 'handler'); expect(res.options).toStrictEqual({ dialect: 'postgresql', @@ -72,6 +75,7 @@ test('generate #3', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, + driver: undefined, }); }); @@ -89,6 +93,7 @@ test('generate #4', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, + driver: undefined, }); }); @@ -105,6 +110,7 @@ test('generate #5', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, + driver: undefined, }); }); @@ -121,6 +127,7 @@ test('generate #6', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, + driver: undefined, }); }); @@ -140,6 +147,7 @@ test('generate #7', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, + driver: undefined, }); }); @@ -157,6 +165,7 @@ test('generate #8', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: true, // expo driver + driver: 'expo', }); }); @@ -177,6 +186,7 @@ test('generate #9', async (t) => { schema: 'schema.ts', out: 'out', bundle: false, + driver: undefined, }); }); diff --git a/drizzle-kit/tests/cli-push.test.ts b/drizzle-kit/tests/cli-push.test.ts index 1a4bde66d..fb1ed3a11 100644 --- a/drizzle-kit/tests/cli-push.test.ts +++ b/drizzle-kit/tests/cli-push.test.ts @@ -46,6 +46,7 @@ test('push #2', async (t) => { tablesFilter: [], strict: false, verbose: false, + driver: 'turso', }); }); @@ -66,6 +67,7 @@ test('push #3', async (t) => { tablesFilter: [], strict: false, verbose: false, + driver: 'd1-http', }); }); diff --git a/drizzle-kit/tests/libsql-statements.test.ts b/drizzle-kit/tests/libsql-statements.test.ts index adf354458..927411e44 100644 --- a/drizzle-kit/tests/libsql-statements.test.ts +++ b/drizzle-kit/tests/libsql-statements.test.ts @@ -833,3 +833,44 @@ test('drop foriegn key for multiple columns', async (t) => { `DROP TABLE \`__old__generate_users\`;`, ); }); + +test('alter column drop generated', async (t) => { + const from = { + users: sqliteTable('table', { + id: int('id').primaryKey().notNull(), + name: text('name').generatedAlwaysAs('drizzle is the best').notNull(), + }), + }; + + const to = { + users: sqliteTable('table', { + id: int('id').primaryKey().notNull(), + name: text('name').notNull(), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + from, + to, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columnAutoIncrement: false, + columnDefault: undefined, + columnGenerated: undefined, + columnName: 'name', + columnNotNull: true, + columnOnUpdate: undefined, + columnPk: false, + newDataType: 'text', + schema: '', + tableName: 'table', + type: 'alter_table_alter_column_drop_generated', + }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe(`ALTER TABLE \`table\` DROP COLUMN \`name\`;`); + expect(sqlStatements[1]).toBe(`ALTER TABLE \`table\` ADD \`name\` text NOT NULL;`); +}); diff --git a/drizzle-kit/tests/sqlite-columns.test.ts b/drizzle-kit/tests/sqlite-columns.test.ts index 8a258072a..e71b95e01 100644 --- a/drizzle-kit/tests/sqlite-columns.test.ts +++ b/drizzle-kit/tests/sqlite-columns.test.ts @@ -8,6 +8,7 @@ import { sqliteTable, text, } from 'drizzle-orm/sqlite-core'; +import { JsonRecreateTableStatement } from 'src/jsonStatements'; import { expect, test } from 'vitest'; import { diffTestSchemasSqlite } from './schemaDiffer'; @@ -223,7 +224,7 @@ test('add columns #5', async (t) => { const { statements } = await diffTestSchemasSqlite(schema1, schema2, []); // TODO: Fix here - expect(statements.length).toBe(2); + expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ type: 'sqlite_alter_table_add_column', tableName: 'users', @@ -332,12 +333,38 @@ test('add foreign key #1', async (t) => { const { statements } = await diffTestSchemasSqlite(schema1, schema2, []); expect(statements.length).toBe(1); - expect(statements[0]).toStrictEqual({ - type: 'create_reference', - tableName: 'users', - schema: '', - data: 'users_report_to_users_id_fk;users;report_to;users;id;no action;no action', - }); + expect(statements[0]).toStrictEqual( + { + type: 'recreate_table', + columns: [{ + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, { + autoincrement: false, + generated: undefined, + name: 'report_to', + notNull: false, + primaryKey: false, + type: 'integer', + }], + compositePKs: [], + referenceData: [{ + columnsFrom: ['report_to'], + columnsTo: ['id'], + name: 'users_report_to_users_id_fk', + tableFrom: 'users', + tableTo: 'users', + onDelete: 'no action', + onUpdate: 'no action', + }], + tableName: 'users', + uniqueConstraints: [], + } as JsonRecreateTableStatement, + ); }); test('add foreign key #2', async (t) => { @@ -371,11 +398,35 @@ test('add foreign key #2', async (t) => { expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - type: 'create_reference', + type: 'recreate_table', + columns: [{ + autoincrement: true, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, { + autoincrement: false, + generated: undefined, + name: 'report_to', + notNull: false, + primaryKey: false, + type: 'integer', + }], + compositePKs: [], + referenceData: [{ + columnsFrom: ['report_to'], + columnsTo: ['id'], + name: 'reportee_fk', + tableFrom: 'users', + tableTo: 'users', + onDelete: 'no action', + onUpdate: 'no action', + }], tableName: 'users', - schema: '', - data: 'reportee_fk;users;report_to;users;id;no action;no action', - }); + uniqueConstraints: [], + } as JsonRecreateTableStatement); }); test('alter column change name #1', async (t) => { @@ -513,9 +564,26 @@ test('alter table add composite pk', async (t) => { expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - type: 'create_composite_pk', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'id1', + notNull: false, + primaryKey: false, + type: 'integer', + }, { + autoincrement: false, + generated: undefined, + name: 'id2', + notNull: false, + primaryKey: false, + type: 'integer', + }], + compositePKs: [['id1', 'id2']], + referenceData: [], tableName: 'table', - data: 'id1,id2', + uniqueConstraints: [], }); }); @@ -540,16 +608,19 @@ test('alter column drop not null', async (t) => { expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - type: 'alter_table_alter_column_drop_notnull', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + primaryKey: false, + type: 'text', + }], + compositePKs: [], + referenceData: [], tableName: 'table', - columnName: 'name', - schema: '', - newDataType: 'text', - columnDefault: undefined, - columnOnUpdate: undefined, - columnNotNull: false, - columnAutoIncrement: false, - columnPk: false, + uniqueConstraints: [], }); }); @@ -574,16 +645,19 @@ test('alter column add not null', async (t) => { expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - type: 'alter_table_alter_column_set_notnull', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: true, + primaryKey: false, + type: 'text', + }], + compositePKs: [], + referenceData: [], tableName: 'table', - columnName: 'name', - schema: '', - newDataType: 'text', - columnDefault: undefined, - columnOnUpdate: undefined, - columnNotNull: true, - columnAutoIncrement: false, - columnPk: false, + uniqueConstraints: [], }); }); @@ -608,16 +682,20 @@ test('alter column add default', async (t) => { expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - type: 'alter_table_alter_column_set_default', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + primaryKey: false, + type: 'text', + default: "'dan'", + }], + compositePKs: [], + referenceData: [], tableName: 'table', - columnName: 'name', - schema: '', - newDataType: 'text', - columnNotNull: false, - columnOnUpdate: undefined, - columnAutoIncrement: false, - newDefaultValue: "'dan'", - columnPk: false, + uniqueConstraints: [], }); }); @@ -642,16 +720,19 @@ test('alter column drop default', async (t) => { expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - type: 'alter_table_alter_column_drop_default', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + primaryKey: false, + type: 'text', + }], + compositePKs: [], + referenceData: [], tableName: 'table', - columnName: 'name', - schema: '', - newDataType: 'text', - columnNotNull: false, - columnOnUpdate: undefined, - columnDefault: undefined, - columnAutoIncrement: false, - columnPk: false, + uniqueConstraints: [], }); }); @@ -674,31 +755,22 @@ test('alter column add default not null', async (t) => { [], ); - expect(statements.length).toBe(2); - expect(statements[0]).toStrictEqual({ - columnAutoIncrement: false, - columnName: 'name', - columnNotNull: true, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - newDefaultValue: "'dan'", - schema: '', - tableName: 'table', - type: 'alter_table_alter_column_set_default', - }); - + expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - columnAutoIncrement: false, - columnName: 'name', - columnNotNull: true, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - newDefaultValue: "'dan'", - schema: '', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: true, + primaryKey: false, + type: 'text', + default: "'dan'", + }], + compositePKs: [], + referenceData: [], tableName: 'table', - type: 'alter_table_alter_column_set_default', + uniqueConstraints: [], }); }); @@ -721,30 +793,61 @@ test('alter column drop default not null', async (t) => { [], ); - expect(statements.length).toBe(2); + expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - columnAutoIncrement: false, - columnDefault: undefined, - columnName: 'name', - columnNotNull: false, - columnOnUpdate: undefined, - columnPk: false, - newDataType: 'text', - schema: '', + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + primaryKey: false, + type: 'text', + }], + compositePKs: [], + referenceData: [], tableName: 'table', - type: 'alter_table_alter_column_drop_default', + uniqueConstraints: [], }); +}); +test('alter column drop generated', async (t) => { + const from = { + users: sqliteTable('table', { + id: int('id').primaryKey().notNull(), + name: text('name').generatedAlwaysAs('drizzle is the best').notNull(), + }), + }; + + const to = { + users: sqliteTable('table', { + id: int('id').primaryKey().notNull(), + name: text('name').notNull(), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasSqlite( + from, + to, + [], + ); + + expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ columnAutoIncrement: false, columnDefault: undefined, + columnGenerated: undefined, columnName: 'name', - columnNotNull: false, + columnNotNull: true, columnOnUpdate: undefined, columnPk: false, newDataType: 'text', schema: '', tableName: 'table', - type: 'alter_table_alter_column_drop_default', + type: 'alter_table_alter_column_drop_generated', }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe(`ALTER TABLE \`table\` DROP COLUMN \`name\`;`); + expect(sqlStatements[1]).toBe(`ALTER TABLE \`table\` ADD \`name\` text NOT NULL;`); }); diff --git a/drizzle-kit/tests/sqlite-tables.test.ts b/drizzle-kit/tests/sqlite-tables.test.ts index d7781f150..aa44908ba 100644 --- a/drizzle-kit/tests/sqlite-tables.test.ts +++ b/drizzle-kit/tests/sqlite-tables.test.ts @@ -162,6 +162,13 @@ test('add table #7', async () => { expect(statements.length).toBe(2); expect(statements[0]).toStrictEqual({ + type: 'rename_table', + tableNameFrom: 'users1', + tableNameTo: 'users2', + fromSchema: undefined, + toSchema: undefined, + }); + expect(statements[1]).toStrictEqual({ type: 'sqlite_create_table', tableName: 'users', columns: [], @@ -169,13 +176,6 @@ test('add table #7', async () => { uniqueConstraints: [], referenceData: [], }); - expect(statements[1]).toStrictEqual({ - type: 'rename_table', - tableNameFrom: 'users1', - tableNameTo: 'users2', - fromSchema: undefined, - toSchema: undefined, - }); }); test('add table #8', async () => { diff --git a/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts index 012ea1eac..342e55232 100644 --- a/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts +++ b/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts @@ -1,5 +1,6 @@ -import { JsonStatement } from 'src/jsonStatements'; +import { JsonAddColumnStatement, JsonSqliteAddColumnStatement, JsonStatement } from 'src/jsonStatements'; import { SQLiteSchemaSquashed } from 'src/serializer/sqliteSchema'; +import { SQLiteAlterTableAddColumnConvertor } from 'src/sqlgenerator'; import { libSQLCombineStatements } from 'src/statementCombiner'; import { expect, test } from 'vitest'; @@ -1207,3 +1208,552 @@ test(`set new type for primary key, unique and normal column`, async (t) => { newJsonStatements, ); }); + +test(`add columns. set fk`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: undefined, + }, + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: undefined, + }, + { + type: 'create_reference', + tableName: 'ref', + data: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_new_age_user_new_age_fk: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: undefined, + }, + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: undefined, + }, + { + type: 'create_reference', + tableName: 'ref', + data: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`add column and fk`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + { + type: 'create_reference', + tableName: 'ref', + data: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_test1_user_new_age_fk: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_new_age_user_new_age_fk: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`add column and fk`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + { + type: 'create_reference', + tableName: 'ref', + data: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_test1_user_new_age_fk: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_new_age_user_new_age_fk: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + ]; + expect(libSQLCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); diff --git a/drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/sqlite-statements-combiner.test.ts similarity index 66% rename from drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts rename to drizzle-kit/tests/statements-combiner/sqlite-statements-combiner.test.ts index 517343d9b..2fcaf6436 100644 --- a/drizzle-kit/tests/statements-combiner/sqilte-statements-combiner.test.ts +++ b/drizzle-kit/tests/statements-combiner/sqlite-statements-combiner.test.ts @@ -776,3 +776,395 @@ test(`create reference on exising column (table includes unique index). expect t newJsonStatements, ); }); + +test(`add columns. set fk`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: undefined, + }, + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: undefined, + }, + { + type: 'create_reference', + tableName: 'ref', + data: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_new_age_user_new_age_fk: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + columns: [ + { + autoincrement: false, + name: 'id1', + notNull: true, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + name: 'new_age', + notNull: false, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + name: 'test', + notNull: false, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + name: 'test1', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [ + { + columnsFrom: [ + 'new_age', + ], + columnsTo: [ + 'new_age', + ], + name: 'ref_new_age_user_new_age_fk', + onDelete: 'no action', + onUpdate: 'no action', + tableFrom: 'ref', + tableTo: 'user', + }, + ], + tableName: 'ref', + type: 'recreate_table', + uniqueConstraints: [], + }, + ]; + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`add column and fk`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + { + type: 'create_reference', + tableName: 'ref', + data: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + schema: '', + columnNotNull: false, + columnDefault: undefined, + columnType: 'integer', + }, + ]; + const json1: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_test1_user_new_age_fk: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + const json2: SQLiteSchemaSquashed = { + version: '6', + dialect: 'sqlite', + tables: { + ref: { + name: 'ref', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test1: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: { + ref_new_age_user_new_age_fk: 'ref_new_age_user_new_age_fk;ref;new_age;user;new_age;no action;no action', + }, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + user: { + name: 'user', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + foreignKeys: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + enums: {}, + }; + + const newJsonStatements = [ + { + type: 'sqlite_alter_table_add_column', + tableName: 'ref', + column: { + name: 'test1', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + referenceData: 'ref_test1_user_new_age_fk;ref;test1;user;new_age;no action;no action', + }, + ]; + expect(sqliteCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); From 7239e9f8e8bce026a3ae06afd94e134ae22d6a7c Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Thu, 15 Aug 2024 18:21:10 +0300 Subject: [PATCH 19/56] - handled foreign keys for recreate table - handled alter column with unique keys - made more tests - updated recreate sql statements order --- drizzle-kit/package.json | 2 +- .../src/cli/commands/libSqlPushUtils.ts | 74 ++- .../src/cli/commands/sqlitePushUtils.ts | 58 +- drizzle-kit/src/jsonStatements.ts | 2 +- drizzle-kit/src/sqlgenerator.ts | 351 +++-------- drizzle-kit/src/statementCombiner.ts | 24 +- drizzle-kit/tests/libsql-statements.test.ts | 335 +++++++---- drizzle-kit/tests/push/libsql.test.ts | 438 ++++++++++++-- drizzle-kit/tests/push/sqlite.test.ts | 554 ++++++++++++++---- drizzle-kit/tests/sqlite-columns.test.ts | 141 ++++- .../libsql-statements-combiner.test.ts | 28 +- 11 files changed, 1388 insertions(+), 619 deletions(-) diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index 552c14d0e..8bfdebac4 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -38,7 +38,7 @@ "build": "rm -rf ./dist && tsx build.ts && cp package.json dist/ && attw --pack dist", "build:dev": "rm -rf ./dist && tsx build.dev.ts && tsc -p tsconfig.cli-types.json && chmod +x ./dist/index.cjs", "pack": "cp package.json README.md dist/ && (cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz", - "tsc": "tsc -p tsconfig.build.json", + "tsc": "tsc -p tsconfig.cli-types.json", "publish": "npm publish package.tgz" }, "dependencies": { diff --git a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts index 537eee0a5..3bc9e8225 100644 --- a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts +++ b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts @@ -32,54 +32,62 @@ export const _moveDataStatements = ( ) => { const statements: string[] = []; - statements.push( - new SqliteRenameTableConvertor().convert({ - type: 'rename_table', - tableNameFrom: tableName, - tableNameTo: `__old_push_${tableName}`, - fromSchema: '', - toSchema: '', - }), - ); + const newTableName = `__new_${tableName}`; + // create table statement from a new json2 with proper name const tableColumns = Object.values(json.tables[tableName].columns); const referenceData = Object.values(json.tables[tableName].foreignKeys); const compositePKs = Object.values( json.tables[tableName].compositePrimaryKeys, ).map((it) => SQLiteSquasher.unsquashPK(it)); + const fks = referenceData.map((it) => SQLiteSquasher.unsquashPushFK(it)); + + // create new table statements.push( new SQLiteCreateTableConvertor().convert({ type: 'sqlite_create_table', - tableName: tableName, + tableName: newTableName, columns: tableColumns, - referenceData: referenceData.map((it) => SQLiteSquasher.unsquashPushFK(it)), + referenceData: fks, compositePKs, }), ); + // move data if (!dataLoss) { const columns = Object.keys(json.tables[tableName].columns).map( (c) => `"${c}"`, ); statements.push( - `INSERT INTO \`${tableName}\`(${ + `INSERT INTO \`${newTableName}\`(${ columns.join( ', ', ) - }) SELECT (${columns.join(', ')}) FROM \`__old_push_${tableName}\`;`, + }) SELECT ${columns.join(', ')} FROM \`${tableName}\`;`, ); } statements.push( new SQLiteDropTableConvertor().convert({ type: 'drop_table', - tableName: `__old_push_${tableName}`, + tableName: tableName, schema: '', }), ); + // rename table + statements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + for (const idx of Object.values(json.tables[tableName].indexes)) { statements.push( new CreateSqliteIndexConvertor().convert({ @@ -90,7 +98,6 @@ export const _moveDataStatements = ( }), ); } - return statements; }; @@ -216,6 +223,8 @@ export const libSqlLogSuggestionsAndReturn = async ( } else if (statement.type === 'recreate_table') { const tableName = statement.tableName; + let dataLoss = false; + const oldTableName = getOldTableName(tableName, meta); const prevColumnNames = Object.keys(json1.tables[oldTableName].columns); @@ -249,28 +258,31 @@ export const libSqlLogSuggestionsAndReturn = async ( if (addedColumns.length) { for (const addedColumn of addedColumns) { const [res] = await connection.query<{ count: string }>( - `select count(\`${tableName}\`.\`${addedColumn}\`) as count from \`${tableName}\``, + `select count(*) as count from \`${tableName}\``, ); const columnConf = json2.tables[tableName].columns[addedColumn]; const count = Number(res.count); if (count > 0 && columnConf.notNull && !columnConf.default) { + dataLoss = true; + infoToPrint.push( `· You're about to add not-null ${ chalk.underline( addedColumn, ) - } column without default value, which contains ${count} items`, + } column without default value to table, which contains ${count} items`, ); shouldAskForApprove = true; tablesToTruncate.push(tableName); + + statementsToExecute.push(`DELETE FROM \`${tableName}\`;`); } } } - statementsToExecute.push(..._moveDataStatements(tableName, json2)); - + // check if some tables referencing current for pragma const tablesReferencingCurrent: string[] = []; for (const table of Object.values(json2.tables)) { @@ -281,12 +293,26 @@ export const libSqlLogSuggestionsAndReturn = async ( tablesReferencingCurrent.push(...tablesRefs); } - const uniqueTableRefs = [...new Set(tablesReferencingCurrent)]; - - for (const table of uniqueTableRefs) { - statementsToExecute.push(..._moveDataStatements(table, json2)); + if (!tablesReferencingCurrent.length) { + statementsToExecute.push(..._moveDataStatements(tableName, json2, dataLoss)); + continue; } - } else if (statement.type === 'alter_table_alter_column_set_generated') { + + // ! for libsql it will break + const [{ foreign_keys: pragmaState }] = await connection.query<{ + foreign_keys: number; + }>(`PRAGMA foreign_keys;`); + + if (pragmaState) statementsToExecute.push(`PRAGMA foreign_keys=OFF;`); + // recreate table + statementsToExecute.push( + ..._moveDataStatements(tableName, json2, dataLoss), + ); + if (pragmaState) statementsToExecute.push(`PRAGMA foreign_keys=ON;`); + } else if ( + statement.type === 'alter_table_alter_column_set_generated' + || statement.type === 'alter_table_alter_column_drop_generated' + ) { const tableName = statement.tableName; const res = await connection.query<{ count: string }>( diff --git a/drizzle-kit/src/cli/commands/sqlitePushUtils.ts b/drizzle-kit/src/cli/commands/sqlitePushUtils.ts index e04546157..bcc2d19db 100644 --- a/drizzle-kit/src/cli/commands/sqlitePushUtils.ts +++ b/drizzle-kit/src/cli/commands/sqlitePushUtils.ts @@ -19,16 +19,7 @@ export const _moveDataStatements = ( ) => { const statements: string[] = []; - // rename table to __old_${tablename} - statements.push( - new SqliteRenameTableConvertor().convert({ - type: 'rename_table', - tableNameFrom: tableName, - tableNameTo: `__old_push_${tableName}`, - fromSchema: '', - toSchema: '', - }), - ); + const newTableName = `__new_${tableName}`; // create table statement from a new json2 with proper name const tableColumns = Object.values(json.tables[tableName].columns); @@ -39,10 +30,11 @@ export const _moveDataStatements = ( const fks = referenceData.map((it) => SQLiteSquasher.unsquashPushFK(it)); + // create new table statements.push( new SQLiteCreateTableConvertor().convert({ type: 'sqlite_create_table', - tableName: tableName, + tableName: newTableName, columns: tableColumns, referenceData: fks, compositePKs, @@ -56,22 +48,33 @@ export const _moveDataStatements = ( ); statements.push( - `INSERT INTO \`${tableName}\`(${ + `INSERT INTO \`${newTableName}\`(${ columns.join( ', ', ) - }) SELECT (${columns.join(', ')}) FROM \`__old_push_${tableName}\`;`, + }) SELECT ${columns.join(', ')} FROM \`${tableName}\`;`, ); } - // drop table with name __old_${tablename} + statements.push( new SQLiteDropTableConvertor().convert({ type: 'drop_table', - tableName: `__old_push_${tableName}`, + tableName: tableName, schema: '', }), ); + // rename table + statements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + for (const idx of Object.values(json.tables[tableName].indexes)) { statements.push( new CreateSqliteIndexConvertor().convert({ @@ -207,6 +210,8 @@ export const logSuggestionsAndReturn = async ( const tableName = statement.tableName; const oldTableName = getOldTableName(tableName, meta); + let dataLoss = false; + const prevColumnNames = Object.keys(json1.tables[oldTableName].columns); const currentColumnNames = Object.keys(json2.tables[tableName].columns); const { removedColumns, addedColumns } = findAddedAndRemoved( @@ -245,6 +250,7 @@ export const logSuggestionsAndReturn = async ( const count = Number(res.count); if (count > 0 && columnConf.notNull && !columnConf.default) { + dataLoss = true; infoToPrint.push( `· You're about to add not-null ${ chalk.underline( @@ -254,12 +260,13 @@ export const logSuggestionsAndReturn = async ( ); shouldAskForApprove = true; tablesToTruncate.push(tableName); + + statementsToExecute.push(`DELETE FROM \`${tableName}\`;`); } } } - statementsToExecute.push(..._moveDataStatements(tableName, json2)); - + // check if some tables referencing current for pragma const tablesReferencingCurrent: string[] = []; for (const table of Object.values(json2.tables)) { @@ -270,10 +277,21 @@ export const logSuggestionsAndReturn = async ( tablesReferencingCurrent.push(...tablesRefs); } - const uniqueTableRefs = [...new Set(tablesReferencingCurrent)]; + if (!tablesReferencingCurrent.length) { + statementsToExecute.push(..._moveDataStatements(tableName, json2, dataLoss)); + continue; + } + + const [{ foreign_keys: pragmaState }] = await connection.query<{ + foreign_keys: number; + }>(`PRAGMA foreign_keys;`); - for (const table of uniqueTableRefs) { - statementsToExecute.push(..._moveDataStatements(table, json2)); + if (pragmaState) { + statementsToExecute.push(`PRAGMA foreign_keys=OFF;`); + } + statementsToExecute.push(..._moveDataStatements(tableName, json2, dataLoss)); + if (pragmaState) { + statementsToExecute.push(`PRAGMA foreign_keys=ON;`); } } else { const fromJsonStatement = fromJson([statement], 'sqlite', 'push'); diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index 99f2ad6f0..c4c51d4b4 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -2481,7 +2481,7 @@ export const prepareLibSQLDropReferencesJson = ( : SQLiteSquasher.unsquashFK(fkData); // If all columns from where were references were deleted -> skip this logic - // Drop colums will cover this scenario + // Drop columns will cover this scenario const keys = Object.keys(json2.tables[tableName].columns); const filtered = columnsFrom.filter((it) => keys.includes(it)); const fullDrop = filtered.length === 0; diff --git a/drizzle-kit/src/sqlgenerator.ts b/drizzle-kit/src/sqlgenerator.ts index cdb2595c4..bcd47565a 100644 --- a/drizzle-kit/src/sqlgenerator.ts +++ b/drizzle-kit/src/sqlgenerator.ts @@ -1242,27 +1242,6 @@ class PgAlterTableAlterColumnSetTypeConvertor extends Convertor { } } -class SQLiteAlterTableAlterColumnSetTypeConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'alter_table_alter_column_set_type' - && dialect === 'sqlite' - && !driver - ); - } - - convert(statement: JsonAlterColumnTypeStatement) { - return ( - '/*\n SQLite does not support "Changing existing column type" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - class PgAlterTableAlterColumnSetDefaultConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( @@ -1282,26 +1261,6 @@ class PgAlterTableAlterColumnSetDefaultConvertor extends Convertor { } } -class SqliteAlterTableAlterColumnSetDefaultConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { - return ( - statement.type === 'alter_table_alter_column_set_default' - && dialect === 'sqlite' - ); - } - - convert(statement: JsonAlterColumnSetDefaultStatement) { - return ( - '/*\n SQLite does not support "Set default to column" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - class PgAlterTableAlterColumnDropDefaultConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( @@ -1706,13 +1665,31 @@ export class LibSQLModifyColumn extends Convertor { ); } - convert(statement: LibSQLModifyColumnStatement, json2: SQLiteSchemaSquashed) { + convert(statement: LibSQLModifyColumnStatement, json2: SQLiteSchemaSquashed, action?: 'push') { const { tableName, columnName } = statement; let columnType = ``; let columnDefault: any = ''; let columnNotNull = ''; + const sqlStatements: string[] = []; + + // collect index info + const indexes: { + name: string; + tableName: string; + columns: string[]; + isUnique: boolean; + where?: string | undefined; + }[] = []; + for (const table of Object.values(json2.tables)) { + for (const index of Object.values(table.indexes)) { + const unsquashed = SQLiteSquasher.unsquashIdx(index); + sqlStatements.push(`DROP INDEX IF EXISTS "${unsquashed.name}";`); + indexes.push({ ...unsquashed, tableName: table.name }); + } + } + switch (statement.type) { case 'alter_table_alter_column_set_type': columnType = ` ${statement.newDataType}`; @@ -1763,7 +1740,22 @@ export class LibSQLModifyColumn extends Convertor { ? columnDefault.toISOString() : columnDefault; - return `ALTER TABLE \`${tableName}\` ALTER COLUMN "${columnName}" TO "${columnName}"${columnType}${columnNotNull}${columnDefault};`; + sqlStatements.push( + `ALTER TABLE \`${tableName}\` ALTER COLUMN "${columnName}" TO "${columnName}"${columnType}${columnNotNull}${columnDefault};`, + ); + + for (const index of indexes) { + const indexPart = index.isUnique ? 'UNIQUE INDEX' : 'INDEX'; + const whereStatement = index.where ? ` WHERE ${index.where}` : ''; + const uniqueString = index.columns.map((it) => `\`${it}\``).join(','); + const tableName = index.tableName; + + sqlStatements.push( + `CREATE ${indexPart} \`${index.name}\` ON \`${tableName}\` (${uniqueString})${whereStatement};`, + ); + } + + return sqlStatements; } } @@ -2632,69 +2624,6 @@ class PgAlterTableAlterColumnSetNotNullConvertor extends Convertor { } } -class SqliteAlterTableAlterColumnSetNotNullConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'alter_table_alter_column_set_notnull' - && dialect === 'sqlite' - && !driver - ); - } - - convert(statement: JsonAlterColumnSetNotNullStatement) { - return ( - '/*\n SQLite does not support "Set not null to column" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - -class SqliteAlterTableAlterColumnSetAutoincrementConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'alter_table_alter_column_set_autoincrement' - && dialect === 'sqlite' - && !driver - ); - } - - convert(statement: JsonAlterColumnSetAutoincrementStatement) { - return ( - '/*\n SQLite does not support "Set autoincrement to a column" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - -class SqliteAlterTableAlterColumnDropAutoincrementConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'alter_table_alter_column_drop_autoincrement' - && dialect === 'sqlite' - && !driver - ); - } - - convert(statement: JsonAlterColumnDropAutoincrementStatement) { - return ( - '/*\n SQLite does not support "Drop autoincrement from a column" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - class PgAlterTableAlterColumnDropNotNullConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( @@ -2714,27 +2643,6 @@ class PgAlterTableAlterColumnDropNotNullConvertor extends Convertor { } } -class SqliteAlterTableAlterColumnDropNotNullConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'alter_table_alter_column_drop_notnull' - && dialect === 'sqlite' - && !driver - ); - } - - convert(statement: JsonAlterColumnDropNotNullStatement) { - return ( - '/*\n SQLite does not support "Drop not null from column" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - // FK class PgCreateForeignKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { @@ -2777,24 +2685,6 @@ class PgCreateForeignKeyConvertor extends Convertor { } } -class SqliteCreateForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'create_reference' && dialect === 'sqlite' && !driver - ); - } - - convert(statement: JsonCreateReferenceStatement): string { - return ( - '/*\n SQLite does not support "Creating foreign key on existing column" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - class LibSQLCreateForeignKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { return ( @@ -2812,17 +2702,7 @@ class LibSQLCreateForeignKeyConvertor extends Convertor { const { columnsFrom, columnsTo, tableFrom, onDelete, onUpdate, tableTo } = action === 'push' ? SQLiteSquasher.unsquashPushFK(statement.data) : SQLiteSquasher.unsquashFK(statement.data); - const { columnDefault, columnNotNull, columnType, isMulticolumn } = statement; - - if (isMulticolumn) { - return ( - '/*\n LibSQL does not support "Creating foreign key on multiple columns" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } + const { columnDefault, columnNotNull, columnType } = statement; const onDeleteStatement = onDelete ? ` ON DELETE ${onDelete}` : ''; const onUpdateStatement = onUpdate ? ` ON UPDATE ${onUpdate}` : ''; @@ -2910,22 +2790,6 @@ class PgAlterForeignKeyConvertor extends Convertor { } } -class SqliteAlterForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'alter_reference' && dialect === 'sqlite'; - } - - convert(statement: JsonAlterReferenceStatement): string { - return ( - '/*\n SQLite does not support "Changing existing foreign key" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - class PgDeleteForeignKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'delete_reference' && dialect === 'postgresql'; @@ -2943,66 +2807,6 @@ class PgDeleteForeignKeyConvertor extends Convertor { } } -class SqliteDeleteForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'delete_reference' && dialect === 'sqlite' && !driver - ); - } - - convert(statement: JsonDeleteReferenceStatement): string { - return ( - '/*\n SQLite does not support "Dropping foreign key" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } -} - -class LibSQLDeleteForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { - return ( - statement.type === 'delete_reference' - && dialect === 'sqlite' - && driver === 'turso' - ); - } - - convert( - statement: JsonDeleteReferenceStatement, - json2?: SQLiteSchemaSquashed, - action?: 'push', - ): string { - const { columnsFrom, tableFrom } = action === 'push' - ? SQLiteSquasher.unsquashPushFK(statement.data) - : SQLiteSquasher.unsquashFK(statement.data); - - const { columnDefault, columnNotNull, columnType, isMulticolumn } = statement; - - if (isMulticolumn) { - return ( - '/*\n LibSQL does not support "Creating foreign key on multiple columns" out of the box, we do not generate automatic migration for that, so it has to be done manually' - + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' - + '\n https://www.sqlite.org/lang_altertable.html' - + "\n\n Due to that we don't generate migration automatically and it has to be done manually" - + '\n*/' - ); - } - - const columnsDefaultValue = columnDefault - ? ` DEFAULT ${columnDefault}` - : ''; - const columnNotNullValue = columnNotNull ? ` NOT NULL` : ''; - const columnTypeValue = columnType ? ` ${columnType}` : ''; - - const columnFrom = columnsFrom[0]; - - return `ALTER TABLE \`${tableFrom}\` ALTER COLUMN "${columnFrom}" TO "${columnFrom}"${columnTypeValue}${columnNotNullValue}${columnsDefaultValue};`; - } -} - class MySqlDeleteForeignKeyConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'delete_reference' && dialect === 'mysql'; @@ -3288,25 +3092,17 @@ class SQLiteRecreateTableConvertor extends Convertor { const { tableName, columns, compositePKs, referenceData } = statement; const columnNames = columns.map((it) => `"${it.name}"`).join(', '); + const newTableName = `__new_${tableName}`; const sqlStatements: string[] = []; - // rename table - sqlStatements.push( - new SqliteRenameTableConvertor().convert({ - fromSchema: '', - tableNameFrom: tableName, - tableNameTo: `__old__generate_${tableName}`, - toSchema: '', - type: 'rename_table', - }), - ); + sqlStatements.push(`PRAGMA foreign_keys=OFF;`); // create new table sqlStatements.push( new SQLiteCreateTableConvertor().convert({ type: 'sqlite_create_table', - tableName, + tableName: newTableName, columns, referenceData, compositePKs, @@ -3315,18 +3111,31 @@ class SQLiteRecreateTableConvertor extends Convertor { // migrate data sqlStatements.push( - `INSERT INTO \`${tableName}\`(${columnNames}) SELECT ${columnNames} FROM \`__old__generate_${tableName}\`;`, + `INSERT INTO \`${newTableName}\`(${columnNames}) SELECT ${columnNames} FROM \`${tableName}\`;`, ); // migrate data sqlStatements.push( new SQLiteDropTableConvertor().convert({ type: 'drop_table', - tableName: `__old__generate_${tableName}`, + tableName: tableName, schema: '', }), ); + // rename table + sqlStatements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + + sqlStatements.push(`PRAGMA foreign_keys=ON;`); + return sqlStatements; } } @@ -3344,25 +3153,17 @@ class LibSQLRecreateTableConvertor extends Convertor { const { tableName, columns, compositePKs, referenceData } = statement; const columnNames = columns.map((it) => `"${it.name}"`).join(', '); + const newTableName = `__new_${tableName}`; const sqlStatements: string[] = []; - // rename table - sqlStatements.push( - new SqliteRenameTableConvertor().convert({ - fromSchema: '', - tableNameFrom: tableName, - tableNameTo: `__old__generate_${tableName}`, - toSchema: '', - type: 'rename_table', - }), - ); + sqlStatements.push(`PRAGMA foreign_keys=OFF;`); // create new table sqlStatements.push( new SQLiteCreateTableConvertor().convert({ type: 'sqlite_create_table', - tableName, + tableName: newTableName, columns, referenceData, compositePKs, @@ -3371,18 +3172,31 @@ class LibSQLRecreateTableConvertor extends Convertor { // migrate data sqlStatements.push( - `INSERT INTO \`${tableName}\`(${columnNames}) SELECT ${columnNames} FROM \`__old__generate_${tableName}\`;`, + `INSERT INTO \`${newTableName}\`(${columnNames}) SELECT ${columnNames} FROM \`${tableName}\`;`, ); // migrate data sqlStatements.push( new SQLiteDropTableConvertor().convert({ type: 'drop_table', - tableName: `__old__generate_${tableName}`, + tableName: tableName, schema: '', }), ); + // rename table + sqlStatements.push( + new SqliteRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + + sqlStatements.push(`PRAGMA foreign_keys=ON;`); + return sqlStatements; } } @@ -3493,33 +3307,12 @@ convertors.push(new PgAlterTableSetSchemaConvertor()); convertors.push(new PgAlterTableSetNewSchemaConvertor()); convertors.push(new PgAlterTableRemoveFromSchemaConvertor()); -// Unhandled sqlite queries, so they will appear last -convertors.push(new SQLiteAlterTableAlterColumnSetTypeConvertor()); -convertors.push(new SqliteAlterForeignKeyConvertor()); -convertors.push(new SqliteDeleteForeignKeyConvertor()); -convertors.push(new LibSQLDeleteForeignKeyConvertor()); -convertors.push(new SqliteCreateForeignKeyConvertor()); convertors.push(new LibSQLCreateForeignKeyConvertor()); -convertors.push(new SQLiteAlterTableAddUniqueConstraintConvertor()); -convertors.push(new SQLiteAlterTableDropUniqueConstraintConvertor()); - convertors.push(new PgAlterTableAlterColumnDropGenerated()); convertors.push(new PgAlterTableAlterColumnSetGenerated()); convertors.push(new PgAlterTableAlterColumnAlterGenerated()); -convertors.push(new SqliteAlterTableAlterColumnSetNotNullConvertor()); -convertors.push(new SqliteAlterTableAlterColumnDropNotNullConvertor()); -convertors.push(new SqliteAlterTableAlterColumnSetDefaultConvertor()); -convertors.push(new SqliteAlterTableAlterColumnDropDefaultConvertor()); - -convertors.push(new SqliteAlterTableAlterColumnSetAutoincrementConvertor()); -convertors.push(new SqliteAlterTableAlterColumnDropAutoincrementConvertor()); - -convertors.push(new SqliteAlterTableCreateCompositePrimaryKeyConvertor()); -convertors.push(new SqliteAlterTableDeleteCompositePrimaryKeyConvertor()); -convertors.push(new SqliteAlterTableAlterCompositePrimaryKeyConvertor()); - convertors.push(new PgAlterTableCreateCompositePrimaryKeyConvertor()); convertors.push(new PgAlterTableDeleteCompositePrimaryKeyConvertor()); convertors.push(new PgAlterTableAlterCompositePrimaryKeyConvertor()); diff --git a/drizzle-kit/src/statementCombiner.ts b/drizzle-kit/src/statementCombiner.ts index 549457cf1..2f7b6ddbe 100644 --- a/drizzle-kit/src/statementCombiner.ts +++ b/drizzle-kit/src/statementCombiner.ts @@ -122,15 +122,15 @@ export const libSQLCombineStatements = ( ) { const { tableName, columnName, columnPk } = statement; - const columnIsPartOfUniqueIndex = Object.values( - json2.tables[tableName].indexes, - ).some((it) => { - const unsquashIndex = SQLiteSquasher.unsquashIdx(it); + // const columnIsPartOfUniqueIndex = Object.values( + // json2.tables[tableName].indexes, + // ).some((it) => { + // const unsquashIndex = SQLiteSquasher.unsquashIdx(it); - return ( - unsquashIndex.columns.includes(columnName) && unsquashIndex.isUnique - ); - }); + // return ( + // unsquashIndex.columns.includes(columnName) && unsquashIndex.isUnique + // ); + // }); const columnIsPartOfForeignKey = Object.values( json2.tables[tableName].foreignKeys, @@ -145,14 +145,14 @@ export const libSQLCombineStatements = ( const statementsForTable = newStatements[tableName]; if ( - !statementsForTable && (columnIsPartOfUniqueIndex || columnIsPartOfForeignKey || columnPk) + !statementsForTable && (columnIsPartOfForeignKey || columnPk) ) { newStatements[tableName] = prepareLibSQLRecreateTable(json2.tables[tableName], action); continue; } if ( - statementsForTable && (columnIsPartOfUniqueIndex || columnIsPartOfForeignKey || columnPk) + statementsForTable && (columnIsPartOfForeignKey || columnPk) ) { if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); @@ -167,7 +167,7 @@ export const libSQLCombineStatements = ( continue; } if ( - statementsForTable && !(columnIsPartOfUniqueIndex || columnIsPartOfForeignKey || columnPk) + statementsForTable && !(columnIsPartOfForeignKey || columnPk) ) { if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { newStatements[tableName].push(statement); @@ -192,7 +192,7 @@ export const libSQLCombineStatements = ( if (!statementsForTable) { newStatements[tableName] = statement.isMulticolumn ? prepareLibSQLRecreateTable(json2.tables[tableName], action) - : newStatements[tableName] = [statement]; + : [statement]; continue; } diff --git a/drizzle-kit/tests/libsql-statements.test.ts b/drizzle-kit/tests/libsql-statements.test.ts index 927411e44..e18e41950 100644 --- a/drizzle-kit/tests/libsql-statements.test.ts +++ b/drizzle-kit/tests/libsql-statements.test.ts @@ -1,4 +1,5 @@ -import { foreignKey, int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import { sql } from 'drizzle-orm'; +import { foreignKey, index, int, integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'; import { JsonRecreateTableStatement } from 'src/jsonStatements'; import { expect, test } from 'vitest'; import { diffTestSchemasLibSQL } from './schemaDiffer'; @@ -429,20 +430,20 @@ test('drop foriegn key', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(4); - expect(sqlStatements[0]).toBe( - `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, - ); - expect(sqlStatements[1]).toBe(`CREATE TABLE \`users\` ( + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`table_id\` integer );\n`); expect(sqlStatements[2]).toBe( - `INSERT INTO \`users\`("id", "table_id") SELECT "id", "table_id" FROM \`__old__generate_users\`;`, + `INSERT INTO \`__new_users\`("id", "table_id") SELECT "id", "table_id" FROM \`users\`;`, ); - expect(sqlStatements[3]).toBe( - `DROP TABLE \`__old__generate_users\`;`, + expect(sqlStatements[3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements[4]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); }); test('alter foriegn key', async (t) => { @@ -500,12 +501,8 @@ test('alter foriegn key', async (t) => { compositePKs: [], referenceData: [ { - columnsFrom: [ - 'table_id', - ], - columnsTo: [ - 'id', - ], + columnsFrom: ['table_id'], + columnsTo: ['id'], name: 'users_table_id_table2_id_fk', onDelete: 'no action', onUpdate: 'no action', @@ -518,21 +515,23 @@ test('alter foriegn key', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(4); - expect(sqlStatements[0]).toBe( - 'ALTER TABLE `users` RENAME TO `__old__generate_users`;', - ); - expect(sqlStatements[1]).toBe(`CREATE TABLE \`users\` ( + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`table_id\` integer, \tFOREIGN KEY (\`table_id\`) REFERENCES \`table2\`(\`id\`) ON UPDATE no action ON DELETE no action );\n`); expect(sqlStatements[2]).toBe( - `INSERT INTO \`users\`("id", "table_id") SELECT "id", "table_id" FROM \`__old__generate_users\`;`, + `INSERT INTO \`__new_users\`("id", "table_id") SELECT "id", "table_id" FROM \`users\`;`, ); expect(sqlStatements[3]).toBe( - `DROP TABLE \`__old__generate_users\`;`, + 'DROP TABLE `users`;', + ); + expect(sqlStatements[4]).toBe( + 'ALTER TABLE `__new_users` RENAME TO `users`;', ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); }); test('add foriegn key for multiple columns', async (t) => { @@ -605,14 +604,8 @@ test('add foriegn key for multiple columns', async (t) => { compositePKs: [], referenceData: [ { - columnsFrom: [ - 'column', - 'column_1', - ], - columnsTo: [ - 'age', - 'age_1', - ], + columnsFrom: ['column', 'column_1'], + columnsTo: ['age', 'age_1'], name: 'users_column_column_1_table_age_age_1_fk', onDelete: 'no action', onUpdate: 'no action', @@ -625,25 +618,24 @@ test('add foriegn key for multiple columns', async (t) => { uniqueConstraints: [], } as JsonRecreateTableStatement); - expect(sqlStatements.length).toBe(4); - expect(sqlStatements[0]).toBe( - `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, - ); + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); expect(sqlStatements[1]).toBe( - `CREATE TABLE \`users\` ( + `CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`column\` integer, \t\`column_1\` integer, \tFOREIGN KEY (\`column\`,\`column_1\`) REFERENCES \`table\`(\`age\`,\`age_1\`) ON UPDATE no action ON DELETE no action -); -`, +);\n`, ); expect(sqlStatements[2]).toBe( - `INSERT INTO \`users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`__old__generate_users\`;`, + `INSERT INTO \`__new_users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`users\`;`, ); - expect(sqlStatements[3]).toBe( - `DROP TABLE \`__old__generate_users\`;`, + expect(sqlStatements[3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements[4]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); }); test('drop foriegn key for multiple columns', async (t) => { @@ -720,59 +712,106 @@ test('drop foriegn key for multiple columns', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(4); - expect(sqlStatements[0]).toBe( - `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, - ); + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); expect(sqlStatements[1]).toBe( - `CREATE TABLE \`users\` ( + `CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`column\` integer, \t\`column_1\` integer -); -`, +);\n`, ); expect(sqlStatements[2]).toBe( - `INSERT INTO \`users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`__old__generate_users\`;`, + `INSERT INTO \`__new_users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`users\`;`, ); - expect(sqlStatements[3]).toBe( - `DROP TABLE \`__old__generate_users\`;`, + expect(sqlStatements[3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements[4]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); }); -test('drop foriegn key for multiple columns', async (t) => { - const tableRef = sqliteTable('table', { - id: int('id').primaryKey({ autoIncrement: true }), - age: int('age'), - age1: int('age_1'), +test('alter column drop generated', async (t) => { + const from = { + users: sqliteTable('table', { + id: int('id').primaryKey().notNull(), + name: text('name').generatedAlwaysAs('drizzle is the best').notNull(), + }), + }; + + const to = { + users: sqliteTable('table', { + id: int('id').primaryKey().notNull(), + name: text('name').notNull(), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + from, + to, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columnAutoIncrement: false, + columnDefault: undefined, + columnGenerated: undefined, + columnName: 'name', + columnNotNull: true, + columnOnUpdate: undefined, + columnPk: false, + newDataType: 'text', + schema: '', + tableName: 'table', + type: 'alter_table_alter_column_drop_generated', }); + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe(`ALTER TABLE \`table\` DROP COLUMN \`name\`;`); + expect(sqlStatements[1]).toBe( + `ALTER TABLE \`table\` ADD \`name\` text NOT NULL;`, + ); +}); + +test('recreate table with nested references', async (t) => { + let users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: integer('age'), + }); + let subscriptions = sqliteTable('subscriptions', { + id: int('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').references(() => users.id), + customerId: text('customer_id'), + }); const schema1 = { - users: sqliteTable( - 'users', - { - id: int('id').primaryKey({ autoIncrement: true }), - column: int('column'), - column1: int('column_1'), - }, - (table) => ({ - foreignKey: foreignKey({ - columns: [table.column, table.column1], - foreignColumns: [tableRef.age, tableRef.age1], - }), - }), - ), - tableRef, + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references( + () => subscriptions.id, + ), + }), }; + users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + }); const schema2 = { - users: sqliteTable('users', { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { id: int('id').primaryKey({ autoIncrement: true }), - column: int('column'), - column1: int('column_1'), + subscriptionId: text('subscription_id').references( + () => subscriptions.id, + ), }), - tableRef, }; + const { statements, sqlStatements } = await diffTestSchemasLibSQL( schema1, schema2, @@ -783,7 +822,7 @@ test('drop foriegn key for multiple columns', async (t) => { expect(statements[0]).toStrictEqual({ columns: [ { - autoincrement: true, + autoincrement: false, generated: undefined, name: 'id', notNull: true, @@ -793,15 +832,15 @@ test('drop foriegn key for multiple columns', async (t) => { { autoincrement: false, generated: undefined, - name: 'column', + name: 'name', notNull: false, primaryKey: false, - type: 'integer', + type: 'text', }, { autoincrement: false, generated: undefined, - name: 'column_1', + name: 'age', notNull: false, primaryKey: false, type: 'integer', @@ -814,63 +853,131 @@ test('drop foriegn key for multiple columns', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(4); + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer +);\n`); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`, + ); + expect(sqlStatements[3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements[4]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, + ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); +}); + +test('set not null with index', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }, (table) => ({ + someIndex: index('users_name_index').on(table.name), + })), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }, (table) => ({ + someIndex: index('users_name_index').on(table.name), + })), + }; + + const { statements, sqlStatements } = await diffTestSchemasLibSQL( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_set_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(3); expect(sqlStatements[0]).toBe( - `ALTER TABLE \`users\` RENAME TO \`__old__generate_users\`;`, + `DROP INDEX IF EXISTS "users_name_index";`, ); expect(sqlStatements[1]).toBe( - `CREATE TABLE \`users\` ( -\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, -\t\`column\` integer, -\t\`column_1\` integer -); -`, + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text NOT NULL;`, ); expect(sqlStatements[2]).toBe( - `INSERT INTO \`users\`("id", "column", "column_1") SELECT "id", "column", "column_1" FROM \`__old__generate_users\`;`, - ); - expect(sqlStatements[3]).toBe( - `DROP TABLE \`__old__generate_users\`;`, + `CREATE INDEX \`users_name_index\` ON \`users\` (\`name\`);`, ); }); -test('alter column drop generated', async (t) => { - const from = { - users: sqliteTable('table', { - id: int('id').primaryKey().notNull(), - name: text('name').generatedAlwaysAs('drizzle is the best').notNull(), - }), +test('drop not null with two indexes', async (t) => { + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + age: int('age').notNull(), + }, (table) => ({ + someUniqeIndex: uniqueIndex('users_name_unique').on(table.name), + someIndex: index('users_age_index').on(table.age), + })), }; - const to = { - users: sqliteTable('table', { - id: int('id').primaryKey().notNull(), - name: text('name').notNull(), - }), + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: int('age').notNull(), + }, (table) => ({ + someUniqeIndex: uniqueIndex('users_name_unique').on(table.name), + someIndex: index('users_age_index').on(table.age), + })), }; const { statements, sqlStatements } = await diffTestSchemasLibSQL( - from, - to, + schema1, + schema2, [], ); expect(statements.length).toBe(1); expect(statements[0]).toStrictEqual({ - columnAutoIncrement: false, - columnDefault: undefined, - columnGenerated: undefined, + type: 'alter_table_alter_column_drop_notnull', + tableName: 'users', columnName: 'name', - columnNotNull: true, + schema: '', + newDataType: 'text', + columnDefault: undefined, columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, columnPk: false, - newDataType: 'text', - schema: '', - tableName: 'table', - type: 'alter_table_alter_column_drop_generated', }); - expect(sqlStatements.length).toBe(2); - expect(sqlStatements[0]).toBe(`ALTER TABLE \`table\` DROP COLUMN \`name\`;`); - expect(sqlStatements[1]).toBe(`ALTER TABLE \`table\` ADD \`name\` text NOT NULL;`); + expect(sqlStatements.length).toBe(5); + expect(sqlStatements[0]).toBe( + `DROP INDEX IF EXISTS "users_name_unique";`, + ); + expect(sqlStatements[1]).toBe( + `DROP INDEX IF EXISTS "users_age_index";`, + ); + expect(sqlStatements[2]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`, + ); + expect(sqlStatements[3]).toBe( + `CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`, + ); + expect(sqlStatements[4]).toBe( + `CREATE INDEX \`users_age_index\` ON \`users\` (\`age\`);`, + ); }); diff --git a/drizzle-kit/tests/push/libsql.test.ts b/drizzle-kit/tests/push/libsql.test.ts index 205f31f0b..3506cef32 100644 --- a/drizzle-kit/tests/push/libsql.test.ts +++ b/drizzle-kit/tests/push/libsql.test.ts @@ -1,9 +1,11 @@ import { createClient } from '@libsql/client'; import chalk from 'chalk'; +import { sql } from 'drizzle-orm'; import { blob, foreignKey, getTableConfig, + index, int, integer, numeric, @@ -390,15 +392,14 @@ test('drop autoincrement. drop column with data', async (t) => { expect(sqlStatements.length).toBe(4); expect(sqlStatements[0]).toBe( - `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, - ); - expect(sqlStatements[1]).toBe( - `CREATE TABLE \`companies\` ( + `CREATE TABLE \`__new_companies\` ( \t\`id\` integer PRIMARY KEY NOT NULL );\n`, ); - expect(sqlStatements[2]).toBe( - `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + expect(sqlStatements[1]).toBe(`INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`); + expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`); + expect(sqlStatements[3]).toBe( + `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`, ); expect(columnsToRemove!.length).toBe(1); @@ -426,7 +427,7 @@ test('change autoincrement. table is part of foreign key', async (t) => { const users1 = sqliteTable('users', { id: integer('id').primaryKey({ autoIncrement: true }), name: text('name').unique(), - companyId: text('company_id').references(() => companies1.id), + companyId: integer('company_id').references(() => companies1.id), }); const schema1 = { companies: companies1, @@ -439,7 +440,7 @@ test('change autoincrement. table is part of foreign key', async (t) => { const users2 = sqliteTable('users', { id: integer('id').primaryKey({ autoIncrement: true }), name: text('name').unique(), - companyId: text('company_id').references(() => companies1.id), + companyId: integer('company_id').references(() => companies2.id), }); const schema2 = { companies: companies2, @@ -451,8 +452,8 @@ test('change autoincrement. table is part of foreign key', async (t) => { const seedStatements = [ `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ("drizzle");`, `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ("turso");`, - `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ("1");`, - `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ("2");`, + `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES (1);`, + `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES (2);`, ]; const { @@ -491,39 +492,21 @@ test('change autoincrement. table is part of foreign key', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(9); - expect(sqlStatements[0]).toBe( - `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, - ); + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); expect(sqlStatements[1]).toBe( - `CREATE TABLE \`companies\` ( + `CREATE TABLE \`__new_companies\` ( \t\`id\` integer PRIMARY KEY NOT NULL -); -`, +);\n`, ); expect(sqlStatements[2]).toBe( - `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + `INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`, ); - expect(sqlStatements[3]).toBe(`DROP TABLE \`__old_push_companies\`;`); + expect(sqlStatements[3]).toBe(`DROP TABLE \`companies\`;`); expect(sqlStatements[4]).toBe( - `ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`, - ); - expect(sqlStatements[5]).toBe( - `CREATE TABLE \`users\` ( -\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, -\t\`name\` text, -\t\`company_id\` text, -\tFOREIGN KEY (\`company_id\`) REFERENCES \`companies\`(\`id\`) ON UPDATE no action ON DELETE no action -); -`, - ); - expect(sqlStatements[6]).toBe( - `INSERT INTO \`users\`("id", "name", "company_id") SELECT ("id", "name", "company_id") FROM \`__old_push_users\`;`, - ); - expect(sqlStatements[7]).toBe(`DROP TABLE \`__old_push_users\`;`); - expect(sqlStatements[8]).toBe( - `CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`, + `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`, ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); @@ -742,3 +725,386 @@ test('drop table with data', async (t) => { expect(tablesToRemove![0]).toBe('users'); expect(tablesToTruncate!.length).toBe(0); }); + +test('recreate table with nested references', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + let users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: integer('age'), + }); + let subscriptions = sqliteTable('subscriptions', { + id: int('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').references(() => users.id), + customerId: text('customer_id'), + }); + const schema1 = { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references( + () => subscriptions.id, + ), + }), + }; + + users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + }); + const schema2 = { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references( + () => subscriptions.id, + ), + }), + }; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL(turso, schema1, schema2, []); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'name', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(6); + expect(sqlStatements[0]).toBe('PRAGMA foreign_keys=OFF;'); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer +);\n`); + expect(sqlStatements![2]).toBe( + `INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`, + ); + expect(sqlStatements![3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![4]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, + ); + expect(sqlStatements[5]).toBe('PRAGMA foreign_keys=ON;'); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('recreate table with added column not null and without default', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: integer('age'), + }), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + newColumn: text('new_column').notNull(), + }), + }; + + const seedStatements = [ + `INSERT INTO \`users\` ("name", "age") VALUES ('drizzle', 12)`, + `INSERT INTO \`users\` ("name", "age") VALUES ('turso', 12)`, + ]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'name', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + name: 'new_column', + notNull: true, + generated: undefined, + primaryKey: false, + type: 'text', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(4); + expect(sqlStatements[0]).toBe('DELETE FROM \`users\`;'); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer, +\t\`new_column\` text NOT NULL +);\n`); + expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![3]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to add not-null ${ + chalk.underline('new_column') + } column without default value to table, which contains 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(1); + expect(tablesToTruncate![0]).toBe('users'); +}); + +test('set not null with index', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + }, (table) => ({ + someIndex: index('users_name_index').on(table.name), + })), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + }, (table) => ({ + someIndex: index('users_name_index').on(table.name), + })), + }; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + ); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + columnAutoIncrement: false, + columnDefault: undefined, + columnName: 'name', + columnNotNull: true, + columnOnUpdate: undefined, + columnPk: false, + newDataType: 'text', + schema: '', + tableName: 'users', + type: 'alter_table_alter_column_set_notnull', + }); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_set_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(3); + expect(sqlStatements[0]).toBe( + `DROP INDEX IF EXISTS "users_name_index";`, + ); + expect(sqlStatements[1]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text NOT NULL;`, + ); + expect(sqlStatements[2]).toBe( + `CREATE INDEX \`users_name_index\` ON \`users\` (\`name\`);`, + ); + expect(columnsToRemove!.length).toBe(0), expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('drop not null with two indexes', async (t) => { + const turso = createClient({ + url: ':memory:', + }); + + const schema1 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + age: int('age').notNull(), + }, (table) => ({ + someUniqeIndex: uniqueIndex('users_name_unique').on(table.name), + someIndex: index('users_age_index').on(table.age), + })), + }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: int('age').notNull(), + }, (table) => ({ + someUniqeIndex: uniqueIndex('users_name_unique').on(table.name), + someIndex: index('users_age_index').on(table.age), + })), + }; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushLibSQL( + turso, + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_alter_column_drop_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + }); + + expect(sqlStatements.length).toBe(5); + expect(sqlStatements[0]).toBe( + `DROP INDEX IF EXISTS "users_name_unique";`, + ); + expect(sqlStatements[1]).toBe( + `DROP INDEX IF EXISTS "users_age_index";`, + ); + expect(sqlStatements[2]).toBe( + `ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`, + ); + expect(sqlStatements[3]).toBe( + `CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`, + ); + expect(sqlStatements[4]).toBe( + `CREATE INDEX \`users_age_index\` ON \`users\` (\`age\`);`, + ); + expect(columnsToRemove!.length).toBe(0), expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); diff --git a/drizzle-kit/tests/push/sqlite.test.ts b/drizzle-kit/tests/push/sqlite.test.ts index d0c3cb05e..e52559256 100644 --- a/drizzle-kit/tests/push/sqlite.test.ts +++ b/drizzle-kit/tests/push/sqlite.test.ts @@ -382,15 +382,133 @@ test('drop autoincrement. drop column with data', async (t) => { expect(sqlStatements.length).toBe(4); expect(sqlStatements[0]).toBe( - `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, + `CREATE TABLE \`__new_companies\` ( +\t\`id\` integer PRIMARY KEY NOT NULL +);\n`, ); expect(sqlStatements[1]).toBe( - `CREATE TABLE \`companies\` ( -\t\`id\` integer PRIMARY KEY NOT NULL + `INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`, + ); + expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`); + expect(sqlStatements[3]).toBe( + `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`, + ); + + expect(columnsToRemove!.length).toBe(1); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to delete ${ + chalk.underline( + 'name', + ) + } column in companies table with 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('drop autoincrement. drop column with data with pragma off', async (t) => { + const client = new Database(':memory:'); + + client.exec('PRAGMA foreign_keys=OFF;'); + + const users = sqliteTable('users', { + id: integer('id').primaryKey({ autoIncrement: true }), + }); + const schema1 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: true }), + name: text('name'), + user_id: integer('user_id').references(() => users.id), + }), + }; + + const schema2 = { + companies: sqliteTable('companies', { + id: integer('id').primaryKey({ autoIncrement: false }), + user_id: integer('user_id').references(() => users.id), + }), + }; + + const table = getTableConfig(schema1.companies); + const seedStatements = [ + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, 'drizzle');`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, 'turso');`, + ]; + + const { + sqlStatements, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'recreate_table', + tableName: 'companies', + columns: [ + { + name: 'id', + type: 'integer', + autoincrement: false, + notNull: true, + primaryKey: true, + generated: undefined, + }, + { + name: 'user_id', + type: 'integer', + autoincrement: false, + notNull: false, + primaryKey: false, + generated: undefined, + }, + ], + compositePKs: [], + referenceData: [ + { + columnsFrom: [ + 'user_id', + ], + columnsTo: [ + 'id', + ], + name: '', + onDelete: 'no action', + onUpdate: 'no action', + tableFrom: 'companies', + tableTo: 'users', + }, + ], + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_companies\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`user_id\` integer, +\tFOREIGN KEY (\`user_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE no action );\n`, ); - expect(sqlStatements[2]).toBe( - `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + expect(sqlStatements[1]).toBe( + `INSERT INTO \`__new_companies\`("id", "user_id") SELECT "id", "user_id" FROM \`companies\`;`, + ); + expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`); + expect(sqlStatements[3]).toBe( + `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`, ); expect(columnsToRemove!.length).toBe(1); @@ -407,7 +525,7 @@ test('drop autoincrement. drop column with data', async (t) => { expect(tablesToTruncate!.length).toBe(0); }); -test('change autoincrement. table is part of foreign key', async (t) => { +test('change autoincrement. other table references current', async (t) => { const client = new Database(':memory:'); const companies1 = sqliteTable('companies', { @@ -481,39 +599,21 @@ test('change autoincrement. table is part of foreign key', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(9); - expect(sqlStatements[0]).toBe( - `ALTER TABLE \`companies\` RENAME TO \`__old_push_companies\`;`, - ); + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); expect(sqlStatements[1]).toBe( - `CREATE TABLE \`companies\` ( + `CREATE TABLE \`__new_companies\` ( \t\`id\` integer PRIMARY KEY NOT NULL -); -`, +);\n`, ); expect(sqlStatements[2]).toBe( - `INSERT INTO \`companies\`("id") SELECT ("id") FROM \`__old_push_companies\`;`, + `INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`, ); - expect(sqlStatements[3]).toBe(`DROP TABLE \`__old_push_companies\`;`); + expect(sqlStatements[3]).toBe(`DROP TABLE \`companies\`;`); expect(sqlStatements[4]).toBe( - `ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`, - ); - expect(sqlStatements[5]).toBe( - `CREATE TABLE \`users\` ( -\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, -\t\`name\` text, -\t\`company_id\` text, -\tFOREIGN KEY (\`company_id\`) REFERENCES \`companies\`(\`id\`) ON UPDATE no action ON DELETE no action -); -`, - ); - expect(sqlStatements[6]).toBe( - `INSERT INTO \`users\`("id", "name", "company_id") SELECT ("id", "name", "company_id") FROM \`__old_push_users\`;`, - ); - expect(sqlStatements[7]).toBe(`DROP TABLE \`__old_push_users\`;`); - expect(sqlStatements[8]).toBe( - `CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`, + `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`, ); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); @@ -586,14 +686,11 @@ test('drop not null, add not null', async (t) => { id: int('id').primaryKey({ autoIncrement: true }), name: text('name').notNull(), }), - posts: sqliteTable( - 'posts', - { - id: int('id').primaryKey({ autoIncrement: true }), - name: text('name'), - userId: int('user_id'), - }, - ), + posts: sqliteTable('posts', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + userId: int('user_id'), + }), }; const schema2 = { @@ -601,14 +698,11 @@ test('drop not null, add not null', async (t) => { id: int('id').primaryKey({ autoIncrement: true }), name: text('name'), }), - posts: sqliteTable( - 'posts', - { - id: int('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - userId: int('user_id'), - }, - ), + posts: sqliteTable('posts', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name').notNull(), + userId: int('user_id'), + }), }; const { statements, @@ -618,12 +712,7 @@ test('drop not null, add not null', async (t) => { shouldAskForApprove, tablesToRemove, tablesToTruncate, - } = await diffTestSchemasPushSqlite( - client, - schema1, - schema2, - [], - ); + } = await diffTestSchemasPushSqlite(client, schema1, schema2, []); expect(statements!.length).toBe(2); expect(statements![0]).toStrictEqual({ @@ -685,27 +774,31 @@ test('drop not null, add not null', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements!.length).toBe(8); - expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`); - expect(sqlStatements![1]).toBe(`CREATE TABLE \`users\` ( + expect(sqlStatements.length).toBe(8); + expect(sqlStatements[0]).toBe(`CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`name\` text );\n`); - expect(sqlStatements![2]).toBe( - `INSERT INTO \`users\`("id", "name") SELECT ("id", "name") FROM \`__old_push_users\`;`, + expect(sqlStatements[1]).toBe( + `INSERT INTO \`__new_users\`("id", "name") SELECT "id", "name" FROM \`users\`;`, + ); + expect(sqlStatements[2]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements[3]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); - expect(sqlStatements![3]).toBe(`DROP TABLE \`__old_push_users\`;`); - expect(sqlStatements![4]).toBe(`ALTER TABLE \`posts\` RENAME TO \`__old_push_posts\`;`); - expect(sqlStatements![5]).toBe(`CREATE TABLE \`posts\` ( + expect(sqlStatements![4]).toBe(`CREATE TABLE \`__new_posts\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`name\` text NOT NULL, \t\`user_id\` integer );\n`); - expect(sqlStatements![6]).toBe( - `INSERT INTO \`posts\`("id", "name", "user_id") SELECT ("id", "name", "user_id") FROM \`__old_push_posts\`;`, + expect(sqlStatements![5]).toBe( + `INSERT INTO \`__new_posts\`("id", "name", "user_id") SELECT "id", "name", "user_id" FROM \`posts\`;`, + ); + expect(sqlStatements![6]).toBe(`DROP TABLE \`posts\`;`); + expect(sqlStatements![7]).toBe( + `ALTER TABLE \`__new_posts\` RENAME TO \`posts\`;`, ); - expect(sqlStatements![7]).toBe(`DROP TABLE \`__old_push_posts\`;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); @@ -738,12 +831,9 @@ test('rename table and change data type', async (t) => { shouldAskForApprove, tablesToRemove, tablesToTruncate, - } = await diffTestSchemasPushSqlite( - client, - schema1, - schema2, - ['public.old_users->public.new_users'], - ); + } = await diffTestSchemasPushSqlite(client, schema1, schema2, [ + 'public.old_users->public.new_users', + ]); expect(statements!.length).toBe(2); expect(statements![0]).toStrictEqual({ @@ -780,16 +870,20 @@ test('rename table and change data type', async (t) => { }); expect(sqlStatements!.length).toBe(5); - expect(sqlStatements![0]).toBe(`ALTER TABLE \`old_users\` RENAME TO \`new_users\`;`); - expect(sqlStatements![1]).toBe(`ALTER TABLE \`new_users\` RENAME TO \`__old_push_new_users\`;`); - expect(sqlStatements![2]).toBe(`CREATE TABLE \`new_users\` ( + expect(sqlStatements![0]).toBe( + `ALTER TABLE \`old_users\` RENAME TO \`new_users\`;`, + ); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`age\` integer );\n`); - expect(sqlStatements![3]).toBe( - `INSERT INTO \`new_users\`("id", "age") SELECT ("id", "age") FROM \`__old_push_new_users\`;`, + expect(sqlStatements![2]).toBe( + `INSERT INTO \`__new_new_users\`("id", "age") SELECT "id", "age" FROM \`new_users\`;`, + ); + expect(sqlStatements![3]).toBe(`DROP TABLE \`new_users\`;`); + expect(sqlStatements![4]).toBe( + `ALTER TABLE \`__new_new_users\` RENAME TO \`new_users\`;`, ); - expect(sqlStatements![4]).toBe(`DROP TABLE \`__old_push_new_users\`;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); @@ -798,18 +892,18 @@ test('rename table and change data type', async (t) => { expect(tablesToTruncate!.length).toBe(0); }); -test('rename table and change data type', async (t) => { +test('rename column and change data type', async (t) => { const client = new Database(':memory:'); const schema1 = { - users: sqliteTable('old_users', { + users: sqliteTable('users', { id: int('id').primaryKey({ autoIncrement: true }), - age: text('age'), + name: text('name'), }), }; const schema2 = { - users: sqliteTable('new_users', { + users: sqliteTable('users', { id: int('id').primaryKey({ autoIncrement: true }), age: integer('age'), }), @@ -822,22 +916,12 @@ test('rename table and change data type', async (t) => { shouldAskForApprove, tablesToRemove, tablesToTruncate, - } = await diffTestSchemasPushSqlite( - client, - schema1, - schema2, - ['public.old_users->public.new_users'], - ); + } = await diffTestSchemasPushSqlite(client, schema1, schema2, [ + 'public.users.name->public.users.age', + ]); - expect(statements!.length).toBe(2); + expect(statements!.length).toBe(1); expect(statements![0]).toStrictEqual({ - fromSchema: undefined, - tableNameFrom: 'old_users', - tableNameTo: 'new_users', - toSchema: undefined, - type: 'rename_table', - }); - expect(statements![1]).toStrictEqual({ columns: [ { autoincrement: true, @@ -858,22 +942,23 @@ test('rename table and change data type', async (t) => { ], compositePKs: [], referenceData: [], - tableName: 'new_users', + tableName: 'users', type: 'recreate_table', uniqueConstraints: [], }); - expect(sqlStatements!.length).toBe(5); - expect(sqlStatements![0]).toBe(`ALTER TABLE \`old_users\` RENAME TO \`new_users\`;`); - expect(sqlStatements![1]).toBe(`ALTER TABLE \`new_users\` RENAME TO \`__old_push_new_users\`;`); - expect(sqlStatements![2]).toBe(`CREATE TABLE \`new_users\` ( + expect(sqlStatements!.length).toBe(4); + expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, \t\`age\` integer );\n`); + expect(sqlStatements![1]).toBe( + `INSERT INTO \`__new_users\`("id", "age") SELECT "id", "age" FROM \`users\`;`, + ); + expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`); expect(sqlStatements![3]).toBe( - `INSERT INTO \`new_users\`("id", "age") SELECT ("id", "age") FROM \`__old_push_new_users\`;`, + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); - expect(sqlStatements![4]).toBe(`DROP TABLE \`__old_push_new_users\`;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); @@ -882,22 +967,247 @@ test('rename table and change data type', async (t) => { expect(tablesToTruncate!.length).toBe(0); }); -test('rename column and change data type', async (t) => { +test('recreate table with nested references', async (t) => { + const client = new Database(':memory:'); + + let users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: integer('age'), + }); + let subscriptions = sqliteTable('subscriptions', { + id: int('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').references(() => users.id), + customerId: text('customer_id'), + }); + const schema1 = { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references( + () => subscriptions.id, + ), + }), + }; + + users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + }); + const schema2 = { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references( + () => subscriptions.id, + ), + }), + }; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite(client, schema1, schema2, [ + 'public.users.name->public.users.age', + ]); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'name', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(6); + expect(sqlStatements[0]).toBe('PRAGMA foreign_keys=OFF;'); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer +);\n`); + expect(sqlStatements![2]).toBe( + `INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`, + ); + expect(sqlStatements![3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![4]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, + ); + expect(sqlStatements[5]).toBe('PRAGMA foreign_keys=ON;'); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); +}); + +test('recreate table with added column not null and without default with data', async (t) => { const client = new Database(':memory:'); const schema1 = { users: sqliteTable('users', { id: int('id').primaryKey({ autoIncrement: true }), name: text('name'), + age: integer('age'), }), }; const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + newColumn: text('new_column').notNull(), + }), + }; + + const seedStatements = [ + `INSERT INTO \`users\` ("name", "age") VALUES ('drizzle', 12)`, + `INSERT INTO \`users\` ("name", "age") VALUES ('turso', 12)`, + ]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSqlite( + client, + schema1, + schema2, + [], + false, + seedStatements, + ); + + expect(statements!.length).toBe(1); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + name: 'id', + notNull: true, + generated: undefined, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + name: 'name', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + name: 'age', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'integer', + }, + { + autoincrement: false, + name: 'new_column', + notNull: true, + generated: undefined, + primaryKey: false, + type: 'text', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements!.length).toBe(4); + expect(sqlStatements[0]).toBe('DELETE FROM \`users\`;'); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer, +\t\`new_column\` text NOT NULL +);\n`); + expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![3]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, + ); + + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about to add not-null ${ + chalk.underline('new_column') + } column without default value to table, which contains 2 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(1); + expect(tablesToTruncate![0]).toBe('users'); +}); + +test('recreate table with added column not null and without default with data', async (t) => { + const client = new Database(':memory:'); + + const schema1 = { users: sqliteTable('users', { id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), age: integer('age'), }), }; + + const schema2 = { + users: sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + newColumn: text('new_column').notNull(), + }), + }; + const { statements, sqlStatements, @@ -910,20 +1220,28 @@ test('rename column and change data type', async (t) => { client, schema1, schema2, - ['public.users.name->public.users.age'], + [], ); expect(statements!.length).toBe(1); expect(statements![0]).toStrictEqual({ columns: [ { - autoincrement: true, + autoincrement: false, name: 'id', notNull: true, generated: undefined, primaryKey: true, type: 'integer', }, + { + autoincrement: false, + name: 'name', + notNull: false, + generated: undefined, + primaryKey: false, + type: 'text', + }, { autoincrement: false, name: 'age', @@ -932,6 +1250,14 @@ test('rename column and change data type', async (t) => { primaryKey: false, type: 'integer', }, + { + autoincrement: false, + name: 'new_column', + notNull: true, + generated: undefined, + primaryKey: false, + type: 'text', + }, ], compositePKs: [], referenceData: [], @@ -941,15 +1267,19 @@ test('rename column and change data type', async (t) => { }); expect(sqlStatements!.length).toBe(4); - expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` RENAME TO \`__old_push_users\`;`); - expect(sqlStatements![1]).toBe(`CREATE TABLE \`users\` ( -\t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, -\t\`age\` integer + expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer, +\t\`new_column\` text NOT NULL );\n`); - expect(sqlStatements![2]).toBe( - `INSERT INTO \`users\`("id", "age") SELECT ("id", "age") FROM \`__old_push_users\`;`, + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_users`("id", "name", "age", "new_column") SELECT "id", "name", "age", "new_column" FROM `users`;', + ); + expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![3]).toBe( + `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); - expect(sqlStatements![3]).toBe(`DROP TABLE \`__old_push_users\`;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); diff --git a/drizzle-kit/tests/sqlite-columns.test.ts b/drizzle-kit/tests/sqlite-columns.test.ts index e71b95e01..7329db7c9 100644 --- a/drizzle-kit/tests/sqlite-columns.test.ts +++ b/drizzle-kit/tests/sqlite-columns.test.ts @@ -8,7 +8,7 @@ import { sqliteTable, text, } from 'drizzle-orm/sqlite-core'; -import { JsonRecreateTableStatement } from 'src/jsonStatements'; +import { JsonCreateIndexStatement, JsonRecreateTableStatement } from 'src/jsonStatements'; import { expect, test } from 'vitest'; import { diffTestSchemasSqlite } from './schemaDiffer'; @@ -774,6 +774,55 @@ test('alter column add default not null', async (t) => { }); }); +test('alter column add default not null with indexes', async (t) => { + const from = { + users: sqliteTable('table', { + name: text('name'), + }, (table) => ({ + someIndex: index('index_name').on(table.name), + })), + }; + + const to = { + users: sqliteTable('table', { + name: text('name').notNull().default('dan'), + }, (table) => ({ + someIndex: index('index_name').on(table.name), + })), + }; + + const { statements, sqlStatements } = await diffTestSchemasSqlite( + from, + to, + [], + ); + + expect(statements.length).toBe(2); + expect(statements[0]).toStrictEqual({ + type: 'recreate_table', + columns: [{ + autoincrement: false, + generated: undefined, + name: 'name', + notNull: true, + primaryKey: false, + type: 'text', + default: "'dan'", + }], + compositePKs: [], + referenceData: [], + tableName: 'table', + uniqueConstraints: [], + }); + expect(statements[1]).toStrictEqual({ + data: 'index_name;name;false;', + schema: '', + tableName: 'table', + type: 'create_index', + internal: undefined, + }); +}); + test('alter column drop default not null', async (t) => { const from = { users: sqliteTable('table', { @@ -851,3 +900,93 @@ test('alter column drop generated', async (t) => { expect(sqlStatements[0]).toBe(`ALTER TABLE \`table\` DROP COLUMN \`name\`;`); expect(sqlStatements[1]).toBe(`ALTER TABLE \`table\` ADD \`name\` text NOT NULL;`); }); + +test('recreate table with nested references', async (t) => { + let users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: true }), + name: text('name'), + age: integer('age'), + }); + let subscriptions = sqliteTable('subscriptions', { + id: int('id').primaryKey({ autoIncrement: true }), + userId: integer('user_id').references(() => users.id), + customerId: text('customer_id'), + }); + const schema1 = { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references(() => subscriptions.id), + }), + }; + + users = sqliteTable('users', { + id: int('id').primaryKey({ autoIncrement: false }), + name: text('name'), + age: integer('age'), + }); + const schema2 = { + users: users, + subscriptions: subscriptions, + subscriptionMetadata: sqliteTable('subscriptions_metadata', { + id: int('id').primaryKey({ autoIncrement: true }), + subscriptionId: text('subscription_id').references(() => subscriptions.id), + }), + }; + + const { statements, sqlStatements } = await diffTestSchemasSqlite( + schema1, + schema2, + [], + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + generated: undefined, + name: 'id', + notNull: true, + primaryKey: true, + type: 'integer', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + generated: undefined, + name: 'age', + notNull: false, + primaryKey: false, + type: 'integer', + }, + ], + compositePKs: [], + referenceData: [], + tableName: 'users', + type: 'recreate_table', + uniqueConstraints: [], + }); + + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` integer PRIMARY KEY NOT NULL, +\t\`name\` text, +\t\`age\` integer +);\n`); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`, + ); + expect(sqlStatements[3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements[4]).toBe(`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); +}); diff --git a/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts index 342e55232..47447decd 100644 --- a/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts +++ b/drizzle-kit/tests/statements-combiner/libsql-statements-combiner.test.ts @@ -1181,27 +1181,17 @@ test(`set new type for primary key, unique and normal column`, async (t) => { columnPk: false, }, { - type: 'recreate_table', + type: 'alter_table_alter_column_set_type', tableName: 'unique', - columns: [ - { - name: 'unique', - type: 'text', - primaryKey: false, - notNull: false, - autoincrement: false, - }, - ], - compositePKs: [], - referenceData: [], - uniqueConstraints: [], - }, - { - data: 'unique_unique_unique;unique;true;', - internal: undefined, + columnName: 'unique', + newDataType: 'text', + oldDataType: 'int', schema: '', - tableName: 'unique', - type: 'create_index', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, }, ]; expect(libSQLCombineStatements(statements, json2)).toStrictEqual( From b0cf5550c6da62c1bb21921026eba4ffd2a8441d Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Thu, 15 Aug 2024 19:04:29 +0300 Subject: [PATCH 20/56] changed package json to prev state --- drizzle-kit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index 8bfdebac4..552c14d0e 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -38,7 +38,7 @@ "build": "rm -rf ./dist && tsx build.ts && cp package.json dist/ && attw --pack dist", "build:dev": "rm -rf ./dist && tsx build.dev.ts && tsc -p tsconfig.cli-types.json && chmod +x ./dist/index.cjs", "pack": "cp package.json README.md dist/ && (cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz", - "tsc": "tsc -p tsconfig.cli-types.json", + "tsc": "tsc -p tsconfig.build.json", "publish": "npm publish package.tgz" }, "dependencies": { From 8d62f90148532c18de33c426d0a7e9d3504bb8ad Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Fri, 16 Aug 2024 12:17:23 +0300 Subject: [PATCH 21/56] updated tests --- drizzle-kit/tests/push/libsql.test.ts | 58 ------------------------ drizzle-kit/tests/push/sqlite.test.ts | 1 + drizzle-kit/tests/sqlite-columns.test.ts | 23 ++++++++++ 3 files changed, 24 insertions(+), 58 deletions(-) diff --git a/drizzle-kit/tests/push/libsql.test.ts b/drizzle-kit/tests/push/libsql.test.ts index 3506cef32..482caa995 100644 --- a/drizzle-kit/tests/push/libsql.test.ts +++ b/drizzle-kit/tests/push/libsql.test.ts @@ -515,64 +515,6 @@ test('change autoincrement. table is part of foreign key', async (t) => { expect(tablesToTruncate!.length).toBe(0); }); -test('create table with custom name references', async (t) => { - const turso = createClient({ - url: ':memory:', - }); - - const users = sqliteTable('users', { - id: int('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - }); - - const schema1 = { - users, - posts: sqliteTable( - 'posts', - { - id: int('id').primaryKey({ autoIncrement: true }), - name: text('name'), - userId: int('user_id'), - }, - (t) => ({ - fk: foreignKey({ - columns: [t.id], - foreignColumns: [users.id], - name: 'custom_name_fk', - }), - }), - ), - }; - - const schema2 = { - users, - posts: sqliteTable( - 'posts', - { - id: int('id').primaryKey({ autoIncrement: true }), - name: text('name'), - userId: int('user_id'), - }, - (t) => ({ - fk: foreignKey({ - columns: [t.id], - foreignColumns: [users.id], - name: 'custom_name_fk', - }), - }), - ), - }; - - const { sqlStatements } = await diffTestSchemasPushLibSQL( - turso, - schema1, - schema2, - [], - ); - - expect(sqlStatements!.length).toBe(0); -}); - test('drop not null, add not null', async (t) => { const turso = createClient({ url: ':memory:', diff --git a/drizzle-kit/tests/push/sqlite.test.ts b/drizzle-kit/tests/push/sqlite.test.ts index e52559256..aea5cd379 100644 --- a/drizzle-kit/tests/push/sqlite.test.ts +++ b/drizzle-kit/tests/push/sqlite.test.ts @@ -395,6 +395,7 @@ test('drop autoincrement. drop column with data', async (t) => { ); expect(columnsToRemove!.length).toBe(1); + expect(columnsToRemove![0]).toBe('name'); expect(infoToPrint!.length).toBe(1); expect(infoToPrint![0]).toBe( `· You're about to delete ${ diff --git a/drizzle-kit/tests/sqlite-columns.test.ts b/drizzle-kit/tests/sqlite-columns.test.ts index 7329db7c9..04dbb940c 100644 --- a/drizzle-kit/tests/sqlite-columns.test.ts +++ b/drizzle-kit/tests/sqlite-columns.test.ts @@ -821,6 +821,18 @@ test('alter column add default not null with indexes', async (t) => { type: 'create_index', internal: undefined, }); + expect(sqlStatements.length).toBe(7); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_table\` ( +\t\`name\` text DEFAULT 'dan' NOT NULL +);\n`); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`__new_table\`("name") SELECT "name" FROM \`table\`;`, + ); + expect(sqlStatements[3]).toBe(`DROP TABLE \`table\`;`); + expect(sqlStatements[4]).toBe(`ALTER TABLE \`__new_table\` RENAME TO \`table\`;`); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); + expect(sqlStatements[6]).toBe(`CREATE INDEX \`index_name\` ON \`table\` (\`name\`);`); }); test('alter column drop default not null', async (t) => { @@ -858,6 +870,17 @@ test('alter column drop default not null', async (t) => { tableName: 'table', uniqueConstraints: [], }); + expect(sqlStatements.length).toBe(6); + expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); + expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_table\` ( +\t\`name\` text +);\n`); + expect(sqlStatements[2]).toBe( + `INSERT INTO \`__new_table\`("name") SELECT "name" FROM \`table\`;`, + ); + expect(sqlStatements[3]).toBe(`DROP TABLE \`table\`;`); + expect(sqlStatements[4]).toBe(`ALTER TABLE \`__new_table\` RENAME TO \`table\`;`); + expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); }); test('alter column drop generated', async (t) => { From ec4420ef109445e7b0f468647f280c40edb1fc11 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Mon, 2 Sep 2024 16:05:10 +0300 Subject: [PATCH 22/56] - moved 'turso' driver to dialect - rewrote tests --- drizzle-kit/package.json | 2 +- drizzle-kit/schema.ts | 0 drizzle-kit/src/cli/commands/introspect.ts | 112 ++++++ .../src/cli/commands/libSqlPushUtils.ts | 10 +- drizzle-kit/src/cli/commands/migrate.ts | 132 +++--- drizzle-kit/src/cli/commands/push.ts | 25 +- drizzle-kit/src/cli/commands/utils.ts | 85 +++- drizzle-kit/src/cli/connections.ts | 162 ++++---- drizzle-kit/src/cli/schema.ts | 39 +- drizzle-kit/src/cli/validations/common.ts | 3 +- drizzle-kit/src/cli/validations/libsql.ts | 27 ++ drizzle-kit/src/cli/validations/sqlite.ts | 5 - drizzle-kit/src/index.ts | 3 +- drizzle-kit/src/schemaValidator.ts | 2 +- drizzle-kit/src/serializer/studio.ts | 29 +- drizzle-kit/src/snapshotsDiffer.ts | 3 +- drizzle-kit/src/sqlgenerator.ts | 55 ++- drizzle-kit/src/utils.ts | 8 + drizzle-kit/tests/cli-generate.test.ts | 9 - drizzle-kit/tests/cli-migrate.test.ts | 3 +- drizzle-kit/tests/cli-push.test.ts | 5 +- drizzle-kit/tests/cli/turso.config.ts | 3 +- drizzle-kit/tests/push/libsql.test.ts | 15 +- drizzle-orm/package.json | 2 +- pnpm-lock.yaml | 379 +++++++++++------- 25 files changed, 726 insertions(+), 392 deletions(-) delete mode 100644 drizzle-kit/schema.ts create mode 100644 drizzle-kit/src/cli/validations/libsql.ts diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index 552c14d0e..75c3d65dc 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -54,7 +54,7 @@ "@electric-sql/pglite": "^0.1.5", "@hono/node-server": "^1.9.0", "@hono/zod-validator": "^0.2.1", - "@libsql/client": "^0.4.2", + "@libsql/client": "^0.10.0", "@neondatabase/serverless": "^0.9.1", "@originjs/vite-plugin-commonjs": "^1.0.3", "@planetscale/database": "^1.16.0", diff --git a/drizzle-kit/schema.ts b/drizzle-kit/schema.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/drizzle-kit/src/cli/commands/introspect.ts b/drizzle-kit/src/cli/commands/introspect.ts index 4e51d6b49..0940385af 100644 --- a/drizzle-kit/src/cli/commands/introspect.ts +++ b/drizzle-kit/src/cli/commands/introspect.ts @@ -25,6 +25,7 @@ import { } from '../../snapshotsDiffer'; import { prepareOutFolder } from '../../utils'; import type { Casing, Prefix } from '../validations/common'; +import { LibSQLCredentials } from '../validations/libsql'; import type { MysqlCredentials } from '../validations/mysql'; import type { PostgresCredentials } from '../validations/postgres'; import { SingleStoreCredentials } from '../validations/singlestore'; @@ -470,6 +471,117 @@ export const introspectSqlite = async ( process.exit(0); }; +export const introspectLibSQL = async ( + casing: Casing, + out: string, + breakpoints: boolean, + credentials: LibSQLCredentials, + tablesFilter: string[], + prefix: Prefix, +) => { + const { connectToLibSQL } = await import('../connections'); + const db = await connectToLibSQL(credentials); + + const matchers = tablesFilter.map((it) => { + return new Minimatch(it); + }); + + const filter = (tableName: string) => { + if (matchers.length === 0) return true; + + let flags: boolean[] = []; + + for (let matcher of matchers) { + if (matcher.negate) { + if (!matcher.match(tableName)) { + flags.push(false); + } + } + + if (matcher.match(tableName)) { + flags.push(true); + } + } + + if (flags.length > 0) { + return flags.every(Boolean); + } + return false; + }; + + const progress = new IntrospectProgress(); + const res = await renderWithTask( + progress, + fromSqliteDatabase(db, filter, (stage, count, status) => { + progress.update(stage, count, status); + }), + ); + + const schema = { id: originUUID, prevId: '', ...res } as SQLiteSchema; + const ts = sqliteSchemaToTypeScript(schema, casing); + const relationsTs = relationsToTypeScript(schema, casing); + + // check orm and orm-pg api version + + const schemaFile = join(out, 'schema.ts'); + writeFileSync(schemaFile, ts.file); + const relationsFile = join(out, 'relations.ts'); + writeFileSync(relationsFile, relationsTs.file); + console.log(); + + const { snapshots, journal } = prepareOutFolder(out, 'postgresql'); + + if (snapshots.length === 0) { + const { sqlStatements, _meta } = await applySqliteSnapshotsDiff( + squashSqliteScheme(drySQLite), + squashSqliteScheme(schema), + tablesResolver, + columnsResolver, + drySQLite, + schema, + ); + + writeResult({ + cur: schema, + sqlStatements, + journal, + _meta, + outFolder: out, + breakpoints, + type: 'introspect', + prefixMode: prefix, + }); + } else { + render( + `[${ + chalk.blue( + 'i', + ) + }] No SQL generated, you already have migrations in project`, + ); + } + + render( + `[${ + chalk.green( + '✓', + ) + }] You schema file is ready ➜ ${chalk.bold.underline.blue(schemaFile)} 🚀`, + ); + render( + `[${ + chalk.green( + '✓', + ) + }] You relations file is ready ➜ ${ + chalk.bold.underline.blue( + relationsFile, + ) + } 🚀`, + ); + process.exit(0); +}; + const withCasing = (value: string, casing: Casing) => { if (casing === 'preserve') { return value; diff --git a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts index 3bc9e8225..98c95f089 100644 --- a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts +++ b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts @@ -133,7 +133,7 @@ export const libSqlLogSuggestionsAndReturn = async ( tablesToRemove.push(statement.tableName); shouldAskForApprove = true; } - const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + const fromJsonStatement = fromJson([statement], 'turso', 'push', json2); statementsToExecute.push( ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), ); @@ -156,7 +156,7 @@ export const libSqlLogSuggestionsAndReturn = async ( shouldAskForApprove = true; } - const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + const fromJsonStatement = fromJson([statement], 'turso', 'push', json2); statementsToExecute.push( ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), ); @@ -185,7 +185,7 @@ export const libSqlLogSuggestionsAndReturn = async ( shouldAskForApprove = true; } - const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + const fromJsonStatement = fromJson([statement], 'turso', 'push', json2); statementsToExecute.push( ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), ); @@ -330,12 +330,12 @@ export const libSqlLogSuggestionsAndReturn = async ( columnsToRemove.push(`${tableName}_${statement.columnName}`); shouldAskForApprove = true; } - const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + const fromJsonStatement = fromJson([statement], 'turso', 'push', json2); statementsToExecute.push( ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), ); } else { - const fromJsonStatement = fromJson([statement], 'sqlite', 'push', 'turso', json2); + const fromJsonStatement = fromJson([statement], 'turso', 'push', json2); statementsToExecute.push( ...(Array.isArray(fromJsonStatement) ? fromJsonStatement : [fromJsonStatement]), ); diff --git a/drizzle-kit/src/cli/commands/migrate.ts b/drizzle-kit/src/cli/commands/migrate.ts index 664e7da02..83f5b2dc8 100644 --- a/drizzle-kit/src/cli/commands/migrate.ts +++ b/drizzle-kit/src/cli/commands/migrate.ts @@ -492,7 +492,6 @@ export const prepareAndMigrateSingleStore = async (config: GenerateConfig) => { export const prepareAndMigrateSqlite = async (config: GenerateConfig) => { const outFolder = config.out; const schemaPath = config.schema; - const driver = config.driver; try { assertV1OutFolder(outFolder); @@ -524,35 +523,74 @@ export const prepareAndMigrateSqlite = async (config: GenerateConfig) => { const squashedPrev = squashSqliteScheme(validatedPrev); const squashedCur = squashSqliteScheme(validatedCur); - let sqlStatements: string[]; - let _meta: - | { - schemas: {}; - tables: {}; - columns: {}; - } - | undefined; - - if (driver === 'turso') { - ({ sqlStatements, _meta } = await applyLibSQLSnapshotsDiff( - squashedPrev, - squashedCur, - tablesResolver, - columnsResolver, - validatedPrev, - validatedCur, - )); - } else { - ({ sqlStatements, _meta } = await applySqliteSnapshotsDiff( - squashedPrev, - squashedCur, - tablesResolver, - columnsResolver, - validatedPrev, - validatedCur, - )); + const { sqlStatements, _meta } = await applySqliteSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + ); + + writeResult({ + cur, + sqlStatements, + journal, + _meta, + outFolder, + name: config.name, + breakpoints: config.breakpoints, + bundle: config.bundle, + prefixMode: config.prefix, + }); + } catch (e) { + console.error(e); + } +}; + +export const prepareAndMigrateLibSQL = async (config: GenerateConfig) => { + const outFolder = config.out; + const schemaPath = config.schema; + + try { + assertV1OutFolder(outFolder); + + const { snapshots, journal } = prepareMigrationFolder(outFolder, 'sqlite'); + const { prev, cur, custom } = await prepareSqliteMigrationSnapshot( + snapshots, + schemaPath, + ); + + const validatedPrev = sqliteSchema.parse(prev); + const validatedCur = sqliteSchema.parse(cur); + + if (config.custom) { + writeResult({ + cur: custom, + sqlStatements: [], + journal, + outFolder, + name: config.name, + breakpoints: config.breakpoints, + bundle: config.bundle, + type: 'custom', + prefixMode: config.prefix, + }); + return; } + const squashedPrev = squashSqliteScheme(validatedPrev); + const squashedCur = squashSqliteScheme(validatedCur); + + const { sqlStatements, _meta } = await applyLibSQLSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + ); + writeResult({ cur, sqlStatements, @@ -572,7 +610,6 @@ export const prepareAndMigrateSqlite = async (config: GenerateConfig) => { export const prepareSQLitePush = async ( schemaPath: string | string[], snapshot: SQLiteSchema, - driver?: Driver, ) => { const { prev, cur } = await prepareSQLiteDbPushSnapshot(snapshot, schemaPath); @@ -582,34 +619,15 @@ export const prepareSQLitePush = async ( const squashedPrev = squashSqliteScheme(validatedPrev, 'push'); const squashedCur = squashSqliteScheme(validatedCur, 'push'); - let sqlStatements: string[]; - let statements: JsonStatement[]; - let _meta: { - schemas: {}; - tables: {}; - columns: {}; - } | undefined; - if (driver === 'turso') { - ({ sqlStatements, statements, _meta } = await applyLibSQLSnapshotsDiff( - squashedPrev, - squashedCur, - tablesResolver, - columnsResolver, - validatedPrev, - validatedCur, - 'push', - )); - } else { - ({ sqlStatements, statements, _meta } = await applySqliteSnapshotsDiff( - squashedPrev, - squashedCur, - tablesResolver, - columnsResolver, - validatedPrev, - validatedCur, - 'push', - )); - } + const { sqlStatements, statements, _meta } = await applySqliteSnapshotsDiff( + squashedPrev, + squashedCur, + tablesResolver, + columnsResolver, + validatedPrev, + validatedCur, + 'push', + ); return { sqlStatements, diff --git a/drizzle-kit/src/cli/commands/push.ts b/drizzle-kit/src/cli/commands/push.ts index e8aaf2e2b..1be844715 100644 --- a/drizzle-kit/src/cli/commands/push.ts +++ b/drizzle-kit/src/cli/commands/push.ts @@ -2,6 +2,7 @@ import chalk from 'chalk'; import { render } from 'hanji'; import { fromJson } from '../../sqlgenerator'; import { Select } from '../selector-ui'; +import { LibSQLCredentials } from '../validations/libsql'; import type { MysqlCredentials } from '../validations/mysql'; import { withStyle } from '../validations/outputs'; import type { PostgresCredentials } from '../validations/postgres'; @@ -519,8 +520,6 @@ export const sqlitePush = async ( await db.query('rollback'); process.exit(1); } - } else if (credentials.driver === 'turso') { - await db.batch!(statementsToExecute.map((it) => ({ query: it }))); } render(`[${chalk.green('✓')}] Changes applied`); } @@ -531,14 +530,14 @@ export const libSQLPush = async ( schemaPath: string | string[], verbose: boolean, strict: boolean, - credentials: SqliteCredentials, + credentials: LibSQLCredentials, tablesFilter: string[], force: boolean, ) => { - const { connectToSQLite } = await import('../connections'); + const { connectToLibSQL } = await import('../connections'); const { sqlitePushIntrospect } = await import('./sqliteIntrospect'); - const db = await connectToSQLite(credentials); + const db = await connectToLibSQL(credentials); const { schema } = await sqlitePushIntrospect(db, tablesFilter); const { prepareLibSQLPush } = await import('./migrate'); @@ -627,21 +626,7 @@ export const libSQLPush = async ( if (statementsToExecute.length === 0) { render(`\n[${chalk.blue('i')}] No changes detected`); } else { - if (!('driver' in credentials)) { - await db.query('begin'); - try { - for (const dStmnt of statementsToExecute) { - await db.query(dStmnt); - } - await db.query('commit'); - } catch (e) { - console.error(e); - await db.query('rollback'); - process.exit(1); - } - } else if (credentials.driver === 'turso') { - await db.batch!(statementsToExecute.map((it) => ({ query: it }))); - } + await db.batchWithPragma!(statementsToExecute); render(`[${chalk.green('✓')}] Changes applied`); } } diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index d8c704e7a..2cfc7273b 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -16,6 +16,8 @@ import { Prefix, wrapParam, } from '../validations/common'; +import { LibSQLCredentials, libSQLCredentials } from '../validations/libsql'; +import { printConfigConnectionIssues as printIssuesLibSql } from '../validations/libsql'; import { MysqlCredentials, mysqlCredentials, @@ -127,7 +129,6 @@ export type GenerateConfig = { prefix: Prefix; custom: boolean; bundle: boolean; - driver?: Driver; }; export const prepareGenerateConfig = async ( @@ -174,7 +175,6 @@ export const prepareGenerateConfig = async ( schema: schema, out: out || 'drizzle', bundle: driver === 'expo', - driver, }; }; @@ -217,7 +217,10 @@ export const preparePushConfig = async ( | { dialect: 'sqlite'; credentials: SqliteCredentials; - driver?: Driver; + } + | { + dialect: 'turso'; + credentials: LibSQLCredentials; } | { dialect: 'singlestore'; @@ -356,7 +359,24 @@ export const preparePushConfig = async ( credentials: parsed.data, tablesFilter, schemasFilter, - driver: config.driver, + }; + } + + if (config.dialect === 'turso') { + const parsed = libSQLCredentials.safeParse(config); + if (!parsed.success) { + printIssuesSqlite(config, 'pull'); + process.exit(1); + } + return { + dialect: 'turso', + schemaPath: config.schema, + strict: config.strict ?? false, + verbose: config.verbose ?? false, + force: (options.force as boolean) ?? false, + credentials: parsed.data, + tablesFilter, + schemasFilter, }; } @@ -384,6 +404,10 @@ export const preparePullConfig = async ( dialect: 'singlestore'; credentials: SingleStoreCredentials; } + | { + dialect: 'turso'; + credentials: LibSQLCredentials; + } ) & { out: string; breakpoints: boolean; @@ -482,11 +506,11 @@ export const preparePullConfig = async ( dialect: 'singlestore', out: config.out, breakpoints: config.breakpoints, - casing: config.introspectCasing, + casing: config.casing, credentials: parsed.data, tablesFilter, schemasFilter, - prefix: config.database?.prefix || 'index', + prefix: config.migrations?.prefix || 'index', }; } @@ -508,6 +532,24 @@ export const preparePullConfig = async ( }; } + if (dialect === 'turso') { + const parsed = libSQLCredentials.safeParse(config); + if (!parsed.success) { + printIssuesLibSql(config, 'pull'); + process.exit(1); + } + return { + dialect: 'sqlite', + out: config.out, + breakpoints: config.breakpoints, + casing: config.casing, + credentials: parsed.data, + tablesFilter, + schemasFilter, + prefix: config.migrations?.prefix || 'index', + }; + } + assertUnreachable(dialect); }; @@ -594,6 +636,22 @@ export const prepareStudioConfig = async (options: Record) => { }; } + if (dialect === 'turso') { + const parsed = libSQLCredentials.safeParse(flattened); + if (!parsed.success) { + printIssuesLibSql(flattened as Record, 'studio'); + process.exit(1); + } + const credentials = parsed.data; + return { + dialect, + schema, + host, + port, + credentials, + }; + } + assertUnreachable(dialect); }; @@ -679,6 +737,21 @@ export const prepareMigrateConfig = async (configPath: string | undefined) => { table, }; } + if (dialect === 'turso') { + const parsed = libSQLCredentials.safeParse(flattened); + if (!parsed.success) { + printIssuesLibSql(flattened as Record, 'migrate'); + process.exit(1); + } + const credentials = parsed.data; + return { + dialect, + out, + credentials, + schema, + table, + }; + } assertUnreachable(dialect); }; diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index 3357bf146..440d3d5bf 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -5,8 +5,17 @@ import fetch from 'node-fetch'; import ws from 'ws'; import { assertUnreachable } from '../global'; import type { ProxyParams } from '../serializer/studio'; -import { type DB, normalisePGliteUrl, normaliseSQLiteUrl, type Proxy, type SQLiteDB, type SqliteProxy } from '../utils'; +import { + type DB, + LibSQLDB, + normalisePGliteUrl, + normaliseSQLiteUrl, + type Proxy, + type SQLiteDB, + type SqliteProxy, +} from '../utils'; import { assertPackages, checkPackage } from './utils'; +import { LibSQLCredentials } from './validations/libsql'; import type { MysqlCredentials } from './validations/mysql'; import { withStyle } from './validations/outputs'; import type { PostgresCredentials } from './validations/postgres'; @@ -483,56 +492,7 @@ export const connectToSQLite = async ( > => { if ('driver' in credentials) { const { driver } = credentials; - if (driver === 'turso') { - assertPackages('@libsql/client'); - const { createClient } = await import('@libsql/client'); - const { drizzle } = await import('drizzle-orm/libsql'); - const { migrate } = await import('drizzle-orm/libsql/migrator'); - - const client = createClient({ - url: credentials.url, - authToken: credentials.authToken, - }); - - const drzl = drizzle(client); - const migrateFn = async (config: MigrationConfig) => { - return migrate(drzl, config); - }; - - const db: SQLiteDB = { - query: async (sql: string, params?: any[]) => { - const res = await client.execute({ sql, args: params || [] }); - return res.rows as T[]; - }, - run: async (query: string) => { - await client.execute(query); - }, - batch: async ( - queries: { query: string; values?: any[] | undefined }[], - ) => { - await client.batch( - queries.map((it) => ({ sql: it.query, args: it.values ?? [] })), - ); - }, - }; - const proxy: SqliteProxy = { - proxy: async (params: ProxyParams) => { - const preparedParams = prepareSqliteParams(params.params); - const result = await client.execute({ - sql: params.sql, - args: preparedParams, - }); - - if (params.mode === 'array') { - return result.rows.map((row) => Object.values(row)); - } else { - return result.rows; - } - }, - }; - - return { ...db, ...proxy, migrate: migrateFn }; - } else if (driver === 'd1-http') { + if (driver === 'd1-http') { const { drizzle } = await import('drizzle-orm/sqlite-proxy'); const { migrate } = await import('drizzle-orm/sqlite-proxy/migrator'); @@ -626,48 +586,6 @@ export const connectToSQLite = async ( } } - if (await checkPackage('@libsql/client')) { - const { createClient } = await import('@libsql/client'); - const { drizzle } = await import('drizzle-orm/libsql'); - const { migrate } = await import('drizzle-orm/libsql/migrator'); - - const client = createClient({ - url: normaliseSQLiteUrl(credentials.url, 'libsql'), - }); - const drzl = drizzle(client); - const migrateFn = async (config: MigrationConfig) => { - return migrate(drzl, config); - }; - - const db: SQLiteDB = { - query: async (sql: string, params?: any[]) => { - const res = await client.execute({ sql, args: params || [] }); - return res.rows as T[]; - }, - run: async (query: string) => { - await client.execute(query); - }, - }; - - const proxy: SqliteProxy = { - proxy: async (params: ProxyParams) => { - const preparedParams = prepareSqliteParams(params.params); - const result = await client.execute({ - sql: params.sql, - args: preparedParams, - }); - - if (params.mode === 'array') { - return result.rows.map((row) => Object.values(row)); - } else { - return result.rows; - } - }, - }; - - return { ...db, ...proxy, migrate: migrateFn }; - } - if (await checkPackage('better-sqlite3')) { const { default: Database } = await import('better-sqlite3'); const { drizzle } = await import('drizzle-orm/better-sqlite3'); @@ -710,7 +628,63 @@ export const connectToSQLite = async ( return { ...db, ...proxy, migrate: migrateFn }; } console.log( - "Please install either 'better-sqlite3' or '@libsql/client' for Drizzle Kit to connect to SQLite databases", + "Please install 'better-sqlite3' for Drizzle Kit to connect to SQLite databases", + ); + process.exit(1); +}; + +export const connectToLibSQL = async (credentials: LibSQLCredentials): Promise< + & LibSQLDB + & SqliteProxy + & { migrate: (config: MigrationConfig) => Promise } +> => { + if (await checkPackage('@libsql/client')) { + const { createClient } = await import('@libsql/client'); + const { drizzle } = await import('drizzle-orm/libsql'); + const { migrate } = await import('drizzle-orm/libsql/migrator'); + + const client = createClient({ + url: normaliseSQLiteUrl(credentials.url, 'libsql'), + }); + const drzl = drizzle(client); + const migrateFn = async (config: MigrationConfig) => { + return migrate(drzl, config); + }; + + const db: LibSQLDB = { + query: async (sql: string, params?: any[]) => { + const res = await client.execute({ sql, args: params || [] }); + return res.rows as T[]; + }, + run: async (query: string) => { + await client.execute(query); + }, + batchWithPragma: async (queries: string[]) => { + await client.migrate(queries); + }, + }; + + const proxy: SqliteProxy = { + proxy: async (params: ProxyParams) => { + const preparedParams = prepareSqliteParams(params.params); + const result = await client.execute({ + sql: params.sql, + args: preparedParams, + }); + + if (params.mode === 'array') { + return result.rows.map((row) => Object.values(row)); + } else { + return result.rows; + } + }, + }; + + return { ...db, ...proxy, migrate: migrateFn }; + } + + console.log( + "Please install '@libsql/client' for Drizzle Kit to connect to LibSQL databases", ); process.exit(1); }; diff --git a/drizzle-kit/src/cli/schema.ts b/drizzle-kit/src/cli/schema.ts index bf825d3e3..c7d5b88f3 100644 --- a/drizzle-kit/src/cli/schema.ts +++ b/drizzle-kit/src/cli/schema.ts @@ -24,13 +24,13 @@ import { mkdirSync } from 'fs'; import { renderWithTask } from 'hanji'; import { dialects } from 'src/schemaValidator'; import { assertUnreachable } from '../global'; -import type { Setup } from '../serializer/studio'; +import { drizzleForLibSQL, type Setup } from '../serializer/studio'; import { certs } from '../utils/certs'; import { grey, MigrateProgress } from './views'; const optionDialect = string('dialect') .enum(...dialects) - .desc(`Database dialect: 'postgresql', 'mysql' or 'sqlite'`); + .desc(`Database dialect: 'postgresql', 'mysql', 'sqlite' or 'turso'`); const optionOut = string().desc("Output folder, 'drizzle' by default"); const optionConfig = string().desc('Path to drizzle config file'); const optionBreakpoints = boolean().desc( @@ -77,6 +77,7 @@ export const generate = command({ prepareAndMigratePg, prepareAndMigrateMysql, prepareAndMigrateSqlite, + prepareAndMigrateLibSQL, } = await import('./commands/migrate'); const dialect = opts.dialect; @@ -86,6 +87,8 @@ export const generate = command({ await prepareAndMigrateMysql(opts); } else if (dialect === 'sqlite') { await prepareAndMigrateSqlite(opts); + } else if (dialect === 'turso') { + await prepareAndMigrateLibSQL(opts); } else { assertUnreachable(dialect); } @@ -159,6 +162,17 @@ export const migrate = command({ migrationsSchema: schema, }), ); + } else if (dialect === 'turso') { + const { connectToLibSQL } = await import('./connections'); + const { migrate } = await connectToLibSQL(credentials); + await renderWithTask( + new MigrateProgress(), + migrate({ + migrationsFolder: opts.out, + migrationsTable: table, + migrationsSchema: schema, + }), + ); } else { assertUnreachable(dialect); } @@ -294,7 +308,7 @@ export const push = command({ schemasFilter, force, ); - } else if (dialect === 'sqlite' && !('driver' in credentials)) { + } else if (dialect === 'sqlite') { const { sqlitePush } = await import('./commands/push'); await sqlitePush( schemaPath, @@ -304,7 +318,7 @@ export const push = command({ tablesFilter, force, ); - } else if (dialect === 'sqlite' && ('driver' in credentials && credentials.driver === 'turso')) { + } else if (dialect === 'turso') { const { libSQLPush } = await import('./commands/push'); await libSQLPush( schemaPath, @@ -369,7 +383,7 @@ export const up = command({ upMysqlHandler(out); } - if (dialect === 'sqlite') { + if (dialect === 'sqlite' || dialect === 'turso') { upSqliteHandler(out); } }, @@ -493,6 +507,16 @@ export const pull = command({ tablesFilter, prefix, ); + } else if (dialect === 'turso') { + const { introspectLibSQL } = await import('./commands/introspect'); + await introspectLibSQL( + casing, + out, + breakpoints, + credentials, + tablesFilter, + prefix, + ); } else { assertUnreachable(dialect); } @@ -593,6 +617,11 @@ export const studio = command({ ? await prepareSQLiteSchema(schemaPath) : { schema: {}, relations: {}, files: [] }; setup = await drizzleForSQLite(credentials, schema, relations, files); + } else if (dialect === 'turso') { + const { schema, relations, files } = schemaPath + ? await prepareSQLiteSchema(schemaPath) + : { schema: {}, relations: {}, files: [] }; + setup = await drizzleForLibSQL(credentials, schema, relations, files); } else { assertUnreachable(dialect); } diff --git a/drizzle-kit/src/cli/validations/common.ts b/drizzle-kit/src/cli/validations/common.ts index a7307f4d6..3a2701e37 100644 --- a/drizzle-kit/src/cli/validations/common.ts +++ b/drizzle-kit/src/cli/validations/common.ts @@ -61,7 +61,6 @@ export const assertCollisions = < }; export const sqliteDriversLiterals = [ - literal('turso'), literal('d1-http'), literal('expo'), ] as const; @@ -156,7 +155,7 @@ export const configPushSchema = object({ }); export type CliConfig = TypeOf; -export const drivers = ['turso', 'd1-http', 'expo', 'aws-data-api', 'pglite'] as const; +export const drivers = ['d1-http', 'expo', 'aws-data-api', 'pglite'] as const; export type Driver = (typeof drivers)[number]; const _: Driver = '' as TypeOf; diff --git a/drizzle-kit/src/cli/validations/libsql.ts b/drizzle-kit/src/cli/validations/libsql.ts new file mode 100644 index 000000000..a9b03c168 --- /dev/null +++ b/drizzle-kit/src/cli/validations/libsql.ts @@ -0,0 +1,27 @@ +import { softAssertUnreachable } from 'src/global'; +import { object, string, TypeOf } from 'zod'; +import { error } from '../views'; +import { wrapParam } from './common'; + +export const libSQLCredentials = object({ + url: string().min(1), + authToken: string().min(1).optional(), +}); + +export type LibSQLCredentials = { + url: string; + authToken?: string; +}; + +const _: LibSQLCredentials = {} as TypeOf; + +export const printConfigConnectionIssues = ( + options: Record, + command: 'generate' | 'migrate' | 'push' | 'pull' | 'studio', +) => { + let text = `Please provide required params for 'turso' dialect:\n`; + console.log(error(text)); + console.log(wrapParam('url', options.url)); + console.log(wrapParam('authToken', options.authToken, true, 'secret')); + process.exit(1); +}; diff --git a/drizzle-kit/src/cli/validations/sqlite.ts b/drizzle-kit/src/cli/validations/sqlite.ts index b6ad062d5..54178fd4a 100644 --- a/drizzle-kit/src/cli/validations/sqlite.ts +++ b/drizzle-kit/src/cli/validations/sqlite.ts @@ -25,11 +25,6 @@ export const sqliteCredentials = union([ ]); export type SqliteCredentials = - | { - driver: 'turso'; - url: string; - authToken: string; - } | { driver: 'd1-http'; accountId: string; diff --git a/drizzle-kit/src/index.ts b/drizzle-kit/src/index.ts index 5da4cb7b4..dc0c6274c 100644 --- a/drizzle-kit/src/index.ts +++ b/drizzle-kit/src/index.ts @@ -128,8 +128,7 @@ export type Config = } & ( | { - dialect: Verify; - driver: Verify; + dialect: Verify; dbCredentials: { url: string; authToken?: string; diff --git a/drizzle-kit/src/schemaValidator.ts b/drizzle-kit/src/schemaValidator.ts index 712252f37..e91b5ab11 100644 --- a/drizzle-kit/src/schemaValidator.ts +++ b/drizzle-kit/src/schemaValidator.ts @@ -4,7 +4,7 @@ import { pgSchema, pgSchemaSquashed } from './serializer/pgSchema'; import { singlestoreSchema, singlestoreSchemaSquashed } from './serializer/singlestoreSchema'; import { sqliteSchema, SQLiteSchemaSquashed } from './serializer/sqliteSchema'; -export const dialects = ['postgresql', 'mysql', 'sqlite', 'singlestore'] as const; +export const dialects = ['postgresql', 'mysql', 'sqlite', 'turso', 'singlestore'] as const; export const dialect = enumType(dialects); export type Dialect = (typeof dialects)[number]; diff --git a/drizzle-kit/src/serializer/studio.ts b/drizzle-kit/src/serializer/studio.ts index 5515e6f59..12ea8207c 100644 --- a/drizzle-kit/src/serializer/studio.ts +++ b/drizzle-kit/src/serializer/studio.ts @@ -25,6 +25,7 @@ import fs from 'fs'; import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { createServer } from 'node:https'; +import { LibSQLCredentials } from 'src/cli/validations/libsql'; import { assertUnreachable } from 'src/global'; import superjson from 'superjson'; import { z } from 'zod'; @@ -342,8 +343,6 @@ export const drizzleForSQLite = async ( const { driver } = credentials; if (driver === 'd1-http') { dbUrl = `d1-http://${credentials.accountId}/${credentials.databaseId}/${credentials.token}`; - } else if (driver === 'turso') { - dbUrl = `turso://${credentials.url}/${credentials.authToken}`; } else { assertUnreachable(driver); } @@ -364,6 +363,32 @@ export const drizzleForSQLite = async ( schemaFiles, }; }; +export const drizzleForLibSQL = async ( + credentials: LibSQLCredentials, + sqliteSchema: Record>, + relations: Record, + schemaFiles?: SchemaFile[], +): Promise => { + const { connectToLibSQL } = await import('../cli/connections'); + + const sqliteDB = await connectToLibSQL(credentials); + const customDefaults = getCustomDefaults(sqliteSchema); + + let dbUrl: string = `turso://${credentials.url}/${credentials.authToken}`; + + const dbHash = createHash('sha256').update(dbUrl).digest('hex'); + + return { + dbHash, + dialect: 'sqlite', + driver: undefined, + proxy: sqliteDB.proxy, + customDefaults, + schema: sqliteSchema, + relations, + schemaFiles, + }; +}; export const drizzleForSingleStore = async ( credentials: SingleStoreCredentials, diff --git a/drizzle-kit/src/snapshotsDiffer.ts b/drizzle-kit/src/snapshotsDiffer.ts index af3677abd..ec8a5c1b8 100644 --- a/drizzle-kit/src/snapshotsDiffer.ts +++ b/drizzle-kit/src/snapshotsDiffer.ts @@ -2896,9 +2896,8 @@ export const applyLibSQLSnapshotsDiff = async ( const sqlStatements = fromJson( combinedJsonStatements, - 'sqlite', - action, 'turso', + action, json2, ); diff --git a/drizzle-kit/src/sqlgenerator.ts b/drizzle-kit/src/sqlgenerator.ts index bcd47565a..227cdf032 100644 --- a/drizzle-kit/src/sqlgenerator.ts +++ b/drizzle-kit/src/sqlgenerator.ts @@ -392,7 +392,7 @@ class SingleStoreCreateTableConvertor extends Convertor { export class SQLiteCreateTableConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'sqlite_create_table' && dialect === 'sqlite'; + return statement.type === 'sqlite_create_table' && (dialect === 'sqlite' || dialect === 'turso'); } convert(st: JsonSqliteCreateTableStatement) { @@ -898,7 +898,7 @@ class SingleStoreDropTableConvertor extends Convertor { export class SQLiteDropTableConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'drop_table' && dialect === 'sqlite'; + return statement.type === 'drop_table' && (dialect === 'sqlite' || dialect === 'turso'); } convert(statement: JsonDropTableStatement) { @@ -924,7 +924,7 @@ class PgRenameTableConvertor extends Convertor { export class SqliteRenameTableConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'rename_table' && dialect === 'sqlite'; + return statement.type === 'rename_table' && (dialect === 'sqlite' || dialect === 'turso'); } convert(statement: JsonRenameTableStatement) { @@ -1002,7 +1002,7 @@ class SingleStoreAlterTableRenameColumnConvertor extends Convertor { class SQLiteAlterTableRenameColumnConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( - statement.type === 'alter_table_rename_column' && dialect === 'sqlite' + statement.type === 'alter_table_rename_column' && (dialect === 'sqlite' || dialect === 'turso') ); } @@ -1054,7 +1054,7 @@ class SingleStoreAlterTableDropColumnConvertor extends Convertor { class SQLiteAlterTableDropColumnConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'alter_table_drop_column' && dialect === 'sqlite'; + return statement.type === 'alter_table_drop_column' && (dialect === 'sqlite' || dialect === 'turso'); } convert(statement: JsonDropColumnStatement) { @@ -1195,7 +1195,7 @@ class SingleStoreAlterTableAddColumnConvertor extends Convertor { export class SQLiteAlterTableAddColumnConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( - statement.type === 'sqlite_alter_table_add_column' && dialect === 'sqlite' + statement.type === 'sqlite_alter_table_add_column' && (dialect === 'sqlite' || dialect === 'turso') ); } @@ -1400,7 +1400,7 @@ class SqliteAlterTableAlterColumnDropGeneratedConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( statement.type === 'alter_table_alter_column_drop_generated' - && dialect === 'sqlite' + && (dialect === 'sqlite' || dialect === 'turso') ); } @@ -1449,7 +1449,7 @@ class SqliteAlterTableAlterColumnSetExpressionConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( statement.type === 'alter_table_alter_column_set_generated' - && dialect === 'sqlite' + && (dialect === 'sqlite' || dialect === 'turso') ); } @@ -1498,7 +1498,7 @@ class SqliteAlterTableAlterColumnAlterGeneratedConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return ( statement.type === 'alter_table_alter_column_alter_generated' - && dialect === 'sqlite' + && (dialect === 'sqlite' || dialect === 'turso') ); } @@ -1653,19 +1653,18 @@ type LibSQLModifyColumnStatement = | JsonAlterColumnDropDefaultStatement; export class LibSQLModifyColumn extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + can(statement: JsonStatement, dialect: Dialect): boolean { return ( (statement.type === 'alter_table_alter_column_set_type' || statement.type === 'alter_table_alter_column_drop_notnull' || statement.type === 'alter_table_alter_column_set_notnull' || statement.type === 'alter_table_alter_column_set_default' || statement.type === 'alter_table_alter_column_drop_default') - && dialect === 'sqlite' - && driver === 'turso' + && dialect === 'turso' ); } - convert(statement: LibSQLModifyColumnStatement, json2: SQLiteSchemaSquashed, action?: 'push') { + convert(statement: LibSQLModifyColumnStatement, json2: SQLiteSchemaSquashed) { const { tableName, columnName } = statement; let columnType = ``; @@ -2686,11 +2685,10 @@ class PgCreateForeignKeyConvertor extends Convertor { } class LibSQLCreateForeignKeyConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + can(statement: JsonStatement, dialect: Dialect): boolean { return ( statement.type === 'create_reference' - && dialect === 'sqlite' - && driver === 'turso' + && dialect === 'turso' ); } @@ -2928,7 +2926,7 @@ class CreateSingleStoreIndexConvertor extends Convertor { export class CreateSqliteIndexConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'create_index' && dialect === 'sqlite'; + return statement.type === 'create_index' && (dialect === 'sqlite' || dialect === 'turso'); } convert(statement: JsonCreateIndexStatement): string { @@ -3050,7 +3048,7 @@ class PgAlterTableRemoveFromSchemaConvertor extends Convertor { export class SqliteDropIndexConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { - return statement.type === 'drop_index' && dialect === 'sqlite'; + return statement.type === 'drop_index' && (dialect === 'sqlite' || dialect === 'turso'); } convert(statement: JsonDropIndexStatement): string { @@ -3082,9 +3080,9 @@ class SingleStoreDropIndexConvertor extends Convertor { } class SQLiteRecreateTableConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + can(statement: JsonStatement, dialect: Dialect): boolean { return ( - statement.type === 'recreate_table' && dialect === 'sqlite' && !driver + statement.type === 'recreate_table' && dialect === 'sqlite' ); } @@ -3114,7 +3112,7 @@ class SQLiteRecreateTableConvertor extends Convertor { `INSERT INTO \`${newTableName}\`(${columnNames}) SELECT ${columnNames} FROM \`${tableName}\`;`, ); - // migrate data + // drop table sqlStatements.push( new SQLiteDropTableConvertor().convert({ type: 'drop_table', @@ -3141,11 +3139,10 @@ class SQLiteRecreateTableConvertor extends Convertor { } class LibSQLRecreateTableConvertor extends Convertor { - can(statement: JsonStatement, dialect: Dialect, driver?: Driver): boolean { + can(statement: JsonStatement, dialect: Dialect): boolean { return ( statement.type === 'recreate_table' - && dialect === 'sqlite' - && driver === 'turso' + && dialect === 'turso' ); } @@ -3175,7 +3172,7 @@ class LibSQLRecreateTableConvertor extends Convertor { `INSERT INTO \`${newTableName}\`(${columnNames}) SELECT ${columnNames} FROM \`${tableName}\`;`, ); - // migrate data + // drop table sqlStatements.push( new SQLiteDropTableConvertor().convert({ type: 'drop_table', @@ -3332,13 +3329,12 @@ convertors.push(new SingleStoreAlterTableAlterCompositePrimaryKeyConvertor()); // overloads for turso driver export function fromJson( statements: JsonStatement[], - dialect: Exclude, + dialect: Exclude, ): string[]; export function fromJson( statements: JsonStatement[], - dialect: 'sqlite', + dialect: 'sqlite' | 'turso', action?: 'push', - driver?: Driver, json2?: SQLiteSchemaSquashed, ): string[]; @@ -3346,13 +3342,12 @@ export function fromJson( statements: JsonStatement[], dialect: Dialect, action?: 'push', - driver?: Driver, json2?: SQLiteSchemaSquashed, ) { const result = statements .flatMap((statement) => { const filtered = convertors.filter((it) => { - return it.can(statement, dialect, driver); + return it.can(statement, dialect); }); const convertor = filtered.length === 1 ? filtered[0] : undefined; diff --git a/drizzle-kit/src/utils.ts b/drizzle-kit/src/utils.ts index 37d26f21e..f5ff34c6f 100644 --- a/drizzle-kit/src/utils.ts +++ b/drizzle-kit/src/utils.ts @@ -31,6 +31,12 @@ export type SQLiteDB = { ): Promise; }; +export type LibSQLDB = { + query: (sql: string, params?: any[]) => Promise; + run(query: string): Promise; + batchWithPragma?(queries: string[]): Promise; +}; + export const copy = (it: T): T => { return JSON.parse(JSON.stringify(it)); }; @@ -116,6 +122,8 @@ const validatorForDialect = (dialect: Dialect) => { return { validator: backwardCompatiblePgSchema, version: 7 }; case 'sqlite': return { validator: backwardCompatibleSqliteSchema, version: 6 }; + case 'turso': + return { validator: backwardCompatibleSqliteSchema, version: 6 }; case 'mysql': return { validator: backwardCompatibleMysqlSchema, version: 5 }; case 'singlestore': diff --git a/drizzle-kit/tests/cli-generate.test.ts b/drizzle-kit/tests/cli-generate.test.ts index 6eece0249..56a3a0d04 100644 --- a/drizzle-kit/tests/cli-generate.test.ts +++ b/drizzle-kit/tests/cli-generate.test.ts @@ -38,7 +38,6 @@ test('generate #1', async (t) => { schema: 'schema.ts', out: 'drizzle', bundle: false, - driver: undefined, }); }); @@ -58,7 +57,6 @@ test('generate #2', async (t) => { schema: 'schema.ts', out: 'out', bundle: false, - driver: undefined, }); }); @@ -75,7 +73,6 @@ test('generate #3', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, - driver: undefined, }); }); @@ -93,7 +90,6 @@ test('generate #4', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, - driver: undefined, }); }); @@ -110,7 +106,6 @@ test('generate #5', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, - driver: undefined, }); }); @@ -127,7 +122,6 @@ test('generate #6', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, - driver: undefined, }); }); @@ -147,7 +141,6 @@ test('generate #7', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: false, - driver: undefined, }); }); @@ -165,7 +158,6 @@ test('generate #8', async (t) => { schema: './schema.ts', out: 'drizzle', bundle: true, // expo driver - driver: 'expo', }); }); @@ -186,7 +178,6 @@ test('generate #9', async (t) => { schema: 'schema.ts', out: 'out', bundle: false, - driver: undefined, }); }); diff --git a/drizzle-kit/tests/cli-migrate.test.ts b/drizzle-kit/tests/cli-migrate.test.ts index a4ffec2f0..1425691f0 100644 --- a/drizzle-kit/tests/cli-migrate.test.ts +++ b/drizzle-kit/tests/cli-migrate.test.ts @@ -31,11 +31,10 @@ test('migrate #2', async (t) => { const res = await brotest(migrate, '--config=turso.config.ts'); if (res.type !== 'handler') assert.fail(res.type, 'handler'); expect(res.options).toStrictEqual({ - dialect: 'sqlite', + dialect: 'turso', out: 'drizzle', credentials: { authToken: 'token', - driver: 'turso', url: 'turso.dev', }, schema: undefined, // drizzle migrations table schema diff --git a/drizzle-kit/tests/cli-push.test.ts b/drizzle-kit/tests/cli-push.test.ts index fb1ed3a11..f5b84fdce 100644 --- a/drizzle-kit/tests/cli-push.test.ts +++ b/drizzle-kit/tests/cli-push.test.ts @@ -34,10 +34,9 @@ test('push #2', async (t) => { const res = await brotest(push, '--config=turso.config.ts'); if (res.type !== 'handler') assert.fail(res.type, 'handler'); expect(res.options).toStrictEqual({ - dialect: 'sqlite', + dialect: 'turso', credentials: { authToken: 'token', - driver: 'turso', url: 'turso.dev', }, force: false, @@ -46,7 +45,6 @@ test('push #2', async (t) => { tablesFilter: [], strict: false, verbose: false, - driver: 'turso', }); }); @@ -67,7 +65,6 @@ test('push #3', async (t) => { tablesFilter: [], strict: false, verbose: false, - driver: 'd1-http', }); }); diff --git a/drizzle-kit/tests/cli/turso.config.ts b/drizzle-kit/tests/cli/turso.config.ts index 089e4d216..85efe5934 100644 --- a/drizzle-kit/tests/cli/turso.config.ts +++ b/drizzle-kit/tests/cli/turso.config.ts @@ -2,8 +2,7 @@ import { defineConfig } from '../../src'; export default defineConfig({ schema: './schema.ts', - dialect: 'sqlite', - driver: 'turso', + dialect: 'turso', dbCredentials: { url: 'turso.dev', authToken: 'token', diff --git a/drizzle-kit/tests/push/libsql.test.ts b/drizzle-kit/tests/push/libsql.test.ts index 482caa995..5799b1fe3 100644 --- a/drizzle-kit/tests/push/libsql.test.ts +++ b/drizzle-kit/tests/push/libsql.test.ts @@ -217,9 +217,10 @@ test('added column not null and without default to table with data', async (t) = }; const table = getTableConfig(schema1.companies); + const seedStatements = [ - `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ("drizzle");`, - `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ("turso");`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('drizzle');`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('turso');`, ]; const { @@ -350,8 +351,8 @@ test('drop autoincrement. drop column with data', async (t) => { const table = getTableConfig(schema1.companies); const seedStatements = [ - `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, "drizzle");`, - `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, "turso");`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, 'drizzle');`, + `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, 'turso');`, ]; const { @@ -450,8 +451,8 @@ test('change autoincrement. table is part of foreign key', async (t) => { const { name: usersTableName } = getTableConfig(users1); const { name: companiesTableName } = getTableConfig(companies1); const seedStatements = [ - `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ("drizzle");`, - `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ("turso");`, + `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('drizzle');`, + `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('turso');`, `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES (1);`, `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES (2);`, ]; @@ -631,7 +632,7 @@ test('drop table with data', async (t) => { }; const seedStatements = [ - `INSERT INTO \`users\` ("name") VALUES ("drizzle")`, + `INSERT INTO \`users\` ("name") VALUES ('drizzle')`, ]; const { statements, diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json index 888f7efcb..7568c6e11 100644 --- a/drizzle-orm/package.json +++ b/drizzle-orm/package.json @@ -161,7 +161,7 @@ "@aws-sdk/client-rds-data": "^3.549.0", "@cloudflare/workers-types": "^4.20230904.0", "@electric-sql/pglite": "^0.1.1", - "@libsql/client": "^0.5.6", + "@libsql/client": "^0.10.0", "@neondatabase/serverless": "^0.9.0", "@op-engineering/op-sqlite": "^2.0.16", "@opentelemetry/api": "^1.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 209d2db3b..4917e0d20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.5.6)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.11.0)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7) + version: drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.10.0)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.11.0)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7) eslint: specifier: ^8.50.0 version: 8.50.0 @@ -123,8 +123,8 @@ importers: specifier: ^0.2.1 version: 0.2.2(hono@4.5.0)(zod@3.23.7) '@libsql/client': - specifier: ^0.4.2 - version: 0.4.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + specifier: ^0.10.0 + version: 0.10.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@neondatabase/serverless': specifier: ^0.9.1 version: 0.9.3 @@ -306,14 +306,14 @@ importers: specifier: ^0.1.1 version: 0.1.5 '@libsql/client': - specifier: ^0.5.6 - version: 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + specifier: ^0.10.0 + version: 0.10.0(bufferutil@4.0.8)(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.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) '@opentelemetry/api': specifier: ^1.4.1 version: 1.8.0 @@ -361,7 +361,7 @@ importers: version: 10.1.0 expo-sqlite: specifier: ^13.2.0 - version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) knex: specifier: ^2.4.2 version: 2.5.1(better-sqlite3@8.7.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7) @@ -2977,10 +2977,12 @@ packages: '@humanwhocodes/config-array@0.11.11': resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/config-array@0.11.13': resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} @@ -2993,9 +2995,11 @@ packages: '@humanwhocodes/object-schema@1.2.1': resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + deprecated: Use @eslint/object-schema instead '@humanwhocodes/object-schema@2.0.1': resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + deprecated: Use @eslint/object-schema instead '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} @@ -3081,31 +3085,31 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@libsql/client@0.4.3': - resolution: {integrity: sha512-AUYKnSPqAsFBVWBvmtrb4dG3pQlvTKT92eztAest9wQU2iJkabH8WzHLDb3dKFWKql7/kiCqvBQUVpozDwhekQ==} + '@libsql/client@0.10.0': + resolution: {integrity: sha512-2ERn08T4XOVx34yBtUPq0RDjAdd9TJ5qNH/izugr208ml2F94mk92qC64kXyDVQINodWJvp3kAdq6P4zTtCZ7g==} '@libsql/client@0.5.6': resolution: {integrity: sha512-UBjmDoxz75Z2sHdP+ETCROpeLA/77VMesiff8R4UWK1rnaWbh6/YoCLDILMJL3Rh0udQeKxjL8MjXthqohax+g==} - '@libsql/core@0.4.3': - resolution: {integrity: sha512-r28iYBtaLBW9RRgXPFh6cGCsVI/rwRlOzSOpAu/1PVTm6EJ3t233pUf97jETVHU0vjdr1d8VvV6fKAvJkokqCw==} + '@libsql/core@0.10.0': + resolution: {integrity: sha512-rqynAXGaiSpTsykOZdBtI1N4z4O+KZ6mt33K/aHeXAY0gSIfK/ctxuWa0Y1Bjo4FMz1idBTCXz4Ps5kITOvZZw==} '@libsql/core@0.5.6': resolution: {integrity: sha512-3vicUAydq6jPth410n4AsHHm1n2psTwvkSf94nfJlSXutGSZsl0updn2N/mJBgqUHkbuFoWZtlMifF0SwBj1xQ==} - '@libsql/darwin-arm64@0.2.0': - resolution: {integrity: sha512-+qyT2W/n5CFH1YZWv2mxW4Fsoo4dX9Z9M/nvbQqZ7H84J8hVegvVAsIGYzcK8xAeMEcpU5yGKB1Y9NoDY4hOSQ==} + '@libsql/darwin-arm64@0.3.18': + resolution: {integrity: sha512-Zt49dt+cwhPCkuoWgvjbQd4ckNfCJR5xzIAyhgHl3CBZqZaEuaXTOGKLNQT7bnFRPuQcdLt5PBT1cenKu2N6pA==} cpu: [arm64] os: [darwin] - '@libsql/darwin-arm64@0.3.18': - resolution: {integrity: sha512-Zt49dt+cwhPCkuoWgvjbQd4ckNfCJR5xzIAyhgHl3CBZqZaEuaXTOGKLNQT7bnFRPuQcdLt5PBT1cenKu2N6pA==} + '@libsql/darwin-arm64@0.3.19': + resolution: {integrity: sha512-rmOqsLcDI65zzxlUOoEiPJLhqmbFsZF6p4UJQ2kMqB+Kc0Rt5/A1OAdOZ/Wo8fQfJWjR1IbkbpEINFioyKf+nQ==} cpu: [arm64] os: [darwin] - '@libsql/darwin-x64@0.2.0': - resolution: {integrity: sha512-hwmO2mF1n8oDHKFrUju6Jv+n9iFtTf5JUK+xlnIE3Td0ZwGC/O1R/Z/btZTd9nD+vsvakC8SJT7/Q6YlWIkhEw==} - cpu: [x64] + '@libsql/darwin-arm64@0.4.1': + resolution: {integrity: sha512-XICT9/OyU8Aa9Iv1xZIHgvM09n/1OQUk3VC+s5uavzdiGHrDMkOWzN47JN7/FiMa/NWrcgoEiDMk3+e7mE53Ig==} + cpu: [arm64] os: [darwin] '@libsql/darwin-x64@0.3.18': @@ -3113,27 +3117,44 @@ packages: cpu: [x64] os: [darwin] + '@libsql/darwin-x64@0.3.19': + resolution: {integrity: sha512-q9O55B646zU+644SMmOQL3FIfpmEvdWpRpzubwFc2trsa+zoBlSkHuzU9v/C+UNoPHQVRMP7KQctJ455I/h/xw==} + cpu: [x64] + os: [darwin] + + '@libsql/darwin-x64@0.4.1': + resolution: {integrity: sha512-pSKxhRrhu4SsTD+IBRZXcs1SkwMdeAG1tv6Z/Ctp/sOEYrgkU8MDKLqkOr9NsmwpK4S0+JdwjkLMyhTkct/5TQ==} + cpu: [x64] + os: [darwin] + '@libsql/hrana-client@0.5.6': resolution: {integrity: sha512-mjQoAmejZ1atG+M3YR2ZW+rg6ceBByH/S/h17ZoYZkqbWrvohFhXyz2LFxj++ARMoY9m6w3RJJIRdJdmnEUlFg==} + '@libsql/hrana-client@0.6.2': + resolution: {integrity: sha512-MWxgD7mXLNf9FXXiM0bc90wCjZSpErWKr5mGza7ERy2FJNNMXd7JIOv+DepBA1FQTIfI8TFO4/QDYgaQC0goNw==} + '@libsql/isomorphic-fetch@0.1.12': resolution: {integrity: sha512-MRo4UcmjAGAa3ac56LoD5OE13m2p0lu0VEtZC2NZMcogM/jc5fU9YtMQ3qbPjFJ+u2BBjFZgMPkQaLS1dlMhpg==} + '@libsql/isomorphic-fetch@0.2.5': + resolution: {integrity: sha512-8s/B2TClEHms2yb+JGpsVRTPBfy1ih/Pq6h6gvyaNcYnMVJvgQRY7wAa8U2nD0dppbCuDU5evTNMEhrQ17ZKKg==} + engines: {node: '>=18.0.0'} + '@libsql/isomorphic-ws@0.1.5': resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} - '@libsql/linux-arm64-gnu@0.2.0': - resolution: {integrity: sha512-1w2lPXIYtnBaK5t/Ej5E8x7lPiE+jP3KATI/W4yei5Z/ONJh7jQW5PJ7sYU95vTME3hWEM1FXN6kvzcpFAte7w==} + '@libsql/linux-arm64-gnu@0.3.18': + resolution: {integrity: sha512-5m9xtDAhoyLSV54tho9uQ2ZIDeJWc0vU3Xpe/VK4+6bpURISs23qNhXiCrZnnq3oV0hFlBfcIgQUIATmb6jD2A==} cpu: [arm64] os: [linux] - '@libsql/linux-arm64-gnu@0.3.18': - resolution: {integrity: sha512-5m9xtDAhoyLSV54tho9uQ2ZIDeJWc0vU3Xpe/VK4+6bpURISs23qNhXiCrZnnq3oV0hFlBfcIgQUIATmb6jD2A==} + '@libsql/linux-arm64-gnu@0.3.19': + resolution: {integrity: sha512-mgeAUU1oqqh57k7I3cQyU6Trpdsdt607eFyEmH5QO7dv303ti+LjUvh1pp21QWV6WX7wZyjeJV1/VzEImB+jRg==} cpu: [arm64] os: [linux] - '@libsql/linux-arm64-musl@0.2.0': - resolution: {integrity: sha512-lkblBEJ7xuNiWNjP8DDq0rqoWccszfkUS7Efh5EjJ+GDWdCBVfh08mPofIZg0fZVLWQCY3j+VZCG1qZfATBizg==} + '@libsql/linux-arm64-gnu@0.4.1': + resolution: {integrity: sha512-9lpvb24tO2qZd9nq5dlq3ESA3hSKYWBIK7lJjfiCM6f7a70AUwBY9QoPJV9q4gILIyVnR1YBGrlm50nnb+dYgw==} cpu: [arm64] os: [linux] @@ -3142,9 +3163,14 @@ packages: cpu: [arm64] os: [linux] - '@libsql/linux-x64-gnu@0.2.0': - resolution: {integrity: sha512-+x/d289KeJydwOhhqSxKT+6MSQTCfLltzOpTzPccsvdt5fxg8CBi+gfvEJ4/XW23Sa+9bc7zodFP0i6MOlxX7w==} - cpu: [x64] + '@libsql/linux-arm64-musl@0.3.19': + resolution: {integrity: sha512-VEZtxghyK6zwGzU9PHohvNxthruSxBEnRrX7BSL5jQ62tN4n2JNepJ6SdzXp70pdzTfwroOj/eMwiPt94gkVRg==} + cpu: [arm64] + os: [linux] + + '@libsql/linux-arm64-musl@0.4.1': + resolution: {integrity: sha512-lyxi+lFxE+NcBRDMQCxCtDg3c4WcKAbc9u63d5+B23Vm+UgphD9XY4seu+tGrBy1MU2tuNVix7r9S7ECpAaVrA==} + cpu: [arm64] os: [linux] '@libsql/linux-x64-gnu@0.3.18': @@ -3152,8 +3178,13 @@ packages: cpu: [x64] os: [linux] - '@libsql/linux-x64-musl@0.2.0': - resolution: {integrity: sha512-5Xn0c5A6vKf9D1ASpgk7mef//FuY7t5Lktj/eiU4n3ryxG+6WTpqstTittJUgepVjcleLPYxIhQAYeYwTYH1IQ==} + '@libsql/linux-x64-gnu@0.3.19': + resolution: {integrity: sha512-2t/J7LD5w2f63wGihEO+0GxfTyYIyLGEvTFEsMO16XI5o7IS9vcSHrxsvAJs4w2Pf907uDjmc7fUfMg6L82BrQ==} + cpu: [x64] + os: [linux] + + '@libsql/linux-x64-gnu@0.4.1': + resolution: {integrity: sha512-psvuQ3UFBEmDFV8ZHG+WkUHIJiWv+elZ+zIPvOVedlIKdxG1O+8WthWUAhFHOGnbiyzc4sAZ4c3de1oCvyHxyQ==} cpu: [x64] os: [linux] @@ -3162,16 +3193,31 @@ packages: cpu: [x64] os: [linux] - '@libsql/win32-x64-msvc@0.2.0': - resolution: {integrity: sha512-rpK+trBIpRST15m3cMYg5aPaX7kvCIottxY7jZPINkKAaScvfbn9yulU/iZUM9YtuK96Y1ZmvwyVIK/Y5DzoMQ==} + '@libsql/linux-x64-musl@0.3.19': + resolution: {integrity: sha512-BLsXyJaL8gZD8+3W2LU08lDEd9MIgGds0yPy5iNPp8tfhXx3pV/Fge2GErN0FC+nzt4DYQtjL+A9GUMglQefXQ==} cpu: [x64] - os: [win32] + os: [linux] + + '@libsql/linux-x64-musl@0.4.1': + resolution: {integrity: sha512-PDidJ3AhGDqosGg3OAZzGxMFIbnuOALya4BoezJKl667AFv3x7BBQ30H81Mngsq3Fh8RkJkXSdWfL91+Txb1iA==} + cpu: [x64] + os: [linux] '@libsql/win32-x64-msvc@0.3.18': resolution: {integrity: sha512-9EEIHz+e8tTbx9TMkb8ByZnzxc0pYFirK1nSbqC6cFEST95fiY0NCfQ/zAzJxe90KckbjifX6BbO69eWIi3TAg==} cpu: [x64] os: [win32] + '@libsql/win32-x64-msvc@0.3.19': + resolution: {integrity: sha512-ay1X9AobE4BpzG0XPw1gplyLZPGHIgJOovvW23gUrukRegiUP62uzhpRbKNogLlUOynyXeq//prHgPXiebUfWg==} + cpu: [x64] + os: [win32] + + '@libsql/win32-x64-msvc@0.4.1': + resolution: {integrity: sha512-IdODVqV/PrdOnHA/004uWyorZQuRsB7U7bCRCE3vXgABj3eJLJGc6cv2C6ksEaEoVxJbD8k53H4VVAGrtYwXzQ==} + cpu: [x64] + os: [win32] + '@miniflare/core@2.14.2': resolution: {integrity: sha512-n/smm5ZTg7ilGM4fxO7Gxhbe573oc8Za06M3b2fO+lPWqF6NJcEKdCC+sJntVFbn3Cbbd2G1ChISmugPfmlCkQ==} engines: {node: '>=16.13'} @@ -4512,6 +4558,7 @@ packages: are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -6278,6 +6325,7 @@ packages: gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. generate-function@2.3.1: resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} @@ -7095,13 +7143,19 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libsql@0.2.0: - resolution: {integrity: sha512-ELBRqhpJx5Dap0187zKQnntZyk4EjlDHSrjIVL8t+fQ5e8IxbQTeYgZgigMjB1EvrETdkm0Y0VxBGhzPQ+t0Jg==} - cpu: [x64, arm64] - os: [darwin, linux, win32] - libsql@0.3.18: resolution: {integrity: sha512-lvhKr7WV3NLWRbXkjn/MeKqXOAqWKU0PX9QYrvDh7fneukapj+iUQ4qgJASrQyxcCrEsClXCQiiK5W6OoYPAlA==} + cpu: [x64, arm64, wasm32] + os: [darwin, linux, win32] + + libsql@0.3.19: + resolution: {integrity: sha512-Aj5cQ5uk/6fHdmeW0TiXK42FqUlwx7ytmMLPSaUQPin5HKKKuUPD62MAbN4OEweGBBI7q1BekoEN4gPUEL6MZA==} + cpu: [x64, arm64, wasm32] + os: [darwin, linux, win32] + + libsql@0.4.1: + resolution: {integrity: sha512-qZlR9Yu1zMBeLChzkE/cKfoKV3Esp9cn9Vx5Zirn4AVhDWPcjYhKwbtJcMuHehgk3mH+fJr9qW+3vesBWbQpBg==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lighthouse-logger@1.4.2: @@ -7788,6 +7842,7 @@ packages: npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. npx-import@1.1.4: resolution: {integrity: sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==} @@ -8305,6 +8360,9 @@ packages: bluebird: optional: true + promise-limit@2.7.0: + resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} + promise-retry@2.0.1: resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} engines: {node: '>=10'} @@ -8601,6 +8659,7 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@5.0.0: @@ -10210,8 +10269,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -10297,11 +10356,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)': + '@aws-sdk/client-sso-oidc@3.583.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -10340,7 +10399,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.2 transitivePeerDependencies: - - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso@3.478.0': @@ -10607,11 +10665,11 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/client-sts@3.583.0': + '@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/client-sso-oidc': 3.583.0 '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -10650,6 +10708,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/core@3.477.0': @@ -10804,7 +10863,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-env': 3.577.0 '@aws-sdk/credential-provider-process': 3.577.0 '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) @@ -11011,7 +11070,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0)': dependencies: - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/types': 3.577.0 '@smithy/property-provider': 3.0.0 '@smithy/types': 3.0.0 @@ -11212,7 +11271,7 @@ snapshots: '@aws-sdk/token-providers@3.568.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/client-sso-oidc': 3.583.0 '@aws-sdk/types': 3.567.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 @@ -11221,7 +11280,7 @@ snapshots: '@aws-sdk/token-providers@3.577.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/client-sso-oidc': 3.583.0 '@aws-sdk/types': 3.577.0 '@smithy/property-provider': 3.0.0 '@smithy/shared-ini-file-loader': 3.0.0 @@ -12893,7 +12952,7 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 - '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)': + '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3)': dependencies: '@babel/runtime': 7.24.6 '@expo/code-signing-certificates': 0.0.5 @@ -12911,7 +12970,7 @@ snapshots: '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) '@expo/spawn-async': 1.7.2 '@expo/xcpretty': 4.3.1 - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/dev-middleware': 0.74.83(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 @@ -13331,16 +13390,15 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@libsql/client@0.4.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@libsql/client@0.10.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: - '@libsql/core': 0.4.3 - '@libsql/hrana-client': 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@libsql/core': 0.10.0 + '@libsql/hrana-client': 0.6.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) js-base64: 3.7.7 - optionalDependencies: - libsql: 0.2.0 + libsql: 0.4.1 + promise-limit: 2.7.0 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)': @@ -13354,7 +13412,7 @@ snapshots: - encoding - utf-8-validate - '@libsql/core@0.4.3': + '@libsql/core@0.10.0': dependencies: js-base64: 3.7.7 @@ -13362,18 +13420,24 @@ snapshots: dependencies: js-base64: 3.7.7 - '@libsql/darwin-arm64@0.2.0': + '@libsql/darwin-arm64@0.3.18': optional: true - '@libsql/darwin-arm64@0.3.18': + '@libsql/darwin-arm64@0.3.19': optional: true - '@libsql/darwin-x64@0.2.0': + '@libsql/darwin-arm64@0.4.1': optional: true '@libsql/darwin-x64@0.3.18': optional: true + '@libsql/darwin-x64@0.3.19': + optional: true + + '@libsql/darwin-x64@0.4.1': + optional: true + '@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) @@ -13385,6 +13449,16 @@ snapshots: - encoding - utf-8-validate + '@libsql/hrana-client@0.6.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)': + dependencies: + '@libsql/isomorphic-fetch': 0.2.5 + '@libsql/isomorphic-ws': 0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3) + js-base64: 3.7.7 + node-fetch: 3.3.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@libsql/isomorphic-fetch@0.1.12(encoding@0.1.13)': dependencies: '@types/node-fetch': 2.6.11 @@ -13392,6 +13466,8 @@ snapshots: transitivePeerDependencies: - encoding + '@libsql/isomorphic-fetch@0.2.5': {} + '@libsql/isomorphic-ws@0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: '@types/ws': 8.5.11 @@ -13400,36 +13476,51 @@ snapshots: - bufferutil - utf-8-validate - '@libsql/linux-arm64-gnu@0.2.0': + '@libsql/linux-arm64-gnu@0.3.18': optional: true - '@libsql/linux-arm64-gnu@0.3.18': + '@libsql/linux-arm64-gnu@0.3.19': optional: true - '@libsql/linux-arm64-musl@0.2.0': + '@libsql/linux-arm64-gnu@0.4.1': optional: true '@libsql/linux-arm64-musl@0.3.18': optional: true - '@libsql/linux-x64-gnu@0.2.0': + '@libsql/linux-arm64-musl@0.3.19': + optional: true + + '@libsql/linux-arm64-musl@0.4.1': optional: true '@libsql/linux-x64-gnu@0.3.18': optional: true - '@libsql/linux-x64-musl@0.2.0': + '@libsql/linux-x64-gnu@0.3.19': + optional: true + + '@libsql/linux-x64-gnu@0.4.1': optional: true '@libsql/linux-x64-musl@0.3.18': optional: true - '@libsql/win32-x64-msvc@0.2.0': + '@libsql/linux-x64-musl@0.3.19': + optional: true + + '@libsql/linux-x64-musl@0.4.1': optional: true '@libsql/win32-x64-msvc@0.3.18': optional: true + '@libsql/win32-x64-msvc@0.3.19': + optional: true + + '@libsql/win32-x64-msvc@0.4.1': + optional: true + '@miniflare/core@2.14.2': dependencies: '@iarna/toml': 2.2.5 @@ -13507,10 +13598,10 @@ snapshots: rimraf: 3.0.2 optional: true - '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': dependencies: react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) '@opentelemetry/api@1.8.0': {} @@ -13647,7 +13738,7 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) @@ -13657,7 +13748,7 @@ snapshots: nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -13684,14 +13775,14 @@ snapshots: dependencies: joi: 17.13.1 - '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-clean': 13.6.6(encoding@0.1.13) '@react-native-community/cli-config': 13.6.6(encoding@0.1.13) '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-doctor': 13.6.6(encoding@0.1.13) '@react-native-community/cli-hermes': 13.6.6(encoding@0.1.13) - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) '@react-native-community/cli-types': 13.6.6 chalk: 4.1.2 @@ -13780,16 +13871,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/metro-babel-transformer': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.9 node-fetch: 2.7.0(encoding@0.1.13) querystring: 0.2.1 @@ -13804,7 +13895,7 @@ snapshots: '@react-native/debugger-frontend@0.74.83': {} - '@react-native/dev-middleware@0.74.83(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native/dev-middleware@0.74.83(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.74.83 @@ -13818,7 +13909,7 @@ snapshots: selfsigned: 2.4.1 serve-static: 1.15.0 temp-dir: 2.0.0 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -13841,12 +13932,12 @@ snapshots: '@react-native/normalize-colors@0.74.83': {} - '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) optionalDependencies: '@types/react': 18.3.1 @@ -15127,7 +15218,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.0.1 sirv: 2.0.4 - vitest: 1.6.0(@types/node@18.19.33)(@vitest/ui@1.6.0)(lightningcss@1.25.1)(terser@5.31.0) + vitest: 1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(lightningcss@1.25.1)(terser@5.31.0) '@vitest/utils@1.6.0': dependencies: @@ -16316,11 +16407,11 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.5.6)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.11.0)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7): + drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.10.0)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.11.0)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7): optionalDependencies: '@aws-sdk/client-rds-data': 3.583.0 '@cloudflare/workers-types': 4.20240524.0 - '@libsql/client': 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@libsql/client': 0.10.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@neondatabase/serverless': 0.9.3 '@opentelemetry/api': 1.8.0 '@planetscale/database': 1.18.0 @@ -17157,35 +17248,35 @@ snapshots: expand-template@2.0.3: {} - expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@react-native/assets-registry': 0.74.83 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) - expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) invariant: 2.2.4 md5-file: 3.2.3 transitivePeerDependencies: - supports-color - expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/config': 9.0.2 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) transitivePeerDependencies: - supports-color - expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) fontfaceobserver: 2.3.0 - expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) expo-modules-autolinking@1.11.1: dependencies: @@ -17199,24 +17290,24 @@ snapshots: dependencies: invariant: 2.2.4 - expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/websql': 1.0.1 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13): + expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/runtime': 7.24.6 - '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1) + '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3) '@expo/config': 9.0.2 '@expo/config-plugins': 8.0.4 '@expo/metro-config': 0.18.4 '@expo/vector-icons': 14.0.2 babel-preset-expo: 11.0.6(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) expo-modules-autolinking: 1.11.1 expo-modules-core: 1.12.11 fbemitter: 3.0.0(encoding@0.1.13) @@ -18357,24 +18448,11 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libsql@0.2.0: - dependencies: - '@neon-rs/load': 0.0.4 - detect-libc: 2.0.2 - optionalDependencies: - '@libsql/darwin-arm64': 0.2.0 - '@libsql/darwin-x64': 0.2.0 - '@libsql/linux-arm64-gnu': 0.2.0 - '@libsql/linux-arm64-musl': 0.2.0 - '@libsql/linux-x64-gnu': 0.2.0 - '@libsql/linux-x64-musl': 0.2.0 - '@libsql/win32-x64-msvc': 0.2.0 - optional: true - libsql@0.3.18: dependencies: '@neon-rs/load': 0.0.4 detect-libc: 2.0.2 + libsql: 0.3.19 optionalDependencies: '@libsql/darwin-arm64': 0.3.18 '@libsql/darwin-x64': 0.3.18 @@ -18384,6 +18462,33 @@ snapshots: '@libsql/linux-x64-musl': 0.3.18 '@libsql/win32-x64-msvc': 0.3.18 + libsql@0.3.19: + dependencies: + '@neon-rs/load': 0.0.4 + detect-libc: 2.0.2 + optionalDependencies: + '@libsql/darwin-arm64': 0.3.19 + '@libsql/darwin-x64': 0.3.19 + '@libsql/linux-arm64-gnu': 0.3.19 + '@libsql/linux-arm64-musl': 0.3.19 + '@libsql/linux-x64-gnu': 0.3.19 + '@libsql/linux-x64-musl': 0.3.19 + '@libsql/win32-x64-msvc': 0.3.19 + + libsql@0.4.1: + dependencies: + '@neon-rs/load': 0.0.4 + detect-libc: 2.0.2 + libsql: 0.3.19 + optionalDependencies: + '@libsql/darwin-arm64': 0.4.1 + '@libsql/darwin-x64': 0.4.1 + '@libsql/linux-arm64-gnu': 0.4.1 + '@libsql/linux-arm64-musl': 0.4.1 + '@libsql/linux-x64-gnu': 0.4.1 + '@libsql/linux-x64-musl': 0.4.1 + '@libsql/win32-x64-msvc': 0.4.1 + lighthouse-logger@1.4.2: dependencies: debug: 2.6.9 @@ -18692,12 +18797,12 @@ snapshots: metro-core: 0.80.9 rimraf: 3.0.2 - metro-config@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro-config@0.80.9(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.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-cache: 0.80.9 metro-core: 0.80.9 metro-runtime: 0.80.9 @@ -18773,13 +18878,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/core': 7.24.6 '@babel/generator': 7.24.6 '@babel/parser': 7.24.6 '@babel/types': 7.24.6 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 @@ -18793,7 +18898,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/code-frame': 7.24.6 '@babel/core': 7.24.6 @@ -18819,7 +18924,7 @@ snapshots: metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.9 metro-file-map: 0.80.9 metro-resolver: 0.80.9 @@ -18827,7 +18932,7 @@ snapshots: metro-source-map: 0.80.9 metro-symbolicate: 0.80.9 metro-transform-plugins: 0.80.9 - metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) mime-types: 2.1.35 node-fetch: 2.7.0(encoding@0.1.13) nullthrows: 1.1.1 @@ -18836,7 +18941,7 @@ snapshots: source-map: 0.5.7 strip-ansi: 6.0.1 throat: 5.0.0 - ws: 7.5.9(bufferutil@4.0.8) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -19641,6 +19746,8 @@ snapshots: promise-inflight@1.0.1: optional: true + promise-limit@2.7.0: {} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 @@ -19720,10 +19827,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-devtools-core@5.2.0(bufferutil@4.0.8): + react-devtools-core@5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: shell-quote: 1.8.1 - ws: 7.5.9(bufferutil@4.0.8) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -19736,19 +19843,19 @@ snapshots: react-is@18.3.1: {} - react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1): + react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-platform-android': 13.6.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 13.6.6(encoding@0.1.13) '@react-native/assets-registry': 0.74.83 '@react-native/codegen': 0.74.83(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/gradle-plugin': 0.74.83 '@react-native/js-polyfills': 0.74.83 '@react-native/normalize-colors': 0.74.83 - '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -19767,14 +19874,14 @@ snapshots: pretty-format: 26.6.2 promise: 8.3.0 react: 18.3.1 - react-devtools-core: 5.2.0(bufferutil@4.0.8) + react-devtools-core: 5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) react-refresh: 0.14.2 react-shallow-renderer: 16.15.0(react@18.3.1) 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) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 optionalDependencies: '@types/react': 18.3.1 @@ -21627,15 +21734,17 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.0.2 - ws@6.2.2(bufferutil@4.0.8): + 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(bufferutil@4.0.8): + 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.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): optionalDependencies: From 15c6b1a6399e741b6c50bebc2b4a1f27fe69f822 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Mon, 2 Sep 2024 16:18:02 +0300 Subject: [PATCH 23/56] fixed dialects in introspect musql, psql, sqlite updated packages --- drizzle-kit/src/cli/commands/introspect.ts | 6 +- drizzle-orm/package.json | 2 +- integration-tests/package.json | 2 +- pnpm-lock.yaml | 334 +++++++++------------ 4 files changed, 143 insertions(+), 201 deletions(-) diff --git a/drizzle-kit/src/cli/commands/introspect.ts b/drizzle-kit/src/cli/commands/introspect.ts index 0940385af..9b5a044f6 100644 --- a/drizzle-kit/src/cli/commands/introspect.ts +++ b/drizzle-kit/src/cli/commands/introspect.ts @@ -211,7 +211,7 @@ export const introspectMysql = async ( writeFileSync(relationsFile, relationsTs.file); console.log(); - const { snapshots, journal } = prepareOutFolder(out, 'postgresql'); + const { snapshots, journal } = prepareOutFolder(out, 'mysql'); if (snapshots.length === 0) { const { sqlStatements, _meta } = await applyMysqlSnapshotsDiff( @@ -418,7 +418,7 @@ export const introspectSqlite = async ( writeFileSync(relationsFile, relationsTs.file); console.log(); - const { snapshots, journal } = prepareOutFolder(out, 'postgresql'); + const { snapshots, journal } = prepareOutFolder(out, 'sqlite'); if (snapshots.length === 0) { const { sqlStatements, _meta } = await applySqliteSnapshotsDiff( @@ -529,7 +529,7 @@ export const introspectLibSQL = async ( writeFileSync(relationsFile, relationsTs.file); console.log(); - const { snapshots, journal } = prepareOutFolder(out, 'postgresql'); + const { snapshots, journal } = prepareOutFolder(out, 'sqlite'); if (snapshots.length === 0) { const { sqlStatements, _meta } = await applySqliteSnapshotsDiff( diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json index 7568c6e11..11970a77e 100644 --- a/drizzle-orm/package.json +++ b/drizzle-orm/package.json @@ -46,7 +46,7 @@ "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", "@electric-sql/pglite": ">=0.1.1", - "@libsql/client": "*", + "@libsql/client": ">=0.10.0", "@neondatabase/serverless": ">=0.1", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", diff --git a/integration-tests/package.json b/integration-tests/package.json index a4fcab0b2..78f36fe30 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -15,6 +15,7 @@ "license": "Apache-2.0", "private": true, "devDependencies": { + "@libsql/client": "^0.10.0", "@neondatabase/serverless": "0.9.0", "@originjs/vite-plugin-commonjs": "^1.0.3", "@paralleldrive/cuid2": "^2.2.2", @@ -41,7 +42,6 @@ "@aws-sdk/client-rds-data": "^3.549.0", "@aws-sdk/credential-providers": "^3.549.0", "@electric-sql/pglite": "^0.1.1", - "@libsql/client": "^0.5.6", "@miniflare/d1": "^2.14.2", "@miniflare/shared": "^2.14.2", "@planetscale/database": "^1.16.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4917e0d20..dfb3ae80d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -313,7 +313,7 @@ importers: version: 0.9.0 '@op-engineering/op-sqlite': specifier: ^2.0.16 - version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) + version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) '@opentelemetry/api': specifier: ^1.4.1 version: 1.8.0 @@ -361,7 +361,7 @@ importers: version: 10.1.0 expo-sqlite: specifier: ^13.2.0 - version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) knex: specifier: ^2.4.2 version: 2.5.1(better-sqlite3@8.7.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7) @@ -554,9 +554,6 @@ importers: '@electric-sql/pglite': specifier: ^0.1.1 version: 0.1.5 - '@libsql/client': - specifier: ^0.5.6 - version: 0.5.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@miniflare/d1': specifier: ^2.14.2 version: 2.14.2 @@ -648,6 +645,9 @@ importers: specifier: ^3.20.2 version: 3.23.7 devDependencies: + '@libsql/client': + specifier: ^0.10.0 + version: 0.10.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) '@neondatabase/serverless': specifier: 0.9.0 version: 0.9.0 @@ -3088,20 +3088,9 @@ packages: '@libsql/client@0.10.0': resolution: {integrity: sha512-2ERn08T4XOVx34yBtUPq0RDjAdd9TJ5qNH/izugr208ml2F94mk92qC64kXyDVQINodWJvp3kAdq6P4zTtCZ7g==} - '@libsql/client@0.5.6': - resolution: {integrity: sha512-UBjmDoxz75Z2sHdP+ETCROpeLA/77VMesiff8R4UWK1rnaWbh6/YoCLDILMJL3Rh0udQeKxjL8MjXthqohax+g==} - '@libsql/core@0.10.0': resolution: {integrity: sha512-rqynAXGaiSpTsykOZdBtI1N4z4O+KZ6mt33K/aHeXAY0gSIfK/ctxuWa0Y1Bjo4FMz1idBTCXz4Ps5kITOvZZw==} - '@libsql/core@0.5.6': - resolution: {integrity: sha512-3vicUAydq6jPth410n4AsHHm1n2psTwvkSf94nfJlSXutGSZsl0updn2N/mJBgqUHkbuFoWZtlMifF0SwBj1xQ==} - - '@libsql/darwin-arm64@0.3.18': - resolution: {integrity: sha512-Zt49dt+cwhPCkuoWgvjbQd4ckNfCJR5xzIAyhgHl3CBZqZaEuaXTOGKLNQT7bnFRPuQcdLt5PBT1cenKu2N6pA==} - cpu: [arm64] - os: [darwin] - '@libsql/darwin-arm64@0.3.19': resolution: {integrity: sha512-rmOqsLcDI65zzxlUOoEiPJLhqmbFsZF6p4UJQ2kMqB+Kc0Rt5/A1OAdOZ/Wo8fQfJWjR1IbkbpEINFioyKf+nQ==} cpu: [arm64] @@ -3112,11 +3101,6 @@ packages: cpu: [arm64] os: [darwin] - '@libsql/darwin-x64@0.3.18': - resolution: {integrity: sha512-faq6HUGDaNaueeqPei5cypHaD/hhazUyfHo094CXiEeRZq6ZKtNl5PHdlr8jE/Uw8USNpVVQaLdnvSgKcpRPHw==} - cpu: [x64] - os: [darwin] - '@libsql/darwin-x64@0.3.19': resolution: {integrity: sha512-q9O55B646zU+644SMmOQL3FIfpmEvdWpRpzubwFc2trsa+zoBlSkHuzU9v/C+UNoPHQVRMP7KQctJ455I/h/xw==} cpu: [x64] @@ -3127,15 +3111,9 @@ packages: cpu: [x64] os: [darwin] - '@libsql/hrana-client@0.5.6': - resolution: {integrity: sha512-mjQoAmejZ1atG+M3YR2ZW+rg6ceBByH/S/h17ZoYZkqbWrvohFhXyz2LFxj++ARMoY9m6w3RJJIRdJdmnEUlFg==} - '@libsql/hrana-client@0.6.2': resolution: {integrity: sha512-MWxgD7mXLNf9FXXiM0bc90wCjZSpErWKr5mGza7ERy2FJNNMXd7JIOv+DepBA1FQTIfI8TFO4/QDYgaQC0goNw==} - '@libsql/isomorphic-fetch@0.1.12': - resolution: {integrity: sha512-MRo4UcmjAGAa3ac56LoD5OE13m2p0lu0VEtZC2NZMcogM/jc5fU9YtMQ3qbPjFJ+u2BBjFZgMPkQaLS1dlMhpg==} - '@libsql/isomorphic-fetch@0.2.5': resolution: {integrity: sha512-8s/B2TClEHms2yb+JGpsVRTPBfy1ih/Pq6h6gvyaNcYnMVJvgQRY7wAa8U2nD0dppbCuDU5evTNMEhrQ17ZKKg==} engines: {node: '>=18.0.0'} @@ -3143,11 +3121,6 @@ packages: '@libsql/isomorphic-ws@0.1.5': resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} - '@libsql/linux-arm64-gnu@0.3.18': - resolution: {integrity: sha512-5m9xtDAhoyLSV54tho9uQ2ZIDeJWc0vU3Xpe/VK4+6bpURISs23qNhXiCrZnnq3oV0hFlBfcIgQUIATmb6jD2A==} - cpu: [arm64] - os: [linux] - '@libsql/linux-arm64-gnu@0.3.19': resolution: {integrity: sha512-mgeAUU1oqqh57k7I3cQyU6Trpdsdt607eFyEmH5QO7dv303ti+LjUvh1pp21QWV6WX7wZyjeJV1/VzEImB+jRg==} cpu: [arm64] @@ -3158,11 +3131,6 @@ packages: cpu: [arm64] os: [linux] - '@libsql/linux-arm64-musl@0.3.18': - resolution: {integrity: sha512-oYD5+oM2gPEalp+EoR5DVQBRtdGjLsocjsRbQs5O2m4WOBJKER7VUfDYZHsifLGZoBSc11Yo6s9IR9rjGWy20w==} - cpu: [arm64] - os: [linux] - '@libsql/linux-arm64-musl@0.3.19': resolution: {integrity: sha512-VEZtxghyK6zwGzU9PHohvNxthruSxBEnRrX7BSL5jQ62tN4n2JNepJ6SdzXp70pdzTfwroOj/eMwiPt94gkVRg==} cpu: [arm64] @@ -3173,11 +3141,6 @@ packages: cpu: [arm64] os: [linux] - '@libsql/linux-x64-gnu@0.3.18': - resolution: {integrity: sha512-QDSSP60nS8KIldGE7H3bpEflQHiL1erwED6huoVJdmDFxsyDJX2CYdWUWW8Za0ZUOvUbnEWAOyMhp6j1dBbZqw==} - cpu: [x64] - os: [linux] - '@libsql/linux-x64-gnu@0.3.19': resolution: {integrity: sha512-2t/J7LD5w2f63wGihEO+0GxfTyYIyLGEvTFEsMO16XI5o7IS9vcSHrxsvAJs4w2Pf907uDjmc7fUfMg6L82BrQ==} cpu: [x64] @@ -3188,11 +3151,6 @@ packages: cpu: [x64] os: [linux] - '@libsql/linux-x64-musl@0.3.18': - resolution: {integrity: sha512-5SXwTlaLCUPzxYyq+P0c7Ko7tcEjpd1X6RZKe1DuRFmJPg6f7j2+LrPEhMSIbqKcrl5ACUUAyoKmGZqNYwz23w==} - cpu: [x64] - os: [linux] - '@libsql/linux-x64-musl@0.3.19': resolution: {integrity: sha512-BLsXyJaL8gZD8+3W2LU08lDEd9MIgGds0yPy5iNPp8tfhXx3pV/Fge2GErN0FC+nzt4DYQtjL+A9GUMglQefXQ==} cpu: [x64] @@ -3203,11 +3161,6 @@ packages: cpu: [x64] os: [linux] - '@libsql/win32-x64-msvc@0.3.18': - resolution: {integrity: sha512-9EEIHz+e8tTbx9TMkb8ByZnzxc0pYFirK1nSbqC6cFEST95fiY0NCfQ/zAzJxe90KckbjifX6BbO69eWIi3TAg==} - cpu: [x64] - os: [win32] - '@libsql/win32-x64-msvc@0.3.19': resolution: {integrity: sha512-ay1X9AobE4BpzG0XPw1gplyLZPGHIgJOovvW23gUrukRegiUP62uzhpRbKNogLlUOynyXeq//prHgPXiebUfWg==} cpu: [x64] @@ -4105,9 +4058,6 @@ packages: '@types/minimist@1.2.2': resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} - '@types/node-fetch@2.6.11': - resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} - '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} @@ -7143,11 +7093,6 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libsql@0.3.18: - resolution: {integrity: sha512-lvhKr7WV3NLWRbXkjn/MeKqXOAqWKU0PX9QYrvDh7fneukapj+iUQ4qgJASrQyxcCrEsClXCQiiK5W6OoYPAlA==} - cpu: [x64, arm64, wasm32] - os: [darwin, linux, win32] - libsql@0.3.19: resolution: {integrity: sha512-Aj5cQ5uk/6fHdmeW0TiXK42FqUlwx7ytmMLPSaUQPin5HKKKuUPD62MAbN4OEweGBBI7q1BekoEN4gPUEL6MZA==} cpu: [x64, arm64, wasm32] @@ -10270,7 +10215,7 @@ snapshots: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 '@aws-sdk/client-sso-oidc': 3.583.0 - '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) + '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -10360,9 +10305,9 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) + '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/core': 3.582.0 - '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)) '@aws-sdk/middleware-host-header': 3.577.0 '@aws-sdk/middleware-logger': 3.577.0 '@aws-sdk/middleware-recursion-detection': 3.577.0 @@ -10665,7 +10610,7 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)': + '@aws-sdk/client-sts@3.583.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 @@ -10707,6 +10652,51 @@ snapshots: '@smithy/util-retry': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)': + dependencies: + '@aws-crypto/sha256-browser': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/core': 3.582.0 + '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)) + '@aws-sdk/middleware-host-header': 3.577.0 + '@aws-sdk/middleware-logger': 3.577.0 + '@aws-sdk/middleware-recursion-detection': 3.577.0 + '@aws-sdk/middleware-user-agent': 3.583.0 + '@aws-sdk/region-config-resolver': 3.577.0 + '@aws-sdk/types': 3.577.0 + '@aws-sdk/util-endpoints': 3.583.0 + '@aws-sdk/util-user-agent-browser': 3.577.0 + '@aws-sdk/util-user-agent-node': 3.577.0 + '@smithy/config-resolver': 3.0.0 + '@smithy/core': 2.0.1 + '@smithy/fetch-http-handler': 3.0.1 + '@smithy/hash-node': 3.0.0 + '@smithy/invalid-dependency': 3.0.0 + '@smithy/middleware-content-length': 3.0.0 + '@smithy/middleware-endpoint': 3.0.0 + '@smithy/middleware-retry': 3.0.1 + '@smithy/middleware-serde': 3.0.0 + '@smithy/middleware-stack': 3.0.0 + '@smithy/node-config-provider': 3.0.0 + '@smithy/node-http-handler': 3.0.0 + '@smithy/protocol-http': 4.0.0 + '@smithy/smithy-client': 3.0.1 + '@smithy/types': 3.0.0 + '@smithy/url-parser': 3.0.0 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.1 + '@smithy/util-defaults-mode-node': 3.0.1 + '@smithy/util-endpoints': 2.0.0 + '@smithy/util-middleware': 3.0.0 + '@smithy/util-retry': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.2 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt @@ -10863,7 +10853,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: - '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) + '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/credential-provider-env': 3.577.0 '@aws-sdk/credential-provider-process': 3.577.0 '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) @@ -10951,6 +10941,25 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt + '@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0))': + dependencies: + '@aws-sdk/credential-provider-env': 3.577.0 + '@aws-sdk/credential-provider-http': 3.582.0 + '@aws-sdk/credential-provider-ini': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/credential-provider-process': 3.577.0 + '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) + '@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)) + '@aws-sdk/types': 3.577.0 + '@smithy/credential-provider-imds': 3.0.0 + '@smithy/property-provider': 3.0.0 + '@smithy/shared-ini-file-loader': 3.0.0 + '@smithy/types': 3.0.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + '@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: '@aws-sdk/credential-provider-env': 3.577.0 @@ -11068,7 +11077,7 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.6.2 - '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0)': + '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0))': dependencies: '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/types': 3.577.0 @@ -11076,6 +11085,14 @@ snapshots: '@smithy/types': 3.0.0 tslib: 2.6.2 + '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0)': + dependencies: + '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/types': 3.577.0 + '@smithy/property-provider': 3.0.0 + '@smithy/types': 3.0.0 + tslib: 2.6.2 + '@aws-sdk/credential-providers@3.569.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-sdk/client-cognito-identity': 3.569.0 @@ -12952,7 +12969,7 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 - '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3)': + '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)': dependencies: '@babel/runtime': 7.24.6 '@expo/code-signing-certificates': 0.0.5 @@ -12970,7 +12987,7 @@ snapshots: '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) '@expo/spawn-async': 1.7.2 '@expo/xcpretty': 4.3.1 - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) '@urql/core': 2.3.6(graphql@15.8.0) '@urql/exchange-retry': 0.3.0(graphql@15.8.0) accepts: 1.3.8 @@ -13401,54 +13418,22 @@ snapshots: - bufferutil - 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.7 - libsql: 0.3.18 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - '@libsql/core@0.10.0': dependencies: js-base64: 3.7.7 - '@libsql/core@0.5.6': - dependencies: - js-base64: 3.7.7 - - '@libsql/darwin-arm64@0.3.18': - optional: true - '@libsql/darwin-arm64@0.3.19': optional: true '@libsql/darwin-arm64@0.4.1': optional: true - '@libsql/darwin-x64@0.3.18': - optional: true - '@libsql/darwin-x64@0.3.19': optional: true '@libsql/darwin-x64@0.4.1': optional: true - '@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.7 - node-fetch: 3.3.2 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - '@libsql/hrana-client@0.6.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: '@libsql/isomorphic-fetch': 0.2.5 @@ -13459,62 +13444,40 @@ snapshots: - bufferutil - utf-8-validate - '@libsql/isomorphic-fetch@0.1.12(encoding@0.1.13)': - dependencies: - '@types/node-fetch': 2.6.11 - node-fetch: 2.7.0(encoding@0.1.13) - transitivePeerDependencies: - - encoding - '@libsql/isomorphic-fetch@0.2.5': {} '@libsql/isomorphic-ws@0.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.3)': dependencies: '@types/ws': 8.5.11 - ws: 8.17.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate - '@libsql/linux-arm64-gnu@0.3.18': - optional: true - '@libsql/linux-arm64-gnu@0.3.19': optional: true '@libsql/linux-arm64-gnu@0.4.1': optional: true - '@libsql/linux-arm64-musl@0.3.18': - optional: true - '@libsql/linux-arm64-musl@0.3.19': optional: true '@libsql/linux-arm64-musl@0.4.1': optional: true - '@libsql/linux-x64-gnu@0.3.18': - optional: true - '@libsql/linux-x64-gnu@0.3.19': optional: true '@libsql/linux-x64-gnu@0.4.1': optional: true - '@libsql/linux-x64-musl@0.3.18': - optional: true - '@libsql/linux-x64-musl@0.3.19': optional: true '@libsql/linux-x64-musl@0.4.1': optional: true - '@libsql/win32-x64-msvc@0.3.18': - optional: true - '@libsql/win32-x64-msvc@0.3.19': optional: true @@ -13598,10 +13561,10 @@ snapshots: rimraf: 3.0.2 optional: true - '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': + '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) '@opentelemetry/api@1.8.0': {} @@ -13738,7 +13701,7 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': dependencies: '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) @@ -13748,7 +13711,7 @@ snapshots: nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 6.2.2(bufferutil@4.0.8) transitivePeerDependencies: - bufferutil - encoding @@ -13775,14 +13738,14 @@ snapshots: dependencies: joi: 17.13.1 - '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': dependencies: '@react-native-community/cli-clean': 13.6.6(encoding@0.1.13) '@react-native-community/cli-config': 13.6.6(encoding@0.1.13) '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-doctor': 13.6.6(encoding@0.1.13) '@react-native-community/cli-hermes': 13.6.6(encoding@0.1.13) - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) '@react-native-community/cli-types': 13.6.6 chalk: 4.1.2 @@ -13871,16 +13834,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) metro-core: 0.80.9 node-fetch: 2.7.0(encoding@0.1.13) querystring: 0.2.1 @@ -13895,7 +13858,7 @@ snapshots: '@react-native/debugger-frontend@0.74.83': {} - '@react-native/dev-middleware@0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': + '@react-native/dev-middleware@0.74.83(bufferutil@4.0.8)(encoding@0.1.13)': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.74.83 @@ -13909,7 +13872,7 @@ snapshots: selfsigned: 2.4.1 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(bufferutil@4.0.8) transitivePeerDependencies: - bufferutil - encoding @@ -13932,12 +13895,12 @@ snapshots: '@react-native/normalize-colors@0.74.83': {} - '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': + '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 @@ -14778,11 +14741,6 @@ snapshots: '@types/minimist@1.2.2': {} - '@types/node-fetch@2.6.11': - dependencies: - '@types/node': 20.12.12 - form-data: 4.0.0 - '@types/node-forge@1.3.11': dependencies: '@types/node': 20.12.12 @@ -17248,35 +17206,35 @@ snapshots: expand-template@2.0.3: {} - expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): dependencies: '@react-native/assets-registry': 0.74.83 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) invariant: 2.2.4 md5-file: 3.2.3 transitivePeerDependencies: - supports-color - expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): dependencies: '@expo/config': 9.0.2 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) transitivePeerDependencies: - supports-color - expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) - expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) fontfaceobserver: 2.3.0 - expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) expo-modules-autolinking@1.11.1: dependencies: @@ -17290,24 +17248,24 @@ snapshots: dependencies: invariant: 2.2.4 - expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): + expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): dependencies: '@expo/websql': 1.0.1 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) - expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13): dependencies: '@babel/runtime': 7.24.6 - '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3) + '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1) '@expo/config': 9.0.2 '@expo/config-plugins': 8.0.4 '@expo/metro-config': 0.18.4 '@expo/vector-icons': 14.0.2 babel-preset-expo: 11.0.6(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) - expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) expo-modules-autolinking: 1.11.1 expo-modules-core: 1.12.11 fbemitter: 3.0.0(encoding@0.1.13) @@ -18448,20 +18406,6 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libsql@0.3.18: - dependencies: - '@neon-rs/load': 0.0.4 - detect-libc: 2.0.2 - libsql: 0.3.19 - optionalDependencies: - '@libsql/darwin-arm64': 0.3.18 - '@libsql/darwin-x64': 0.3.18 - '@libsql/linux-arm64-gnu': 0.3.18 - '@libsql/linux-arm64-musl': 0.3.18 - '@libsql/linux-x64-gnu': 0.3.18 - '@libsql/linux-x64-musl': 0.3.18 - '@libsql/win32-x64-msvc': 0.3.18 - libsql@0.3.19: dependencies: '@neon-rs/load': 0.0.4 @@ -18797,12 +18741,12 @@ snapshots: metro-core: 0.80.9 rimraf: 3.0.2 - metro-config@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + metro-config@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): dependencies: connect: 3.7.0 cosmiconfig: 5.2.1 jest-validate: 29.7.0 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) metro-cache: 0.80.9 metro-core: 0.80.9 metro-runtime: 0.80.9 @@ -18878,13 +18822,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): dependencies: '@babel/core': 7.24.6 '@babel/generator': 7.24.6 '@babel/parser': 7.24.6 '@babel/types': 7.24.6 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 @@ -18898,7 +18842,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): + metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): dependencies: '@babel/code-frame': 7.24.6 '@babel/core': 7.24.6 @@ -18924,7 +18868,7 @@ snapshots: metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) metro-core: 0.80.9 metro-file-map: 0.80.9 metro-resolver: 0.80.9 @@ -18932,7 +18876,7 @@ snapshots: metro-source-map: 0.80.9 metro-symbolicate: 0.80.9 metro-transform-plugins: 0.80.9 - metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) mime-types: 2.1.35 node-fetch: 2.7.0(encoding@0.1.13) nullthrows: 1.1.1 @@ -18941,7 +18885,7 @@ snapshots: 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(bufferutil@4.0.8) yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -19827,10 +19771,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-devtools-core@5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): + react-devtools-core@5.2.0(bufferutil@4.0.8): dependencies: shell-quote: 1.8.1 - ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 7.5.9(bufferutil@4.0.8) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -19843,19 +19787,19 @@ snapshots: react-is@18.3.1: {} - react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3): + react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) '@react-native-community/cli-platform-android': 13.6.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 13.6.6(encoding@0.1.13) '@react-native/assets-registry': 0.74.83 '@react-native/codegen': 0.74.83(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) '@react-native/gradle-plugin': 0.74.83 '@react-native/js-polyfills': 0.74.83 '@react-native/normalize-colors': 0.74.83 - '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) + '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -19874,14 +19818,14 @@ snapshots: pretty-format: 26.6.2 promise: 8.3.0 react: 18.3.1 - react-devtools-core: 5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + react-devtools-core: 5.2.0(bufferutil@4.0.8) react-refresh: 0.14.2 react-shallow-renderer: 16.15.0(react@18.3.1) 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(bufferutil@4.0.8) yargs: 17.7.2 optionalDependencies: '@types/react': 18.3.1 @@ -21734,17 +21678,15 @@ 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(bufferutil@4.0.8): 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): + ws@7.5.9(bufferutil@4.0.8): optionalDependencies: bufferutil: 4.0.8 - utf-8-validate: 6.0.3 ws@8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): optionalDependencies: From deb7533a9926870a1b4e7506a1ea2cfa964c0666 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Mon, 2 Sep 2024 17:28:57 +0300 Subject: [PATCH 24/56] updated tests and removed pragma from push in libsql --- .../src/cli/commands/libSqlPushUtils.ts | 7 ------ drizzle-kit/src/cli/connections.ts | 1 + .../src/serializer/sqliteSerializer.ts | 3 ++- drizzle-kit/tests/push/libsql.test.ts | 24 ++++++++----------- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts index 98c95f089..01bb61334 100644 --- a/drizzle-kit/src/cli/commands/libSqlPushUtils.ts +++ b/drizzle-kit/src/cli/commands/libSqlPushUtils.ts @@ -298,17 +298,10 @@ export const libSqlLogSuggestionsAndReturn = async ( continue; } - // ! for libsql it will break - const [{ foreign_keys: pragmaState }] = await connection.query<{ - foreign_keys: number; - }>(`PRAGMA foreign_keys;`); - - if (pragmaState) statementsToExecute.push(`PRAGMA foreign_keys=OFF;`); // recreate table statementsToExecute.push( ..._moveDataStatements(tableName, json2, dataLoss), ); - if (pragmaState) statementsToExecute.push(`PRAGMA foreign_keys=ON;`); } else if ( statement.type === 'alter_table_alter_column_set_generated' || statement.type === 'alter_table_alter_column_drop_generated' diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index 440d3d5bf..e2923f9df 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -645,6 +645,7 @@ export const connectToLibSQL = async (credentials: LibSQLCredentials): Promise< const client = createClient({ url: normaliseSQLiteUrl(credentials.url, 'libsql'), + authToken: credentials.authToken, }); const drzl = drizzle(client); const migrateFn = async (config: MigrationConfig) => { diff --git a/drizzle-kit/src/serializer/sqliteSerializer.ts b/drizzle-kit/src/serializer/sqliteSerializer.ts index ce544235b..81c633cb2 100644 --- a/drizzle-kit/src/serializer/sqliteSerializer.ts +++ b/drizzle-kit/src/serializer/sqliteSerializer.ts @@ -363,7 +363,6 @@ export const fromDatabase = async ( ) => void, ): Promise => { const result: Record = {}; - const columns = await db.query<{ tableName: string; columnName: string; @@ -389,6 +388,8 @@ export const fromDatabase = async ( `, ); + console.log('HERE'); + const tablesWithSeq: string[] = []; const seq = await db.query<{ diff --git a/drizzle-kit/tests/push/libsql.test.ts b/drizzle-kit/tests/push/libsql.test.ts index 5799b1fe3..89ec008ca 100644 --- a/drizzle-kit/tests/push/libsql.test.ts +++ b/drizzle-kit/tests/push/libsql.test.ts @@ -493,21 +493,19 @@ test('change autoincrement. table is part of foreign key', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements.length).toBe(6); - expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`); - expect(sqlStatements[1]).toBe( + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( `CREATE TABLE \`__new_companies\` ( \t\`id\` integer PRIMARY KEY NOT NULL );\n`, ); - expect(sqlStatements[2]).toBe( + expect(sqlStatements[1]).toBe( `INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`, ); - expect(sqlStatements[3]).toBe(`DROP TABLE \`companies\`;`); - expect(sqlStatements[4]).toBe( + expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`); + expect(sqlStatements[3]).toBe( `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`, ); - expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); @@ -756,21 +754,19 @@ test('recreate table with nested references', async (t) => { uniqueConstraints: [], }); - expect(sqlStatements!.length).toBe(6); - expect(sqlStatements[0]).toBe('PRAGMA foreign_keys=OFF;'); - expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` ( + expect(sqlStatements!.length).toBe(4); + expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` ( \t\`id\` integer PRIMARY KEY NOT NULL, \t\`name\` text, \t\`age\` integer );\n`); - expect(sqlStatements![2]).toBe( + expect(sqlStatements![1]).toBe( `INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`, ); - expect(sqlStatements![3]).toBe(`DROP TABLE \`users\`;`); - expect(sqlStatements![4]).toBe( + expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![3]).toBe( `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`, ); - expect(sqlStatements[5]).toBe('PRAGMA foreign_keys=ON;'); expect(columnsToRemove!.length).toBe(0); expect(infoToPrint!.length).toBe(0); From 380a4ce69687e1a829053eca1e21bd32ab88ea71 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Mon, 2 Sep 2024 17:55:02 +0300 Subject: [PATCH 25/56] removed console.log --- drizzle-kit/src/serializer/sqliteSerializer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/drizzle-kit/src/serializer/sqliteSerializer.ts b/drizzle-kit/src/serializer/sqliteSerializer.ts index 81c633cb2..41edd78a9 100644 --- a/drizzle-kit/src/serializer/sqliteSerializer.ts +++ b/drizzle-kit/src/serializer/sqliteSerializer.ts @@ -388,8 +388,6 @@ export const fromDatabase = async ( `, ); - console.log('HERE'); - const tablesWithSeq: string[] = []; const seq = await db.query<{ From 9b68f3d94afa54413d7b8fc4e2c557073c264873 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Tue, 3 Sep 2024 13:23:48 +0300 Subject: [PATCH 26/56] updated pull config update migrate function in drizzle-orm --- drizzle-kit/src/cli/commands/utils.ts | 2 +- drizzle-kit/src/cli/connections.ts | 46 +++++++++++++++ drizzle-kit/src/cli/utils.ts | 2 +- drizzle-kit/tests/libsql-statements.test.ts | 1 - drizzle-kit/tests/migrate/libsq-schema.ts | 6 ++ .../tests/migrate/libsql-migrate.test.ts | 58 +++++++++++++++++++ .../migrations/0000_little_blizzard.sql | 4 ++ .../migrations/0001_nebulous_storm.sql | 10 ++++ .../migrations/meta/0000_snapshot.json | 40 +++++++++++++ .../migrations/meta/0001_snapshot.json | 40 +++++++++++++ .../migrate/migrations/meta/_journal.json | 20 +++++++ drizzle-orm/src/libsql/migrator.ts | 2 +- drizzle-orm/src/libsql/session.ts | 15 +++++ drizzle-orm/src/version.ts | 2 +- 14 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 drizzle-kit/tests/migrate/libsq-schema.ts create mode 100644 drizzle-kit/tests/migrate/libsql-migrate.test.ts create mode 100644 drizzle-kit/tests/migrate/migrations/0000_little_blizzard.sql create mode 100644 drizzle-kit/tests/migrate/migrations/0001_nebulous_storm.sql create mode 100644 drizzle-kit/tests/migrate/migrations/meta/0000_snapshot.json create mode 100644 drizzle-kit/tests/migrate/migrations/meta/0001_snapshot.json create mode 100644 drizzle-kit/tests/migrate/migrations/meta/_journal.json diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index 2cfc7273b..bbad4bdb1 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -539,7 +539,7 @@ export const preparePullConfig = async ( process.exit(1); } return { - dialect: 'sqlite', + dialect, out: config.out, breakpoints: config.breakpoints, casing: config.casing, diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index e2923f9df..26311dbd6 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -586,6 +586,51 @@ export const connectToSQLite = async ( } } + if (await checkPackage('@libsql/client')) { + const { createClient } = await import('@libsql/client'); + const { drizzle } = await import('drizzle-orm/libsql'); + const { migrate } = await import('drizzle-orm/libsql/migrator'); + + const client = createClient({ + url: credentials.url, + }); + const drzl = drizzle(client); + const migrateFn = async (config: MigrationConfig) => { + return migrate(drzl, config); + }; + + const db: LibSQLDB = { + query: async (sql: string, params?: any[]) => { + const res = await client.execute({ sql, args: params || [] }); + return res.rows as T[]; + }, + run: async (query: string) => { + await client.execute(query); + }, + batchWithPragma: async (queries: string[]) => { + await client.migrate(queries); + }, + }; + + const proxy: SqliteProxy = { + proxy: async (params: ProxyParams) => { + const preparedParams = prepareSqliteParams(params.params); + const result = await client.execute({ + sql: params.sql, + args: preparedParams, + }); + + if (params.mode === 'array') { + return result.rows.map((row) => Object.values(row)); + } else { + return result.rows; + } + }, + }; + + return { ...db, ...proxy, migrate: migrateFn }; + } + if (await checkPackage('better-sqlite3')) { const { default: Database } = await import('better-sqlite3'); const { drizzle } = await import('drizzle-orm/better-sqlite3'); @@ -627,6 +672,7 @@ export const connectToSQLite = async ( }; return { ...db, ...proxy, migrate: migrateFn }; } + console.log( "Please install 'better-sqlite3' for Drizzle Kit to connect to SQLite databases", ); diff --git a/drizzle-kit/src/cli/utils.ts b/drizzle-kit/src/cli/utils.ts index f7e7a2ae9..0a5d7862e 100644 --- a/drizzle-kit/src/cli/utils.ts +++ b/drizzle-kit/src/cli/utils.ts @@ -74,7 +74,7 @@ export const assertEitherPackage = async ( process.exit(1); }; -const requiredApiVersion = 7; +const requiredApiVersion = 8; export const assertOrmCoreVersion = async () => { try { const { compatibilityVersion } = await import('drizzle-orm/version'); diff --git a/drizzle-kit/tests/libsql-statements.test.ts b/drizzle-kit/tests/libsql-statements.test.ts index e18e41950..8221e52e0 100644 --- a/drizzle-kit/tests/libsql-statements.test.ts +++ b/drizzle-kit/tests/libsql-statements.test.ts @@ -1,4 +1,3 @@ -import { sql } from 'drizzle-orm'; import { foreignKey, index, int, integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'; import { JsonRecreateTableStatement } from 'src/jsonStatements'; import { expect, test } from 'vitest'; diff --git a/drizzle-kit/tests/migrate/libsq-schema.ts b/drizzle-kit/tests/migrate/libsq-schema.ts new file mode 100644 index 000000000..5cb344d51 --- /dev/null +++ b/drizzle-kit/tests/migrate/libsq-schema.ts @@ -0,0 +1,6 @@ +import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; + +export const users = sqliteTable('users', { + id: integer('id').primaryKey().notNull(), + name: text('name').notNull(), +}); diff --git a/drizzle-kit/tests/migrate/libsql-migrate.test.ts b/drizzle-kit/tests/migrate/libsql-migrate.test.ts new file mode 100644 index 000000000..b937b644f --- /dev/null +++ b/drizzle-kit/tests/migrate/libsql-migrate.test.ts @@ -0,0 +1,58 @@ +import { createClient } from '@libsql/client'; +import { connectToLibSQL } from 'src/cli/connections'; +import { expect, test } from 'vitest'; + +test('validate migrate function', async () => { + const credentials = { + url: ':memory:', + }; + const { migrate, query } = await connectToLibSQL(credentials); + + await migrate({ migrationsFolder: 'tests/migrate/migrations' }); + + const res = await query(`PRAGMA table_info("users");`); + + expect(res).toStrictEqual([{ + cid: 0, + name: 'id', + type: 'INTEGER', + notnull: 0, + dflt_value: null, + pk: 0, + }, { + cid: 1, + name: 'name', + type: 'INTEGER', + notnull: 1, + dflt_value: null, + pk: 0, + }]); +}); + +// test('validate migrate function', async () => { +// const credentials = { +// url: '', +// authToken: '', +// }; +// const { migrate, query } = await connectToLibSQL(credentials); + +// await migrate({ migrationsFolder: 'tests/migrate/migrations' }); + +// const res = await query(`PRAGMA table_info("users");`); + +// expect(res).toStrictEqual([{ +// cid: 0, +// name: 'id', +// type: 'INTEGER', +// notnull: 0, +// dflt_value: null, +// pk: 0, +// }, { +// cid: 1, +// name: 'name', +// type: 'INTEGER', +// notnull: 1, +// dflt_value: null, +// pk: 0, +// }]); +// }); diff --git a/drizzle-kit/tests/migrate/migrations/0000_little_blizzard.sql b/drizzle-kit/tests/migrate/migrations/0000_little_blizzard.sql new file mode 100644 index 000000000..9de0a139d --- /dev/null +++ b/drizzle-kit/tests/migrate/migrations/0000_little_blizzard.sql @@ -0,0 +1,4 @@ +CREATE TABLE `users` ( + `id` integer PRIMARY KEY NOT NULL, + `name` text NOT NULL +); diff --git a/drizzle-kit/tests/migrate/migrations/0001_nebulous_storm.sql b/drizzle-kit/tests/migrate/migrations/0001_nebulous_storm.sql new file mode 100644 index 000000000..4309a05c2 --- /dev/null +++ b/drizzle-kit/tests/migrate/migrations/0001_nebulous_storm.sql @@ -0,0 +1,10 @@ +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE `__new_users` ( + `id` integer, + `name` integer NOT NULL +); +--> statement-breakpoint +INSERT INTO `__new_users`("id", "name") SELECT "id", "name" FROM `users`;--> statement-breakpoint +DROP TABLE `users`;--> statement-breakpoint +ALTER TABLE `__new_users` RENAME TO `users`;--> statement-breakpoint +PRAGMA foreign_keys=ON; \ No newline at end of file diff --git a/drizzle-kit/tests/migrate/migrations/meta/0000_snapshot.json b/drizzle-kit/tests/migrate/migrations/meta/0000_snapshot.json new file mode 100644 index 000000000..599d02b91 --- /dev/null +++ b/drizzle-kit/tests/migrate/migrations/meta/0000_snapshot.json @@ -0,0 +1,40 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "2bd46776-9e41-4a6c-b617-5c600bb176f2", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle-kit/tests/migrate/migrations/meta/0001_snapshot.json b/drizzle-kit/tests/migrate/migrations/meta/0001_snapshot.json new file mode 100644 index 000000000..e3b26ba14 --- /dev/null +++ b/drizzle-kit/tests/migrate/migrations/meta/0001_snapshot.json @@ -0,0 +1,40 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "6c0ec455-42fd-47fd-a22c-4bb4551e1358", + "prevId": "2bd46776-9e41-4a6c-b617-5c600bb176f2", + "tables": { + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle-kit/tests/migrate/migrations/meta/_journal.json b/drizzle-kit/tests/migrate/migrations/meta/_journal.json new file mode 100644 index 000000000..c836eb194 --- /dev/null +++ b/drizzle-kit/tests/migrate/migrations/meta/_journal.json @@ -0,0 +1,20 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1725358702427, + "tag": "0000_little_blizzard", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1725358713033, + "tag": "0001_nebulous_storm", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/drizzle-orm/src/libsql/migrator.ts b/drizzle-orm/src/libsql/migrator.ts index 58bcc9e05..aa262bc2d 100644 --- a/drizzle-orm/src/libsql/migrator.ts +++ b/drizzle-orm/src/libsql/migrator.ts @@ -47,5 +47,5 @@ export async function migrate>( } } - await db.session.batch(statementToBatch); + await db.session.migrate(statementToBatch); } diff --git a/drizzle-orm/src/libsql/session.ts b/drizzle-orm/src/libsql/session.ts index 29e4e268f..640977734 100644 --- a/drizzle-orm/src/libsql/session.ts +++ b/drizzle-orm/src/libsql/session.ts @@ -76,6 +76,21 @@ export class LibSQLSession< return batchResults.map((result, i) => preparedQueries[i]!.mapResult(result, true)); } + async migrate[] | readonly BatchItem<'sqlite'>[]>(queries: T) { + const preparedQueries: PreparedQuery[] = []; + const builtQueries: InStatement[] = []; + + for (const query of queries) { + const preparedQuery = query._prepare(); + const builtQuery = preparedQuery.getQuery(); + preparedQueries.push(preparedQuery); + builtQueries.push({ sql: builtQuery.sql, args: builtQuery.params as InArgs }); + } + + const batchResults = await this.client.migrate(builtQueries); + return batchResults.map((result, i) => preparedQueries[i]!.mapResult(result, true)); + } + override async transaction( transaction: (db: LibSQLTransaction) => T | Promise, _config?: SQLiteTransactionConfig, 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 779098355dccc1d82a0cca1e454269899e4b942c Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Tue, 3 Sep 2024 14:58:14 +0300 Subject: [PATCH 27/56] removed driver --- drizzle-kit/src/cli/commands/migrate.ts | 3 +-- drizzle-kit/src/cli/commands/utils.ts | 1 - drizzle-kit/src/cli/validations/cli.ts | 3 +-- drizzle-kit/src/sqlgenerator.ts | 2 -- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drizzle-kit/src/cli/commands/migrate.ts b/drizzle-kit/src/cli/commands/migrate.ts index 83f5b2dc8..de1d8bc45 100644 --- a/drizzle-kit/src/cli/commands/migrate.ts +++ b/drizzle-kit/src/cli/commands/migrate.ts @@ -13,7 +13,6 @@ import { import chalk from 'chalk'; import { render } from 'hanji'; import path, { join } from 'path'; -import { JsonStatement } from 'src/jsonStatements'; import { SingleStoreSchema, singlestoreSchema, squashSingleStoreScheme } from 'src/serializer/singlestoreSchema'; import { TypeOf } from 'zod'; import type { CommonSchema } from '../../schemaValidator'; @@ -38,7 +37,7 @@ import { } from '../../snapshotsDiffer'; import { assertV1OutFolder, Journal, prepareMigrationFolder } from '../../utils'; import { prepareMigrationMetadata } from '../../utils/words'; -import { Driver, Prefix } from '../validations/common'; +import { Prefix } from '../validations/common'; import { withStyle } from '../validations/outputs'; import { isRenamePromptItem, diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index bbad4bdb1..5e5681b2c 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -241,7 +241,6 @@ export const preparePushConfig = async ( : options, ); - raw.driver ||= options.driver; raw.verbose ||= options.verbose; // if provided in cli to debug raw.strict ||= options.strict; // if provided in cli only diff --git a/drizzle-kit/src/cli/validations/cli.ts b/drizzle-kit/src/cli/validations/cli.ts index 9d580fbe4..c4bbbe530 100644 --- a/drizzle-kit/src/cli/validations/cli.ts +++ b/drizzle-kit/src/cli/validations/cli.ts @@ -1,6 +1,6 @@ import { boolean, intersection, literal, object, string, TypeOf, union } from 'zod'; import { dialect } from '../../schemaValidator'; -import { casing, driver, prefix } from './common'; +import { casing, prefix } from './common'; export const cliConfigGenerate = object({ dialect: dialect.optional(), @@ -25,7 +25,6 @@ export const pushParams = object({ extensionsFilters: literal('postgis').array().optional(), verbose: boolean().optional(), strict: boolean().optional(), - driver: driver.optional(), }).passthrough(); export type PushParams = TypeOf; diff --git a/drizzle-kit/src/sqlgenerator.ts b/drizzle-kit/src/sqlgenerator.ts index 227cdf032..f1e783a50 100644 --- a/drizzle-kit/src/sqlgenerator.ts +++ b/drizzle-kit/src/sqlgenerator.ts @@ -1,5 +1,4 @@ import { BREAKPOINT } from './cli/commands/migrate'; -import { Driver } from './cli/validations/common'; import { JsonAddColumnStatement, JsonAddValueToEnumStatement, @@ -132,7 +131,6 @@ abstract class Convertor { abstract can( statement: JsonStatement, dialect: Dialect, - driver?: Driver, ): boolean; abstract convert( statement: JsonStatement, From d954a4bd3cb3e84538f5dca7247658e21fe07fdd Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Wed, 4 Sep 2024 11:48:57 +0300 Subject: [PATCH 28/56] updated sqlite connection to libsql --- drizzle-kit/src/cli/connections.ts | 7 ++----- drizzle-kit/src/utils.ts | 3 --- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index 26311dbd6..54945386e 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -592,14 +592,14 @@ export const connectToSQLite = async ( const { migrate } = await import('drizzle-orm/libsql/migrator'); const client = createClient({ - url: credentials.url, + url: normaliseSQLiteUrl(credentials.url, 'libsql'), }); const drzl = drizzle(client); const migrateFn = async (config: MigrationConfig) => { return migrate(drzl, config); }; - const db: LibSQLDB = { + const db: SQLiteDB = { query: async (sql: string, params?: any[]) => { const res = await client.execute({ sql, args: params || [] }); return res.rows as T[]; @@ -607,9 +607,6 @@ export const connectToSQLite = async ( run: async (query: string) => { await client.execute(query); }, - batchWithPragma: async (queries: string[]) => { - await client.migrate(queries); - }, }; const proxy: SqliteProxy = { diff --git a/drizzle-kit/src/utils.ts b/drizzle-kit/src/utils.ts index f5ff34c6f..71454550e 100644 --- a/drizzle-kit/src/utils.ts +++ b/drizzle-kit/src/utils.ts @@ -26,9 +26,6 @@ export type DB = { export type SQLiteDB = { query: (sql: string, params?: any[]) => Promise; run(query: string): Promise; - batch?( - queries: { query: string; values?: any[] | undefined }[], - ): Promise; }; export type LibSQLDB = { From 482cdce9de02cf8a63f7ccec29fbcecece7f5f1c Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 2 Sep 2024 15:51:05 +0300 Subject: [PATCH 29/56] Added alternate config for better-sqlite3, bun:sqlite; Renamed bun-sqlite to bun:sqlite; Fixed schema inferrence issue (no schema would resrresult in `unknown`, expected: `Record`) --- drizzle-orm/src/monodriver.ts | 56 +++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index b2b82af3f..db7ac1ddd 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -50,15 +50,21 @@ type BunSqliteDatabaseOptions = readwrite?: boolean; }; -type BunSqliteDatabaseConfig = { - filename?: string; - options?: BunSqliteDatabaseOptions; -}; +type BunSqliteDatabaseConfig = + | { + filename?: string; + options?: BunSqliteDatabaseOptions; + } + | string + | undefined; -type BetterSQLite3DatabaseConfig = { - filename?: string | Buffer; - options?: BetterSQLite3Options; -}; +type BetterSQLite3DatabaseConfig = + | { + filename?: string | Buffer; + options?: BetterSQLite3Options; + } + | string + | undefined; type MonodriverNeonHttpConfig = { connectionString: string; @@ -77,7 +83,7 @@ type ClientDrizzleInstanceMap> = { 'tidb-serverless': TiDBServerlessDatabase; libsql: LibSQLDatabase; d1: DrizzleD1Database; - 'bun-sqlite': BunSQLiteDatabase; + 'bun:sqlite': BunSQLiteDatabase; 'better-sqlite3': BetterSQLite3Database; }; @@ -129,7 +135,7 @@ type InitializerParams< connection: D1Database; } & DrizzleConfig) | ({ - client: 'bun-sqlite'; + client: 'bun:sqlite'; connection: BunSqliteDatabaseConfig; } & DrizzleConfig) | ({ @@ -139,7 +145,9 @@ type InitializerParams< type DetermineClient< TParams extends InitializerParams, -> = ClientDrizzleInstanceMap[TParams['client']]; +> = ClientDrizzleInstanceMap< + TParams['schema'] extends Record ? TParams['schema'] : Record +>[TParams['client']]; const importError = (libName: string) => { throw new Error( @@ -172,17 +180,33 @@ export const drizzle = async < } case 'better-sqlite3': { const { default: Client } = await import('better-sqlite3').catch(() => importError('better-sqlite3')); - const { filename, options } = connection as BetterSQLite3DatabaseConfig; const { drizzle } = await import('./better-sqlite3'); - const instance = new Client(filename, options); + + if (typeof connection === 'object') { + const { filename, options } = connection as Exclude; + + const instance = new Client(filename, options); + + return drizzle(instance, drizzleConfig) as any; + } + + const instance = new Client(connection); return drizzle(instance, drizzleConfig) as any; } - case 'bun-sqlite': { + case 'bun:sqlite': { const { Database: Client } = await import('bun:sqlite').catch(() => importError('bun:sqlite')); - const { filename, options } = connection as BunSqliteDatabaseConfig; const { drizzle } = await import('./bun-sqlite'); - const instance = new Client(filename, options); + + if (typeof connection === 'object') { + const { filename, options } = connection as Exclude; + + const instance = new Client(filename, options); + + return drizzle(instance, drizzleConfig) as any; + } + + const instance = new Client(connection); return drizzle(instance, drizzleConfig) as any; } From b40b752fae093f06b2c37e527237e5e3593c109f Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Mon, 2 Sep 2024 18:33:42 +0300 Subject: [PATCH 30/56] Added monomigrator --- drizzle-orm/src/index.ts | 1 + drizzle-orm/src/monomigrator.ts | 266 ++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 drizzle-orm/src/monomigrator.ts diff --git a/drizzle-orm/src/index.ts b/drizzle-orm/src/index.ts index 5cabdb0d8..469f5713e 100644 --- a/drizzle-orm/src/index.ts +++ b/drizzle-orm/src/index.ts @@ -6,6 +6,7 @@ export * from './errors.ts'; export * from './expressions.ts'; export * from './logger.ts'; export * from './monodriver.ts'; +export * from './monomigrator.ts'; export * from './operations.ts'; export * from './query-promise.ts'; export * from './relations.ts'; diff --git a/drizzle-orm/src/monomigrator.ts b/drizzle-orm/src/monomigrator.ts new file mode 100644 index 000000000..b82a1ea0b --- /dev/null +++ b/drizzle-orm/src/monomigrator.ts @@ -0,0 +1,266 @@ +/* eslint-disable import/extensions */ +import type { AwsDataApiPgDatabase } from './aws-data-api/pg/index.ts'; +import type { BetterSQLite3Database } from './better-sqlite3/index.ts'; +import type { BunSQLiteDatabase } from './bun-sqlite/index.ts'; +import type { DrizzleD1Database } from './d1/index.ts'; +import type { ExpoSQLiteDatabase } from './expo-sqlite/index.ts'; +import type { LibSQLDatabase } from './libsql/index.ts'; +import type { MigrationConfig } from './migrator.ts'; +import type { MySqlRemoteDatabase } from './mysql-proxy/index.ts'; +import type { ProxyMigrator as MySqlProxyMigrator } from './mysql-proxy/migrator.ts'; +import type { MySql2Database } from './mysql2/index.ts'; +import type { NeonHttpDatabase } from './neon-http/index.ts'; +import type { NeonDatabase } from './neon-serverless/index.ts'; +import type { NodePgDatabase } from './node-postgres/index.ts'; +import type { OPSQLiteDatabase } from './op-sqlite/index.ts'; +import type { PgRemoteDatabase } from './pg-proxy/index.ts'; +import type { ProxyMigrator as PgProxyMigrator } from './pg-proxy/migrator.ts'; +import type { PgliteDatabase } from './pglite/index.ts'; +import type { PlanetScaleDatabase } from './planetscale-serverless/index.ts'; +import type { ProxyMigrator as SQLiteProxyMigrator } from './sqlite-proxy/migrator.ts'; +import type { VercelPgDatabase } from './vercel-postgres/index.ts'; +import type { XataHttpDatabase } from './xata-http/index.ts'; +import type { MigrationConfig as XataHttpMigrationConfig } from './xata-http/migrator.ts'; + +type OPSQLiteMigrationConfig = { + journal: { + entries: { idx: number; when: number; tag: string; breakpoints: boolean }[]; + }; + migrations: Record; +}; + +type ExpoSQLiteMigrationConfig = { + journal: { + entries: { idx: number; when: number; tag: string; breakpoints: boolean }[]; + }; + migrations: Record; +}; + +type DatabaseType = + | 'aws-data-api-pg' + | 'better-sqlite3' + | 'bun:sqlite' + | 'd1' + | 'expo-sqlite' + | 'libsql' + | 'mysql-proxy' + | 'mysql2' + | 'neon-http' + | 'neon-serverless' + | 'node-postgres' + | 'op-sqlite' + | 'pg-proxy' + | 'pglite' + | 'planetscale' + | 'postgres-js' + | 'sqlite-proxy' + | 'tidb-serverless' + | 'vercel-postgres' + | 'xata-http'; + +export async function migrate( + type: 'aws-data-api-pg', + db: AwsDataApiPgDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'better-sqlite3', + db: BetterSQLite3Database, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'bun:sqlite', + db: BunSQLiteDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate(type: 'd1', db: DrizzleD1Database, config: string | MigrationConfig): Promise; +export async function migrate( + type: 'expo-sqlite', + db: ExpoSQLiteDatabase, + config: ExpoSQLiteMigrationConfig, +): Promise; +export async function migrate(type: 'libsql', db: LibSQLDatabase, config: MigrationConfig): Promise; +export async function migrate( + type: 'mysql-proxy', + db: MySqlRemoteDatabase, + callback: MySqlProxyMigrator, + config: MigrationConfig, +): Promise; +export async function migrate(type: 'mysql2', db: MySql2Database, config: MigrationConfig): Promise; +export async function migrate( + type: 'neon-http', + db: NeonHttpDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'neon-serverless', + db: NeonDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'node-postgres', + db: NodePgDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'op-sqlite', + db: OPSQLiteDatabase, + config: OPSQLiteMigrationConfig, +): Promise; +export async function migrate( + type: 'pg-proxy', + db: PgRemoteDatabase, + callback: PgProxyMigrator, + config: string | MigrationConfig, +): Promise; +export async function migrate(type: 'pglite', db: PgliteDatabase, config: string | MigrationConfig): Promise; +export async function migrate( + type: 'planetscale', + db: PlanetScaleDatabase, + config: MigrationConfig, +): Promise; +export async function migrate( + type: 'postgres-js', + db: PlanetScaleDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'sqlite-proxy', + db: PgRemoteDatabase, + callback: SQLiteProxyMigrator, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'tidb-serverless', + db: PlanetScaleDatabase, + config: MigrationConfig, +): Promise; +export async function migrate( + type: 'vercel-postgres', + db: VercelPgDatabase, + config: string | MigrationConfig, +): Promise; +export async function migrate( + type: 'xata-http', + db: XataHttpDatabase, + config: string | XataHttpMigrationConfig, +): Promise; +export async function migrate( + type: DatabaseType, + db: any, + config: + | string + | MigrationConfig + | ExpoSQLiteMigrationConfig + | OPSQLiteMigrationConfig + | XataHttpMigrationConfig + | PgProxyMigrator + | MySqlProxyMigrator + | SQLiteProxyMigrator, + extraConfig?: string | MigrationConfig | undefined, +) { + const rest = [db, config, extraConfig]; + + switch (type) { + case 'aws-data-api-pg': { + const { migrate } = await import('./aws-data-api/pg/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'better-sqlite3': { + const { migrate } = await import('./better-sqlite3/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'bun:sqlite': { + const { migrate } = await import('./bun-sqlite/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'd1': { + const { migrate } = await import('./d1/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'expo-sqlite': { + const { migrate } = await import('./expo-sqlite/migrator'); + + return migrate(rest[0], rest[1] as ExpoSQLiteMigrationConfig); + } + case 'libsql': { + const { migrate } = await import('./libsql/migrator'); + + return migrate(rest[0], rest[1] as MigrationConfig); + } + case 'mysql-proxy': { + const { migrate } = await import('./mysql-proxy/migrator'); + + return migrate(rest[0], rest[1] as MySqlProxyMigrator, rest[2] as MigrationConfig); + } + case 'mysql2': { + const { migrate } = await import('./mysql2/migrator'); + + return migrate(rest[0], rest[1] as MigrationConfig); + } + case 'neon-http': { + const { migrate } = await import('./neon-http/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'neon-serverless': { + const { migrate } = await import('./neon-serverless/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'node-postgres': { + const { migrate } = await import('./node-postgres/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'op-sqlite': { + const { migrate } = await import('./op-sqlite/migrator'); + + return migrate(rest[0], rest[1] as OPSQLiteMigrationConfig); + } + case 'pg-proxy': { + const { migrate } = await import('./pg-proxy/migrator'); + + return migrate(rest[0], rest[1] as PgProxyMigrator, rest[2] as string | MigrationConfig); + } + case 'pglite': { + const { migrate } = await import('./pglite/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'planetscale': { + const { migrate } = await import('./planetscale-serverless/migrator'); + + return migrate(rest[0], rest[1] as MigrationConfig); + } + case 'postgres-js': { + const { migrate } = await import('./postgres-js/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'sqlite-proxy': { + const { migrate } = await import('./sqlite-proxy/migrator'); + + return migrate(rest[0], rest[1] as SQLiteProxyMigrator, rest[2] as string | MigrationConfig); + } + case 'tidb-serverless': { + const { migrate } = await import('./tidb-serverless/migrator'); + + return migrate(rest[0], rest[1] as MigrationConfig); + } + case 'vercel-postgres': { + const { migrate } = await import('./vercel-postgres/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + case 'xata-http': { + const { migrate } = await import('./xata-http/migrator'); + + return migrate(rest[0], rest[1] as string | MigrationConfig); + } + } +} From ccac44066fe33bdd49670df20c5cc84cac210a7b Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Tue, 3 Sep 2024 12:47:31 +0300 Subject: [PATCH 31/56] Changed style of monodriver, monomigrator function args --- drizzle-orm/src/better-sqlite3/driver.ts | 11 +- drizzle-orm/src/bun-sqlite/driver.ts | 9 +- drizzle-orm/src/expo-sqlite/driver.ts | 11 +- drizzle-orm/src/monodriver.ts | 120 ++++++++++++---- drizzle-orm/src/monomigrator.ts | 132 ++++++------------ drizzle-orm/src/mysql-proxy/driver.ts | 9 +- drizzle-orm/src/neon-serverless/driver.ts | 8 +- drizzle-orm/src/op-sqlite/driver.ts | 9 +- drizzle-orm/src/pg-proxy/driver.ts | 9 +- drizzle-orm/src/pglite/driver.ts | 8 +- .../src/planetscale-serverless/driver.ts | 9 +- drizzle-orm/src/postgres-js/driver.ts | 9 +- drizzle-orm/src/tidb-serverless/driver.ts | 9 +- drizzle-orm/src/vercel-postgres/driver.ts | 8 +- 14 files changed, 213 insertions(+), 148 deletions(-) diff --git a/drizzle-orm/src/better-sqlite3/driver.ts b/drizzle-orm/src/better-sqlite3/driver.ts index 728586e57..8d19cd8ab 100644 --- a/drizzle-orm/src/better-sqlite3/driver.ts +++ b/drizzle-orm/src/better-sqlite3/driver.ts @@ -1,4 +1,5 @@ import type { Database, RunResult } from 'better-sqlite3'; +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { createTableRelationsHelpers, @@ -11,9 +12,11 @@ import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; import type { DrizzleConfig } from '~/utils.ts'; import { BetterSQLiteSession } from './session.ts'; -export type BetterSQLite3Database< - TSchema extends Record = Record, -> = BaseSQLiteDatabase<'sync', RunResult, TSchema>; +export class BetterSQLite3Database> + extends BaseSQLiteDatabase<'sync', RunResult, TSchema> +{ + static readonly [entityKind]: string = 'BetterSQLite3Database'; +} export function drizzle = Record>( client: Database, @@ -41,5 +44,5 @@ export function drizzle = Record; + return new BetterSQLite3Database('sync', dialect, session, schema) as BetterSQLite3Database; } diff --git a/drizzle-orm/src/bun-sqlite/driver.ts b/drizzle-orm/src/bun-sqlite/driver.ts index 0d196ff03..5771bd371 100644 --- a/drizzle-orm/src/bun-sqlite/driver.ts +++ b/drizzle-orm/src/bun-sqlite/driver.ts @@ -1,6 +1,7 @@ /// import type { Database } from 'bun:sqlite'; +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { createTableRelationsHelpers, @@ -13,9 +14,11 @@ import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; import type { DrizzleConfig } from '~/utils.ts'; import { SQLiteBunSession } from './session.ts'; -export type BunSQLiteDatabase< +export class BunSQLiteDatabase< TSchema extends Record = Record, -> = BaseSQLiteDatabase<'sync', void, TSchema>; +> extends BaseSQLiteDatabase<'sync', void, TSchema> { + static readonly [entityKind]: string = 'BunSQLiteDatabase'; +} export function drizzle = Record>( client: Database, @@ -43,5 +46,5 @@ export function drizzle = Record; + return new BunSQLiteDatabase('sync', dialect, session, schema) as BunSQLiteDatabase; } diff --git a/drizzle-orm/src/expo-sqlite/driver.ts b/drizzle-orm/src/expo-sqlite/driver.ts index ae8ce6577..51cb1a204 100644 --- a/drizzle-orm/src/expo-sqlite/driver.ts +++ b/drizzle-orm/src/expo-sqlite/driver.ts @@ -1,4 +1,5 @@ import type { SQLiteDatabase, SQLiteRunResult } from 'expo-sqlite/next'; +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { createTableRelationsHelpers, @@ -11,9 +12,11 @@ import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; import type { DrizzleConfig } from '~/utils.ts'; import { ExpoSQLiteSession } from './session.ts'; -export type ExpoSQLiteDatabase< - TSchema extends Record = Record, -> = BaseSQLiteDatabase<'sync', SQLiteRunResult, TSchema>; +export class ExpoSQLiteDatabase> + extends BaseSQLiteDatabase<'sync', SQLiteRunResult, TSchema> +{ + static readonly [entityKind]: string = 'ExpoSQLiteDatabase'; +} export function drizzle = Record>( client: SQLiteDatabase, @@ -41,5 +44,5 @@ export function drizzle = Record; + return new ExpoSQLiteDatabase('sync', dialect, session, schema) as ExpoSQLiteDatabase; } diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index db7ac1ddd..41bb202e8 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -1,5 +1,5 @@ /* eslint-disable import/extensions */ -import type { RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; +import type { RDSDataClientConfig, RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; import type { Config as LibsqlConfig } from '@libsql/client'; import type { HTTPTransactionOptions as NeonHttpConfig, @@ -71,6 +71,21 @@ type MonodriverNeonHttpConfig = { options?: NeonHttpConfig; }; +type DatabaseVendor = + | 'node-postgres' + | 'postgres-js' + | 'neon-serverless' + | 'neon-http' + | 'vercel-postgres' + | 'aws-data-api-pg' + | 'planetscale' + | 'mysql2' + | 'tidb-serverless' + | 'libsql' + | 'd1' + | 'bun:sqlite' + | 'better-sqlite3'; + type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; 'postgres-js': PostgresJsDatabase; @@ -91,63 +106,51 @@ type InitializerParams< TSchema extends Record = Record, > = | ({ - client: 'node-postgres'; connection: NodePGPoolConfig; } & DrizzleConfig) | ({ - client: 'postgres-js'; connection: PostgresJSOptions>; } & DrizzleConfig) | ({ - client: 'neon-serverless'; connection: NeonServerlessConfig; } & DrizzleConfig) | ({ - client: 'neon-http'; connection: MonodriverNeonHttpConfig; } & DrizzleConfig) | ({ - client: 'vercel-postgres'; connection: VercelPool; } & DrizzleConfig) | ({ - client: 'aws-data-api-pg'; connection: RDSConfig; } & DrizzleAwsDataApiPgConfig) | ({ - client: 'planetscale'; connection: PlanetscaleConfig; } & DrizzleConfig) | ({ - client: 'mysql2'; connection: Mysql2Config; } & MySql2DrizzleConfig) | ({ - client: 'tidb-serverless'; connection: TiDBServerlessConfig; } & DrizzleConfig) | ({ - client: 'libsql'; connection: LibsqlConfig; } & DrizzleConfig) | ({ - client: 'd1'; connection: D1Database; } & DrizzleConfig) | ({ - client: 'bun:sqlite'; - connection: BunSqliteDatabaseConfig; + connection?: BunSqliteDatabaseConfig; } & DrizzleConfig) | ({ - client: 'better-sqlite3'; - connection: BetterSQLite3DatabaseConfig; + connection?: BetterSQLite3DatabaseConfig; } & DrizzleConfig); type DetermineClient< - TParams extends InitializerParams, + TVendor extends DatabaseVendor, + TSchema extends Record, > = ClientDrizzleInstanceMap< - TParams['schema'] extends Record ? TParams['schema'] : Record ->[TParams['client']]; + TSchema +>[TVendor]; const importError = (libName: string) => { throw new Error( @@ -155,11 +158,75 @@ const importError = (libName: string) => { ); }; -export const drizzle = async < +const removeKey = , TKey extends keyof TRecord>( + obj: TRecord, + key: TKey, +): Omit => { + if (!(key in obj)) return obj; + + delete ( obj).key; + return obj; +}; + +export async function drizzle = Record>( + client: 'node-postgres', + params: { connection: NodePGPoolConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'postgres-js', + params: { connection: string | PostgresJSOptions> } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'neon-serverless', + params: { connection: NeonServerlessConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'neon-http', + params: { connection: MonodriverNeonHttpConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'vercel-postgres', + params: { connection: VercelPool } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'aws-data-api-pg', + params?: { connection?: RDSConfig } & DrizzleAwsDataApiPgConfig, +): Promise>; +export async function drizzle = Record>( + client: 'planetscale', + params: { connection: PlanetscaleConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'mysql2', + params: { connection: Mysql2Config | string } & MySql2DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'tidb-serverless', + params: { connection: TiDBServerlessConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'libsql', + params: { connection: LibsqlConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'd1', + params: { connection: D1Database } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'bun:sqlite', + params?: { connection?: BunSqliteDatabaseConfig } & DrizzleConfig, +): Promise>; +export async function drizzle = Record>( + client: 'better-sqlite3', + params?: { connection?: BetterSQLite3DatabaseConfig } & DrizzleConfig, +): Promise>; +export async function drizzle< + TVendor extends DatabaseVendor, TSchema extends Record, TParams extends InitializerParams, ->(params: TParams): Promise> => { - const { client, connection, ...drizzleConfig } = params; +>(client: TVendor, params?: TParams): Promise> { + const connection = params?.connection; + const drizzleConfig = params ? removeKey(params, 'connection') : undefined; switch (client) { case 'node-postgres': { @@ -174,7 +241,7 @@ export const drizzle = async < importError('@aws-sdk/client-rds-data') ); const { drizzle } = await import('./aws-data-api/pg'); - const instance = new RDSDataClient(connection); + const instance = new RDSDataClient(connection as RDSDataClientConfig); return drizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; } @@ -272,5 +339,10 @@ export const drizzle = async < return drizzle(sql, drizzleConfig) as any; } + default: { + throw new Error( + `Unsupported vendor for Drizzle ORM monodriver: '${client}'. Use dedicated drizzle initializer instead.`, + ); + } } -}; +} diff --git a/drizzle-orm/src/monomigrator.ts b/drizzle-orm/src/monomigrator.ts index b82a1ea0b..eec37bbdf 100644 --- a/drizzle-orm/src/monomigrator.ts +++ b/drizzle-orm/src/monomigrator.ts @@ -3,6 +3,7 @@ import type { AwsDataApiPgDatabase } from './aws-data-api/pg/index.ts'; import type { BetterSQLite3Database } from './better-sqlite3/index.ts'; import type { BunSQLiteDatabase } from './bun-sqlite/index.ts'; import type { DrizzleD1Database } from './d1/index.ts'; +import { entityKind } from './entity.ts'; import type { ExpoSQLiteDatabase } from './expo-sqlite/index.ts'; import type { LibSQLDatabase } from './libsql/index.ts'; import type { MigrationConfig } from './migrator.ts'; @@ -36,117 +37,78 @@ type ExpoSQLiteMigrationConfig = { migrations: Record; }; -type DatabaseType = - | 'aws-data-api-pg' - | 'better-sqlite3' - | 'bun:sqlite' - | 'd1' - | 'expo-sqlite' - | 'libsql' - | 'mysql-proxy' - | 'mysql2' - | 'neon-http' - | 'neon-serverless' - | 'node-postgres' - | 'op-sqlite' - | 'pg-proxy' - | 'pglite' - | 'planetscale' - | 'postgres-js' - | 'sqlite-proxy' - | 'tidb-serverless' - | 'vercel-postgres' - | 'xata-http'; - export async function migrate( - type: 'aws-data-api-pg', db: AwsDataApiPgDatabase, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'better-sqlite3', db: BetterSQLite3Database, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'bun:sqlite', db: BunSQLiteDatabase, config: string | MigrationConfig, ): Promise; -export async function migrate(type: 'd1', db: DrizzleD1Database, config: string | MigrationConfig): Promise; +export async function migrate(db: DrizzleD1Database, config: string | MigrationConfig): Promise; export async function migrate( - type: 'expo-sqlite', db: ExpoSQLiteDatabase, config: ExpoSQLiteMigrationConfig, ): Promise; -export async function migrate(type: 'libsql', db: LibSQLDatabase, config: MigrationConfig): Promise; +export async function migrate(db: LibSQLDatabase, config: MigrationConfig): Promise; export async function migrate( - type: 'mysql-proxy', db: MySqlRemoteDatabase, callback: MySqlProxyMigrator, config: MigrationConfig, ): Promise; -export async function migrate(type: 'mysql2', db: MySql2Database, config: MigrationConfig): Promise; +export async function migrate(db: MySql2Database, config: MigrationConfig): Promise; export async function migrate( - type: 'neon-http', db: NeonHttpDatabase, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'neon-serverless', db: NeonDatabase, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'node-postgres', db: NodePgDatabase, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'op-sqlite', db: OPSQLiteDatabase, config: OPSQLiteMigrationConfig, ): Promise; export async function migrate( - type: 'pg-proxy', db: PgRemoteDatabase, callback: PgProxyMigrator, config: string | MigrationConfig, ): Promise; -export async function migrate(type: 'pglite', db: PgliteDatabase, config: string | MigrationConfig): Promise; +export async function migrate(db: PgliteDatabase, config: string | MigrationConfig): Promise; export async function migrate( - type: 'planetscale', db: PlanetScaleDatabase, config: MigrationConfig, ): Promise; export async function migrate( - type: 'postgres-js', db: PlanetScaleDatabase, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'sqlite-proxy', db: PgRemoteDatabase, callback: SQLiteProxyMigrator, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'tidb-serverless', db: PlanetScaleDatabase, config: MigrationConfig, ): Promise; export async function migrate( - type: 'vercel-postgres', db: VercelPgDatabase, config: string | MigrationConfig, ): Promise; export async function migrate( - type: 'xata-http', db: XataHttpDatabase, config: string | XataHttpMigrationConfig, ): Promise; export async function migrate( - type: DatabaseType, db: any, config: | string @@ -159,108 +121,106 @@ export async function migrate( | SQLiteProxyMigrator, extraConfig?: string | MigrationConfig | undefined, ) { - const rest = [db, config, extraConfig]; - - switch (type) { - case 'aws-data-api-pg': { + switch (db[entityKind]) { + case 'AwsDataApiPgDatabase': { const { migrate } = await import('./aws-data-api/pg/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'better-sqlite3': { + case 'BetterSQLite3Database': { const { migrate } = await import('./better-sqlite3/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'bun:sqlite': { + case 'BunSQLiteDatabase': { const { migrate } = await import('./bun-sqlite/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'd1': { + case 'D1Database': { const { migrate } = await import('./d1/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'expo-sqlite': { + case 'ExpoSQLiteDatabase': { const { migrate } = await import('./expo-sqlite/migrator'); - return migrate(rest[0], rest[1] as ExpoSQLiteMigrationConfig); + return migrate(db, config as ExpoSQLiteMigrationConfig); } - case 'libsql': { + case 'LibSQLDatabase': { const { migrate } = await import('./libsql/migrator'); - return migrate(rest[0], rest[1] as MigrationConfig); + return migrate(db, config as MigrationConfig); } - case 'mysql-proxy': { + case 'MySqlRemoteDatabase': { const { migrate } = await import('./mysql-proxy/migrator'); - return migrate(rest[0], rest[1] as MySqlProxyMigrator, rest[2] as MigrationConfig); + return migrate(db, config as MySqlProxyMigrator, extraConfig as MigrationConfig); } - case 'mysql2': { + case 'MySql2Driver': { const { migrate } = await import('./mysql2/migrator'); - return migrate(rest[0], rest[1] as MigrationConfig); + return migrate(db, config as MigrationConfig); } - case 'neon-http': { + case 'NeonHttpDatabase': { const { migrate } = await import('./neon-http/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'neon-serverless': { + case 'NeonServerlessDatabase': { const { migrate } = await import('./neon-serverless/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'node-postgres': { + case 'NodePgDriver': { const { migrate } = await import('./node-postgres/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'op-sqlite': { + case 'OPSQLiteDatabase': { const { migrate } = await import('./op-sqlite/migrator'); - return migrate(rest[0], rest[1] as OPSQLiteMigrationConfig); + return migrate(db, config as OPSQLiteMigrationConfig); } - case 'pg-proxy': { + case 'PgRemoteDatabase': { const { migrate } = await import('./pg-proxy/migrator'); - return migrate(rest[0], rest[1] as PgProxyMigrator, rest[2] as string | MigrationConfig); + return migrate(db, config as PgProxyMigrator, extraConfig as string | MigrationConfig); } - case 'pglite': { + case 'PgliteDatabase': { const { migrate } = await import('./pglite/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'planetscale': { + case 'PlanetScaleDatabase': { const { migrate } = await import('./planetscale-serverless/migrator'); - return migrate(rest[0], rest[1] as MigrationConfig); + return migrate(db, config as MigrationConfig); } - case 'postgres-js': { + case 'PostgresJsDatabase': { const { migrate } = await import('./postgres-js/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'sqlite-proxy': { + case 'SqliteRemoteDatabase': { const { migrate } = await import('./sqlite-proxy/migrator'); - return migrate(rest[0], rest[1] as SQLiteProxyMigrator, rest[2] as string | MigrationConfig); + return migrate(db, config as SQLiteProxyMigrator, extraConfig as string | MigrationConfig); } - case 'tidb-serverless': { + case 'TiDBServerlessDatabase': { const { migrate } = await import('./tidb-serverless/migrator'); - return migrate(rest[0], rest[1] as MigrationConfig); + return migrate(db, config as MigrationConfig); } - case 'vercel-postgres': { + case 'VercelPgDatabase': { const { migrate } = await import('./vercel-postgres/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } - case 'xata-http': { + case 'XataHttpDatabase': { const { migrate } = await import('./xata-http/migrator'); - return migrate(rest[0], rest[1] as string | MigrationConfig); + return migrate(db, config as string | MigrationConfig); } } } diff --git a/drizzle-orm/src/mysql-proxy/driver.ts b/drizzle-orm/src/mysql-proxy/driver.ts index 574db42c1..dfbf69cc9 100644 --- a/drizzle-orm/src/mysql-proxy/driver.ts +++ b/drizzle-orm/src/mysql-proxy/driver.ts @@ -1,3 +1,4 @@ +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { MySqlDatabase } from '~/mysql-core/db.ts'; import { MySqlDialect } from '~/mysql-core/dialect.ts'; @@ -10,9 +11,11 @@ import { import type { DrizzleConfig } from '~/utils.ts'; import { type MySqlRemotePreparedQueryHKT, type MySqlRemoteQueryResultHKT, MySqlRemoteSession } from './session.ts'; -export type MySqlRemoteDatabase< +export class MySqlRemoteDatabase< TSchema extends Record = Record, -> = MySqlDatabase; +> extends MySqlDatabase { + static readonly [entityKind]: string = 'MySqlRemoteDatabase'; +} export type RemoteCallback = ( sql: string, @@ -46,5 +49,5 @@ export function drizzle = Record; + return new MySqlRemoteDatabase(dialect, session, schema as any, 'default') as MySqlRemoteDatabase; } diff --git a/drizzle-orm/src/neon-serverless/driver.ts b/drizzle-orm/src/neon-serverless/driver.ts index 8a15dd678..7f42cfeb3 100644 --- a/drizzle-orm/src/neon-serverless/driver.ts +++ b/drizzle-orm/src/neon-serverless/driver.ts @@ -43,9 +43,11 @@ export class NeonDriver { } } -export type NeonDatabase< +export class NeonDatabase< TSchema extends Record = Record, -> = PgDatabase; +> extends PgDatabase { + static readonly [entityKind]: string = 'NeonServerlessDatabase'; +} export function drizzle = Record>( client: NeonClient, @@ -74,5 +76,5 @@ export function drizzle = Record; + return new NeonDatabase(dialect, session, schema as any) as NeonDatabase; } diff --git a/drizzle-orm/src/op-sqlite/driver.ts b/drizzle-orm/src/op-sqlite/driver.ts index 24c663abf..94ee6e866 100644 --- a/drizzle-orm/src/op-sqlite/driver.ts +++ b/drizzle-orm/src/op-sqlite/driver.ts @@ -1,4 +1,5 @@ import type { OPSQLiteConnection, QueryResult } from '@op-engineering/op-sqlite'; +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { createTableRelationsHelpers, @@ -11,9 +12,11 @@ import { SQLiteAsyncDialect } from '~/sqlite-core/dialect.ts'; import type { DrizzleConfig } from '~/utils.ts'; import { OPSQLiteSession } from './session.ts'; -export type OPSQLiteDatabase< +export class OPSQLiteDatabase< TSchema extends Record = Record, -> = BaseSQLiteDatabase<'async', QueryResult, TSchema>; +> extends BaseSQLiteDatabase<'async', QueryResult, TSchema> { + static readonly [entityKind]: string = 'OPSQLiteDatabase'; +} export function drizzle = Record>( client: OPSQLiteConnection, @@ -41,5 +44,5 @@ export function drizzle = Record; + return new OPSQLiteDatabase('async', dialect, session, schema) as OPSQLiteDatabase; } diff --git a/drizzle-orm/src/pg-proxy/driver.ts b/drizzle-orm/src/pg-proxy/driver.ts index cdffa15c1..d82e86962 100644 --- a/drizzle-orm/src/pg-proxy/driver.ts +++ b/drizzle-orm/src/pg-proxy/driver.ts @@ -1,3 +1,4 @@ +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { PgDatabase } from '~/pg-core/db.ts'; import { PgDialect } from '~/pg-core/dialect.ts'; @@ -10,9 +11,11 @@ import { import type { DrizzleConfig } from '~/utils.ts'; import { type PgRemoteQueryResultHKT, PgRemoteSession } from './session.ts'; -export type PgRemoteDatabase< +export class PgRemoteDatabase< TSchema extends Record = Record, -> = PgDatabase; +> extends PgDatabase { + static readonly [entityKind]: string = 'PgRemoteDatabase'; +} export type RemoteCallback = ( sql: string, @@ -48,5 +51,5 @@ export function drizzle = Record; + return new PgRemoteDatabase(dialect, session, schema as any) as PgRemoteDatabase; } diff --git a/drizzle-orm/src/pglite/driver.ts b/drizzle-orm/src/pglite/driver.ts index 7de2ce110..a801005d8 100644 --- a/drizzle-orm/src/pglite/driver.ts +++ b/drizzle-orm/src/pglite/driver.ts @@ -34,9 +34,11 @@ export class PgliteDriver { } } -export type PgliteDatabase< +export class PgliteDatabase< TSchema extends Record = Record, -> = PgDatabase; +> extends PgDatabase { + static readonly [entityKind]: string = 'PgliteDatabase'; +} export function drizzle = Record>( client: PgliteClient, @@ -65,5 +67,5 @@ export function drizzle = Record; + return new PgliteDatabase(dialect, session, schema as any) as PgliteDatabase; } diff --git a/drizzle-orm/src/planetscale-serverless/driver.ts b/drizzle-orm/src/planetscale-serverless/driver.ts index b1d2d6e6f..fd1327bbc 100644 --- a/drizzle-orm/src/planetscale-serverless/driver.ts +++ b/drizzle-orm/src/planetscale-serverless/driver.ts @@ -1,5 +1,6 @@ import type { Connection } from '@planetscale/database'; import { Client } from '@planetscale/database'; +import { entityKind } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { DefaultLogger } from '~/logger.ts'; import { MySqlDatabase } from '~/mysql-core/db.ts'; @@ -18,9 +19,11 @@ export interface PlanetscaleSDriverOptions { logger?: Logger; } -export type PlanetScaleDatabase< +export class PlanetScaleDatabase< TSchema extends Record = Record, -> = MySqlDatabase; +> extends MySqlDatabase { + static readonly [entityKind]: string = 'PlanetScaleDatabase'; +} export function drizzle = Record>( client: Client | Connection, @@ -82,5 +85,5 @@ Starting from version 0.30.0, you will encounter an error if you attempt to use } const session = new PlanetscaleSession(client, dialect, undefined, schema, { logger }); - return new MySqlDatabase(dialect, session, schema, 'planetscale') as PlanetScaleDatabase; + return new PlanetScaleDatabase(dialect, session, schema as any, 'planetscale') as PlanetScaleDatabase; } diff --git a/drizzle-orm/src/postgres-js/driver.ts b/drizzle-orm/src/postgres-js/driver.ts index 7f44344e8..6714cff8d 100644 --- a/drizzle-orm/src/postgres-js/driver.ts +++ b/drizzle-orm/src/postgres-js/driver.ts @@ -1,4 +1,5 @@ import type { Sql } from 'postgres'; +import { entityKind } from '~/entity.ts'; import { DefaultLogger } from '~/logger.ts'; import { PgDatabase } from '~/pg-core/db.ts'; import { PgDialect } from '~/pg-core/dialect.ts'; @@ -12,9 +13,11 @@ import type { DrizzleConfig } from '~/utils.ts'; import type { PostgresJsQueryResultHKT } from './session.ts'; import { PostgresJsSession } from './session.ts'; -export type PostgresJsDatabase< +export class PostgresJsDatabase< TSchema extends Record = Record, -> = PgDatabase; +> extends PgDatabase { + static readonly [entityKind]: string = 'PostgresJsDatabase'; +} export function drizzle = Record>( client: Sql, @@ -52,5 +55,5 @@ export function drizzle = Record; + return new PostgresJsDatabase(dialect, session, schema as any) as PostgresJsDatabase; } diff --git a/drizzle-orm/src/tidb-serverless/driver.ts b/drizzle-orm/src/tidb-serverless/driver.ts index bdd5324db..b762bd889 100644 --- a/drizzle-orm/src/tidb-serverless/driver.ts +++ b/drizzle-orm/src/tidb-serverless/driver.ts @@ -1,4 +1,5 @@ import type { Connection } from '@tidbcloud/serverless'; +import { entityKind } from '~/entity.ts'; import type { Logger } from '~/logger.ts'; import { DefaultLogger } from '~/logger.ts'; import { MySqlDatabase } from '~/mysql-core/db.ts'; @@ -17,9 +18,11 @@ export interface TiDBServerlessSDriverOptions { logger?: Logger; } -export type TiDBServerlessDatabase< +export class TiDBServerlessDatabase< TSchema extends Record = Record, -> = MySqlDatabase; +> extends MySqlDatabase { + static readonly [entityKind]: string = 'TiDBServerlessDatabase'; +} export function drizzle = Record>( client: Connection, @@ -47,5 +50,5 @@ export function drizzle = Record; + return new TiDBServerlessDatabase(dialect, session, schema as any, 'default') as TiDBServerlessDatabase; } diff --git a/drizzle-orm/src/vercel-postgres/driver.ts b/drizzle-orm/src/vercel-postgres/driver.ts index 07e73c732..bc990d0b3 100644 --- a/drizzle-orm/src/vercel-postgres/driver.ts +++ b/drizzle-orm/src/vercel-postgres/driver.ts @@ -42,9 +42,11 @@ export class VercelPgDriver { } } -export type VercelPgDatabase< +export class VercelPgDatabase< TSchema extends Record = Record, -> = PgDatabase; +> extends PgDatabase { + static readonly [entityKind]: string = 'VercelPgDatabase'; +} export function drizzle = Record>( client: VercelPgClient, @@ -73,5 +75,5 @@ export function drizzle = Record; + return new VercelPgDatabase(dialect, session, schema as any) as VercelPgDatabase; } From 79b2dd953a09fff89280f7a7f0aef21f331aaeb7 Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Tue, 3 Sep 2024 12:51:09 +0300 Subject: [PATCH 32/56] Fixed overload order for better autocomplete --- drizzle-orm/src/monodriver.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 41bb202e8..0eb2214ee 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -168,6 +168,14 @@ const removeKey = , TKey extends keyof TReco return obj; }; +export async function drizzle = Record>( + client: 'aws-data-api-pg', + params: { connection?: RDSConfig } & DrizzleAwsDataApiPgConfig, +): Promise>; +export async function drizzle = Record>( + client: 'mysql2', + params: { connection: Mysql2Config | string } & MySql2DrizzleConfig, +): Promise>; export async function drizzle = Record>( client: 'node-postgres', params: { connection: NodePGPoolConfig } & DrizzleConfig, @@ -188,18 +196,10 @@ export async function drizzle = Record, ): Promise>; -export async function drizzle = Record>( - client: 'aws-data-api-pg', - params?: { connection?: RDSConfig } & DrizzleAwsDataApiPgConfig, -): Promise>; export async function drizzle = Record>( client: 'planetscale', params: { connection: PlanetscaleConfig } & DrizzleConfig, ): Promise>; -export async function drizzle = Record>( - client: 'mysql2', - params: { connection: Mysql2Config | string } & MySql2DrizzleConfig, -): Promise>; export async function drizzle = Record>( client: 'tidb-serverless', params: { connection: TiDBServerlessConfig } & DrizzleConfig, @@ -346,3 +346,9 @@ export async function drizzle< } } } + +const db = await drizzle('aws-data-api-pg', { + database: '', + resourceArn: '', + secretArn: '', +}); From 977fdd241d3dedf34c48f2c0262d6a9f6d470a6d Mon Sep 17 00:00:00 2001 From: Sukairo-02 Date: Tue, 3 Sep 2024 12:51:30 +0300 Subject: [PATCH 33/56] Removed garbage --- drizzle-orm/src/monodriver.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 0eb2214ee..05c676157 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -346,9 +346,3 @@ export async function drizzle< } } } - -const db = await drizzle('aws-data-api-pg', { - database: '', - resourceArn: '', - secretArn: '', -}); From 3430e1c5a502191869e5925b37a9b48fcd7af60f Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 3 Sep 2024 15:37:53 +0300 Subject: [PATCH 34/56] Added missing postgres.js to monomigrator --- drizzle-orm/src/monomigrator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drizzle-orm/src/monomigrator.ts b/drizzle-orm/src/monomigrator.ts index eec37bbdf..1fc9dbc71 100644 --- a/drizzle-orm/src/monomigrator.ts +++ b/drizzle-orm/src/monomigrator.ts @@ -18,6 +18,7 @@ import type { PgRemoteDatabase } from './pg-proxy/index.ts'; import type { ProxyMigrator as PgProxyMigrator } from './pg-proxy/migrator.ts'; import type { PgliteDatabase } from './pglite/index.ts'; import type { PlanetScaleDatabase } from './planetscale-serverless/index.ts'; +import type { PostgresJsDatabase } from './postgres-js/driver.ts'; import type { ProxyMigrator as SQLiteProxyMigrator } from './sqlite-proxy/migrator.ts'; import type { VercelPgDatabase } from './vercel-postgres/index.ts'; import type { XataHttpDatabase } from './xata-http/index.ts'; @@ -83,6 +84,7 @@ export async function migrate( config: string | MigrationConfig, ): Promise; export async function migrate(db: PgliteDatabase, config: string | MigrationConfig): Promise; +export async function migrate(db: PostgresJsDatabase, config: string | MigrationConfig): Promise; export async function migrate( db: PlanetScaleDatabase, config: MigrationConfig, From 56f7b1663470fa341f17b088d1f25b0a99c9c62f Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Tue, 3 Sep 2024 15:54:02 +0300 Subject: [PATCH 35/56] Fixed missing defaults in templates --- drizzle-orm/src/better-sqlite3/driver.ts | 2 +- drizzle-orm/src/expo-sqlite/driver.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drizzle-orm/src/better-sqlite3/driver.ts b/drizzle-orm/src/better-sqlite3/driver.ts index 8d19cd8ab..8fe7c00fb 100644 --- a/drizzle-orm/src/better-sqlite3/driver.ts +++ b/drizzle-orm/src/better-sqlite3/driver.ts @@ -12,7 +12,7 @@ import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; import type { DrizzleConfig } from '~/utils.ts'; import { BetterSQLiteSession } from './session.ts'; -export class BetterSQLite3Database> +export class BetterSQLite3Database = Record> extends BaseSQLiteDatabase<'sync', RunResult, TSchema> { static readonly [entityKind]: string = 'BetterSQLite3Database'; diff --git a/drizzle-orm/src/expo-sqlite/driver.ts b/drizzle-orm/src/expo-sqlite/driver.ts index 51cb1a204..fb858e482 100644 --- a/drizzle-orm/src/expo-sqlite/driver.ts +++ b/drizzle-orm/src/expo-sqlite/driver.ts @@ -12,7 +12,7 @@ import { SQLiteSyncDialect } from '~/sqlite-core/dialect.ts'; import type { DrizzleConfig } from '~/utils.ts'; import { ExpoSQLiteSession } from './session.ts'; -export class ExpoSQLiteDatabase> +export class ExpoSQLiteDatabase = Record> extends BaseSQLiteDatabase<'sync', SQLiteRunResult, TSchema> { static readonly [entityKind]: string = 'ExpoSQLiteDatabase'; From 091a8f4eeddc4707145ed731fa87ed271ffdeb78 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 13:00:44 +0300 Subject: [PATCH 36/56] Improved types, matched supported instances of monodriver and monomigrator, fixed pg withReplicas type's schema inferrence, fixed lack of [entityKind] on some instances, fixed migrators not accepting string as a config --- drizzle-orm/src/libsql/migrator.ts | 2 +- drizzle-orm/src/monodriver.ts | 134 ++++-------- drizzle-orm/src/monomigrator.ts | 190 +++--------------- drizzle-orm/src/mysql2/driver.ts | 8 +- drizzle-orm/src/mysql2/migrator.ts | 11 +- drizzle-orm/src/node-postgres/driver.ts | 8 +- drizzle-orm/src/pg-core/db.ts | 6 +- .../src/planetscale-serverless/migrator.ts | 11 +- 8 files changed, 110 insertions(+), 260 deletions(-) diff --git a/drizzle-orm/src/libsql/migrator.ts b/drizzle-orm/src/libsql/migrator.ts index aa262bc2d..d362a2e4d 100644 --- a/drizzle-orm/src/libsql/migrator.ts +++ b/drizzle-orm/src/libsql/migrator.ts @@ -5,7 +5,7 @@ import type { LibSQLDatabase } from './driver.ts'; export async function migrate>( db: LibSQLDatabase, - config: MigrationConfig, + config: MigrationConfig | string, ) { const migrations = readMigrationFiles(config); const migrationsTable = config === undefined diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 05c676157..d2b86848b 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -71,7 +71,7 @@ type MonodriverNeonHttpConfig = { options?: NeonHttpConfig; }; -type DatabaseVendor = +type DatabaseClient = | 'node-postgres' | 'postgres-js' | 'neon-serverless' @@ -102,51 +102,50 @@ type ClientDrizzleInstanceMap> = { 'better-sqlite3': BetterSQLite3Database; }; -type InitializerParams< - TSchema extends Record = Record, -> = - | ({ +type InitializerParams = { + 'node-postgres': { connection: NodePGPoolConfig; - } & DrizzleConfig) - | ({ - connection: PostgresJSOptions>; - } & DrizzleConfig) - | ({ + }; + 'postgres-js': { + connection: string | PostgresJSOptions>; + }; + 'neon-serverless': { connection: NeonServerlessConfig; - } & DrizzleConfig) - | ({ + }; + 'neon-http': { connection: MonodriverNeonHttpConfig; - } & DrizzleConfig) - | ({ + }; + 'vercel-postgres': { connection: VercelPool; - } & DrizzleConfig) - | ({ - connection: RDSConfig; - } & DrizzleAwsDataApiPgConfig) - | ({ + }; + 'aws-data-api-pg': { + connection?: RDSConfig; + }; + planetscale: { connection: PlanetscaleConfig; - } & DrizzleConfig) - | ({ - connection: Mysql2Config; - } & MySql2DrizzleConfig) - | ({ + }; + mysql2: { + connection: Mysql2Config | string; + }; + 'tidb-serverless': { connection: TiDBServerlessConfig; - } & DrizzleConfig) - | ({ + }; + libsql: { connection: LibsqlConfig; - } & DrizzleConfig) - | ({ + }; + d1: { connection: D1Database; - } & DrizzleConfig) - | ({ + }; + 'bun:sqlite': { connection?: BunSqliteDatabaseConfig; - } & DrizzleConfig) - | ({ + }; + 'better-sqlite3': { connection?: BetterSQLite3DatabaseConfig; - } & DrizzleConfig); + }; +}; type DetermineClient< - TVendor extends DatabaseVendor, + TVendor extends DatabaseClient, TSchema extends Record, > = ClientDrizzleInstanceMap< TSchema @@ -167,64 +166,17 @@ const removeKey = , TKey extends keyof TReco delete ( obj).key; return obj; }; - -export async function drizzle = Record>( - client: 'aws-data-api-pg', - params: { connection?: RDSConfig } & DrizzleAwsDataApiPgConfig, -): Promise>; -export async function drizzle = Record>( - client: 'mysql2', - params: { connection: Mysql2Config | string } & MySql2DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'node-postgres', - params: { connection: NodePGPoolConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'postgres-js', - params: { connection: string | PostgresJSOptions> } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'neon-serverless', - params: { connection: NeonServerlessConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'neon-http', - params: { connection: MonodriverNeonHttpConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'vercel-postgres', - params: { connection: VercelPool } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'planetscale', - params: { connection: PlanetscaleConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'tidb-serverless', - params: { connection: TiDBServerlessConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'libsql', - params: { connection: LibsqlConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'd1', - params: { connection: D1Database } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'bun:sqlite', - params?: { connection?: BunSqliteDatabaseConfig } & DrizzleConfig, -): Promise>; -export async function drizzle = Record>( - client: 'better-sqlite3', - params?: { connection?: BetterSQLite3DatabaseConfig } & DrizzleConfig, -): Promise>; export async function drizzle< - TVendor extends DatabaseVendor, - TSchema extends Record, - TParams extends InitializerParams, ->(client: TVendor, params?: TParams): Promise> { + TClient extends DatabaseClient, + TSchema extends Record = Record, +>( + client: TClient, + params: + & InitializerParams[TClient] + & (TClient extends 'mysql2' ? MySql2DrizzleConfig + : TClient extends 'aws-data-api-pg' ? DrizzleAwsDataApiPgConfig + : DrizzleConfig), +): Promise> { const connection = params?.connection; const drizzleConfig = params ? removeKey(params, 'connection') : undefined; diff --git a/drizzle-orm/src/monomigrator.ts b/drizzle-orm/src/monomigrator.ts index 1fc9dbc71..aa590c8d7 100644 --- a/drizzle-orm/src/monomigrator.ts +++ b/drizzle-orm/src/monomigrator.ts @@ -4,225 +4,101 @@ import type { BetterSQLite3Database } from './better-sqlite3/index.ts'; import type { BunSQLiteDatabase } from './bun-sqlite/index.ts'; import type { DrizzleD1Database } from './d1/index.ts'; import { entityKind } from './entity.ts'; -import type { ExpoSQLiteDatabase } from './expo-sqlite/index.ts'; import type { LibSQLDatabase } from './libsql/index.ts'; import type { MigrationConfig } from './migrator.ts'; -import type { MySqlRemoteDatabase } from './mysql-proxy/index.ts'; -import type { ProxyMigrator as MySqlProxyMigrator } from './mysql-proxy/migrator.ts'; import type { MySql2Database } from './mysql2/index.ts'; import type { NeonHttpDatabase } from './neon-http/index.ts'; import type { NeonDatabase } from './neon-serverless/index.ts'; import type { NodePgDatabase } from './node-postgres/index.ts'; -import type { OPSQLiteDatabase } from './op-sqlite/index.ts'; -import type { PgRemoteDatabase } from './pg-proxy/index.ts'; -import type { ProxyMigrator as PgProxyMigrator } from './pg-proxy/migrator.ts'; -import type { PgliteDatabase } from './pglite/index.ts'; import type { PlanetScaleDatabase } from './planetscale-serverless/index.ts'; -import type { PostgresJsDatabase } from './postgres-js/driver.ts'; -import type { ProxyMigrator as SQLiteProxyMigrator } from './sqlite-proxy/migrator.ts'; +import type { PostgresJsDatabase } from './postgres-js/index.ts'; +import type { TiDBServerlessDatabase } from './tidb-serverless/index.ts'; import type { VercelPgDatabase } from './vercel-postgres/index.ts'; -import type { XataHttpDatabase } from './xata-http/index.ts'; -import type { MigrationConfig as XataHttpMigrationConfig } from './xata-http/migrator.ts'; -type OPSQLiteMigrationConfig = { - journal: { - entries: { idx: number; when: number; tag: string; breakpoints: boolean }[]; - }; - migrations: Record; -}; - -type ExpoSQLiteMigrationConfig = { - journal: { - entries: { idx: number; when: number; tag: string; breakpoints: boolean }[]; - }; - migrations: Record; -}; - -export async function migrate( - db: AwsDataApiPgDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: BetterSQLite3Database, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: BunSQLiteDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate(db: DrizzleD1Database, config: string | MigrationConfig): Promise; -export async function migrate( - db: ExpoSQLiteDatabase, - config: ExpoSQLiteMigrationConfig, -): Promise; -export async function migrate(db: LibSQLDatabase, config: MigrationConfig): Promise; -export async function migrate( - db: MySqlRemoteDatabase, - callback: MySqlProxyMigrator, - config: MigrationConfig, -): Promise; -export async function migrate(db: MySql2Database, config: MigrationConfig): Promise; -export async function migrate( - db: NeonHttpDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: NeonDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: NodePgDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: OPSQLiteDatabase, - config: OPSQLiteMigrationConfig, -): Promise; -export async function migrate( - db: PgRemoteDatabase, - callback: PgProxyMigrator, - config: string | MigrationConfig, -): Promise; -export async function migrate(db: PgliteDatabase, config: string | MigrationConfig): Promise; -export async function migrate(db: PostgresJsDatabase, config: string | MigrationConfig): Promise; -export async function migrate( - db: PlanetScaleDatabase, - config: MigrationConfig, -): Promise; -export async function migrate( - db: PlanetScaleDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: PgRemoteDatabase, - callback: SQLiteProxyMigrator, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: PlanetScaleDatabase, - config: MigrationConfig, -): Promise; export async function migrate( - db: VercelPgDatabase, - config: string | MigrationConfig, -): Promise; -export async function migrate( - db: XataHttpDatabase, - config: string | XataHttpMigrationConfig, -): Promise; -export async function migrate( - db: any, + db: + | AwsDataApiPgDatabase + | BetterSQLite3Database + | BunSQLiteDatabase + | DrizzleD1Database + | LibSQLDatabase + | MySql2Database + | NeonHttpDatabase + | NeonDatabase + | NodePgDatabase + | PlanetScaleDatabase + | PostgresJsDatabase + | VercelPgDatabase + | TiDBServerlessDatabase, config: | string - | MigrationConfig - | ExpoSQLiteMigrationConfig - | OPSQLiteMigrationConfig - | XataHttpMigrationConfig - | PgProxyMigrator - | MySqlProxyMigrator - | SQLiteProxyMigrator, - extraConfig?: string | MigrationConfig | undefined, + | MigrationConfig, ) { - switch (db[entityKind]) { + switch (( db)[entityKind]) { case 'AwsDataApiPgDatabase': { const { migrate } = await import('./aws-data-api/pg/migrator'); - return migrate(db, config as string | MigrationConfig); + return migrate(db as AwsDataApiPgDatabase, config as string | MigrationConfig); } case 'BetterSQLite3Database': { const { migrate } = await import('./better-sqlite3/migrator'); - return migrate(db, config as string | MigrationConfig); + return migrate(db as BetterSQLite3Database, config as string | MigrationConfig); } case 'BunSQLiteDatabase': { const { migrate } = await import('./bun-sqlite/migrator'); - return migrate(db, config as string | MigrationConfig); + return migrate(db as BunSQLiteDatabase, config as string | MigrationConfig); } case 'D1Database': { const { migrate } = await import('./d1/migrator'); - return migrate(db, config as string | MigrationConfig); - } - case 'ExpoSQLiteDatabase': { - const { migrate } = await import('./expo-sqlite/migrator'); - - return migrate(db, config as ExpoSQLiteMigrationConfig); + return migrate(db as DrizzleD1Database, config as string | MigrationConfig); } case 'LibSQLDatabase': { const { migrate } = await import('./libsql/migrator'); - return migrate(db, config as MigrationConfig); + return migrate(db as LibSQLDatabase, config as string | MigrationConfig); } - case 'MySqlRemoteDatabase': { - const { migrate } = await import('./mysql-proxy/migrator'); - - return migrate(db, config as MySqlProxyMigrator, extraConfig as MigrationConfig); - } - case 'MySql2Driver': { + case 'MySql2Database': { const { migrate } = await import('./mysql2/migrator'); - return migrate(db, config as MigrationConfig); + return migrate(db as MySql2Database, config as string | MigrationConfig); } case 'NeonHttpDatabase': { const { migrate } = await import('./neon-http/migrator'); - return migrate(db, config as string | MigrationConfig); + return migrate(db as NeonHttpDatabase, config as string | MigrationConfig); } case 'NeonServerlessDatabase': { const { migrate } = await import('./neon-serverless/migrator'); - return migrate(db, config as string | MigrationConfig); + return migrate(db as NeonDatabase, config as string | MigrationConfig); } - case 'NodePgDriver': { + case 'NodePgDatabase': { const { migrate } = await import('./node-postgres/migrator'); - return migrate(db, config as string | MigrationConfig); - } - case 'OPSQLiteDatabase': { - const { migrate } = await import('./op-sqlite/migrator'); - - return migrate(db, config as OPSQLiteMigrationConfig); - } - case 'PgRemoteDatabase': { - const { migrate } = await import('./pg-proxy/migrator'); - - return migrate(db, config as PgProxyMigrator, extraConfig as string | MigrationConfig); - } - case 'PgliteDatabase': { - const { migrate } = await import('./pglite/migrator'); - - return migrate(db, config as string | MigrationConfig); + return migrate(db as NodePgDatabase, config as string | MigrationConfig); } case 'PlanetScaleDatabase': { const { migrate } = await import('./planetscale-serverless/migrator'); - return migrate(db, config as MigrationConfig); + return migrate(db as PlanetScaleDatabase, config as string | MigrationConfig); } case 'PostgresJsDatabase': { const { migrate } = await import('./postgres-js/migrator'); - return migrate(db, config as string | MigrationConfig); - } - case 'SqliteRemoteDatabase': { - const { migrate } = await import('./sqlite-proxy/migrator'); - - return migrate(db, config as SQLiteProxyMigrator, extraConfig as string | MigrationConfig); + return migrate(db as PostgresJsDatabase, config as string | MigrationConfig); } case 'TiDBServerlessDatabase': { const { migrate } = await import('./tidb-serverless/migrator'); - return migrate(db, config as MigrationConfig); + return migrate(db as TiDBServerlessDatabase, config as MigrationConfig); } case 'VercelPgDatabase': { const { migrate } = await import('./vercel-postgres/migrator'); - return migrate(db, config as string | MigrationConfig); - } - case 'XataHttpDatabase': { - const { migrate } = await import('./xata-http/migrator'); - - return migrate(db, config as string | MigrationConfig); + return migrate(db as VercelPgDatabase, config as string | MigrationConfig); } } } diff --git a/drizzle-orm/src/mysql2/driver.ts b/drizzle-orm/src/mysql2/driver.ts index 3b21bf11d..a8fb65c3b 100644 --- a/drizzle-orm/src/mysql2/driver.ts +++ b/drizzle-orm/src/mysql2/driver.ts @@ -40,9 +40,11 @@ export class MySql2Driver { export { MySqlDatabase } from '~/mysql-core/db.ts'; -export type MySql2Database< +export class MySql2Database< TSchema extends Record = Record, -> = MySqlDatabase; +> extends MySqlDatabase { + static readonly [entityKind]: string = 'MySql2Database'; +} export type MySql2DrizzleConfig = Record> = & Omit, 'schema'> @@ -87,7 +89,7 @@ export function drizzle = Record; + return new MySql2Database(dialect, session, schema as any, mode) as MySql2Database; } interface CallbackClient { diff --git a/drizzle-orm/src/mysql2/migrator.ts b/drizzle-orm/src/mysql2/migrator.ts index 2f3c9c3dc..ae56e01f1 100644 --- a/drizzle-orm/src/mysql2/migrator.ts +++ b/drizzle-orm/src/mysql2/migrator.ts @@ -4,8 +4,15 @@ import type { MySql2Database } from './driver.ts'; export async function migrate>( db: MySql2Database, - config: MigrationConfig, + config: MigrationConfig | string, ) { const migrations = readMigrationFiles(config); - await db.dialect.migrate(migrations, db.session, config); + + const preparedConfig = typeof config === 'string' + ? { + migrationsFolder: config, + } + : config; + + await db.dialect.migrate(migrations, db.session, preparedConfig); } diff --git a/drizzle-orm/src/node-postgres/driver.ts b/drizzle-orm/src/node-postgres/driver.ts index 4c233f891..15ac8fc06 100644 --- a/drizzle-orm/src/node-postgres/driver.ts +++ b/drizzle-orm/src/node-postgres/driver.ts @@ -45,9 +45,11 @@ export class NodePgDriver { } } -export type NodePgDatabase< +export class NodePgDatabase< TSchema extends Record = Record, -> = PgDatabase; +> extends PgDatabase { + static readonly [entityKind]: string = 'NodePgDatabase'; +} export function drizzle = Record>( client: NodePgClient, @@ -76,5 +78,5 @@ export function drizzle = Record; + return new NodePgDatabase(dialect, session, schema as any) as NodePgDatabase; } diff --git a/drizzle-orm/src/pg-core/db.ts b/drizzle-orm/src/pg-core/db.ts index 3c8da44f1..85dc797c9 100644 --- a/drizzle-orm/src/pg-core/db.ts +++ b/drizzle-orm/src/pg-core/db.ts @@ -631,7 +631,11 @@ export const withReplicas = < HKT extends PgQueryResultHKT, TFullSchema extends Record, TSchema extends TablesRelationalConfig, - Q extends PgDatabase, + Q extends PgDatabase< + HKT, + TFullSchema, + TSchema extends Record ? ExtractTablesWithRelations : TSchema + >, >( primary: Q, replicas: [Q, ...Q[]], diff --git a/drizzle-orm/src/planetscale-serverless/migrator.ts b/drizzle-orm/src/planetscale-serverless/migrator.ts index 5a668ae01..8b3713602 100644 --- a/drizzle-orm/src/planetscale-serverless/migrator.ts +++ b/drizzle-orm/src/planetscale-serverless/migrator.ts @@ -4,8 +4,15 @@ import type { PlanetScaleDatabase } from './driver.ts'; export async function migrate>( db: PlanetScaleDatabase, - config: MigrationConfig, + config: MigrationConfig | string, ) { const migrations = readMigrationFiles(config); - await db.dialect.migrate(migrations, db.session, config); + + const preparedConfig = typeof config === 'string' + ? { + migrationsFolder: config, + } + : config; + + await db.dialect.migrate(migrations, db.session, preparedConfig); } From 9af5fece91fc3ce5a3ed04427e3b6b99f67a1590 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 13:10:27 +0300 Subject: [PATCH 37/56] Fixed invalid [entityKind] call --- drizzle-orm/src/monomigrator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/monomigrator.ts b/drizzle-orm/src/monomigrator.ts index aa590c8d7..bee18ad46 100644 --- a/drizzle-orm/src/monomigrator.ts +++ b/drizzle-orm/src/monomigrator.ts @@ -34,7 +34,7 @@ export async function migrate( | string | MigrationConfig, ) { - switch (( db)[entityKind]) { + switch (( db).constructor[entityKind]) { case 'AwsDataApiPgDatabase': { const { migrate } = await import('./aws-data-api/pg/migrator'); From ca04053a4a6ab8dee378fdd9e5543fe38d09bbba Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 13:31:23 +0300 Subject: [PATCH 38/56] Added `:memory:` autocomplete --- drizzle-orm/src/monodriver.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index d2b86848b..d7955a585 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -52,18 +52,23 @@ type BunSqliteDatabaseOptions = type BunSqliteDatabaseConfig = | { - filename?: string; + filename?: ':memory:' | (string & {}); options?: BunSqliteDatabaseOptions; } - | string + | ':memory:' + | (string & {}) | undefined; type BetterSQLite3DatabaseConfig = | { - filename?: string | Buffer; + filename?: + | ':memory:' + | (string & {}) + | Buffer; options?: BetterSQLite3Options; } - | string + | ':memory:' + | (string & {}) | undefined; type MonodriverNeonHttpConfig = { From 1e1d8aec69610147aaac25997d7612a7173bc165 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 14:11:25 +0300 Subject: [PATCH 39/56] Removed leftover code from overloads, added `assertUnreachable` --- drizzle-orm/src/monodriver.ts | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index d7955a585..eb259e028 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -162,15 +162,10 @@ const importError = (libName: string) => { ); }; -const removeKey = , TKey extends keyof TRecord>( - obj: TRecord, - key: TKey, -): Omit => { - if (!(key in obj)) return obj; - - delete ( obj).key; - return obj; -}; +function assertUnreachable(_: never | undefined): never { + throw new Error("Didn't expect to get here"); +} + export async function drizzle< TClient extends DatabaseClient, TSchema extends Record = Record, @@ -182,8 +177,7 @@ export async function drizzle< : TClient extends 'aws-data-api-pg' ? DrizzleAwsDataApiPgConfig : DrizzleConfig), ): Promise> { - const connection = params?.connection; - const drizzleConfig = params ? removeKey(params, 'connection') : undefined; + const { connection, ...drizzleConfig } = params; switch (client) { case 'node-postgres': { @@ -296,10 +290,7 @@ export async function drizzle< return drizzle(sql, drizzleConfig) as any; } - default: { - throw new Error( - `Unsupported vendor for Drizzle ORM monodriver: '${client}'. Use dedicated drizzle initializer instead.`, - ); - } } + + assertUnreachable(client); } From 1de8bf8b2ec7871dde6fb274e24b4035c5358a8b Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 15:39:49 +0300 Subject: [PATCH 40/56] Exposed db drivers from monodriver --- drizzle-orm/src/monodriver.ts | 144 ++++++++++++++++++++++++++-------- 1 file changed, 112 insertions(+), 32 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index eb259e028..47e1881e1 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -1,19 +1,26 @@ /* eslint-disable import/extensions */ -import type { RDSDataClientConfig, RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; -import type { Config as LibsqlConfig } from '@libsql/client'; +import type { RDSDataClient, RDSDataClientConfig, RDSDataClientConfig as RDSConfig } from '@aws-sdk/client-rds-data'; +import type { Client as LibsqlClient, Config as LibsqlConfig } from '@libsql/client'; import type { HTTPTransactionOptions as NeonHttpConfig, + NeonQueryFunction, + Pool as NeonServerlessPool, PoolConfig as NeonServerlessConfig, } from '@neondatabase/serverless'; -import type { Config as PlanetscaleConfig } from '@planetscale/database'; -import type { Config as TiDBServerlessConfig } from '@tidbcloud/serverless'; -import type { VercelPool } from '@vercel/postgres'; -import type { Options as BetterSQLite3Options } from 'better-sqlite3'; -import type { PoolOptions as Mysql2Config } from 'mysql2'; -import type { PoolConfig as NodePGPoolConfig } from 'pg'; -import type { Options as PostgresJSOptions, PostgresType as PostgresJSPostgresType } from 'postgres'; +import type { Client as PlanetscaleClient, Config as PlanetscaleConfig } from '@planetscale/database'; +import type { Config as TiDBServerlessConfig, Connection as TiDBConnection } from '@tidbcloud/serverless'; +import type { QueryResult, QueryResultRow, VercelPool } from '@vercel/postgres'; +import type { Database as BetterSQLite3Database, Options as BetterSQLite3Options } from 'better-sqlite3'; +import type { Database as BunDatabase } from 'bun:sqlite'; +import type { Pool as Mysql2Pool, PoolOptions as Mysql2Config } from 'mysql2'; +import type { Pool as NodePgPool, PoolConfig as NodePGPoolConfig } from 'pg'; +import type { + Options as PostgresJSOptions, + PostgresType as PostgresJSPostgresType, + Sql as PostgresJsClient, +} from 'postgres'; import type { AwsDataApiPgDatabase, DrizzleAwsDataApiPgConfig } from './aws-data-api/pg/index.ts'; -import type { BetterSQLite3Database } from './better-sqlite3/index.ts'; +import type { BetterSQLite3Database as DrizzleBetterSQLite3Database } from './better-sqlite3/index.ts'; import type { BunSQLiteDatabase } from './bun-sqlite/index.ts'; import type { DrizzleD1Database } from './d1/index.ts'; import type { LibSQLDatabase } from './libsql/index.ts'; @@ -76,6 +83,8 @@ type MonodriverNeonHttpConfig = { options?: NeonHttpConfig; }; +type VercelPrimitive = string | number | boolean | undefined | null; + type DatabaseClient = | 'node-postgres' | 'postgres-js' @@ -104,7 +113,28 @@ type ClientDrizzleInstanceMap> = { libsql: LibSQLDatabase; d1: DrizzleD1Database; 'bun:sqlite': BunSQLiteDatabase; - 'better-sqlite3': BetterSQLite3Database; + 'better-sqlite3': DrizzleBetterSQLite3Database; +}; + +type ClientInstanceMap = { + 'node-postgres': NodePgPool; + 'postgres-js': PostgresJsClient; + 'neon-serverless': NeonServerlessPool; + 'neon-http': NeonQueryFunction; + 'vercel-postgres': + & VercelPool + & (( + strings: TemplateStringsArray, + ...values: VercelPrimitive[] + ) => Promise>); + 'aws-data-api-pg': RDSDataClient; + planetscale: PlanetscaleClient; + mysql2: Mysql2Pool; + 'tidb-serverless': TiDBConnection; + libsql: LibsqlClient; + d1: D1Database; + 'bun:sqlite': BunDatabase; + 'better-sqlite3': BetterSQLite3Database; }; type InitializerParams = { @@ -150,11 +180,15 @@ type InitializerParams = { }; type DetermineClient< - TVendor extends DatabaseClient, + TClient extends DatabaseClient, TSchema extends Record, -> = ClientDrizzleInstanceMap< - TSchema ->[TVendor]; +> = + & ClientDrizzleInstanceMap< + TSchema + >[TClient] + & { + $client: ClientInstanceMap[TClient]; + }; const importError = (libName: string) => { throw new Error( @@ -185,7 +219,10 @@ export async function drizzle< const { drizzle } = await import('./node-postgres'); const instance = new Pool(connection as NodePGPoolConfig); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'aws-data-api-pg': { const { RDSDataClient } = await import('@aws-sdk/client-rds-data').catch(() => @@ -194,7 +231,10 @@ export async function drizzle< const { drizzle } = await import('./aws-data-api/pg'); const instance = new RDSDataClient(connection as RDSDataClientConfig); - return drizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; + const db = drizzle(instance, drizzleConfig as any as DrizzleAwsDataApiPgConfig) as any; + db.$client = instance; + + return db; } case 'better-sqlite3': { const { default: Client } = await import('better-sqlite3').catch(() => importError('better-sqlite3')); @@ -205,12 +245,18 @@ export async function drizzle< const instance = new Client(filename, options); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } const instance = new Client(connection); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'bun:sqlite': { const { Database: Client } = await import('bun:sqlite').catch(() => importError('bun:sqlite')); @@ -221,30 +267,46 @@ export async function drizzle< const instance = new Client(filename, options); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } const instance = new Client(connection); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'd1': { const { drizzle } = await import('./d1'); - return drizzle(connection as D1Database, drizzleConfig) as any; + + const db = drizzle(connection as D1Database, drizzleConfig) as any; + db.$client = connection; + + return db; } case 'libsql': { const { createClient } = await import('@libsql/client').catch(() => importError('@libsql/client')); const { drizzle } = await import('./libsql'); const instance = createClient(connection as LibsqlConfig); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'mysql2': { - const { createConnection } = await import('mysql2/promise').catch(() => importError('mysql2/promise')); - const instance = await createConnection(connection as Mysql2Config); + const { createPool } = await import('mysql2/promise').catch(() => importError('mysql2/promise')); + const instance = createPool(connection as Mysql2Config); const { drizzle } = await import('./mysql2'); - return drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any; + const db = drizzle(instance, drizzleConfig as MySql2DrizzleConfig) as any; + db.$client = instance; + + return db; } case 'neon-http': { const { neon } = await import('@neondatabase/serverless').catch(() => importError('@neondatabase/serverless')); @@ -252,14 +314,20 @@ export async function drizzle< const { drizzle } = await import('./neon-http'); const instance = neon(connectionString, options); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'neon-serverless': { const { Pool } = await import('@neondatabase/serverless').catch(() => importError('@neondatabase/serverless')); const { drizzle } = await import('./neon-serverless'); const instance = new Pool(connection as NeonServerlessConfig); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'planetscale': { const { Client } = await import('@planetscale/database').catch(() => importError('@planetscale/database')); @@ -268,27 +336,39 @@ export async function drizzle< connection as PlanetscaleConfig, ); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'postgres-js': { const { default: client } = await import('postgres').catch(() => importError('postgres')); const { drizzle } = await import('./postgres-js'); const instance = client(connection as PostgresJSOptions>); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'tidb-serverless': { const { connect } = await import('@tidbcloud/serverless').catch(() => importError('@tidbcloud/serverless')); const { drizzle } = await import('./tidb-serverless'); const instance = connect(connection as TiDBServerlessConfig); - return drizzle(instance, drizzleConfig) as any; + const db = drizzle(instance, drizzleConfig) as any; + db.$client = instance; + + return db; } case 'vercel-postgres': { const { sql } = await import('@vercel/postgres').catch(() => importError('@vercel/postgres')); const { drizzle } = await import('./vercel-postgres'); - return drizzle(sql, drizzleConfig) as any; + const db = drizzle(sql, drizzleConfig) as any; + db.$client = sql; + + return db; } } From 0f81774950ce1548c16ac0e9ddb2be3127ddbddb Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 16:50:20 +0300 Subject: [PATCH 41/56] Fixed test tables using already existing names --- integration-tests/tests/mysql/mysql-common.ts | 12 ++++++------ integration-tests/tests/pg/pg-common.ts | 12 ++++++------ integration-tests/tests/sqlite/sqlite-common.ts | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index 05c69cada..7a02a251d 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -3581,7 +3581,7 @@ export function tests(driver?: string) { test('$count separate', async (ctx) => { const { db } = ctx.mysql; - const countTestTable = mysqlTable('users_distinct', { + const countTestTable = mysqlTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -3606,7 +3606,7 @@ export function tests(driver?: string) { test('$count embedded', async (ctx) => { const { db } = ctx.mysql; - const countTestTable = mysqlTable('users_distinct', { + const countTestTable = mysqlTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -3638,7 +3638,7 @@ export function tests(driver?: string) { test('$count separate reuse', async (ctx) => { const { db } = ctx.mysql; - const countTestTable = mysqlTable('users_distinct', { + const countTestTable = mysqlTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -3675,7 +3675,7 @@ export function tests(driver?: string) { test('$count embedded reuse', async (ctx) => { const { db } = ctx.mysql; - const countTestTable = mysqlTable('users_distinct', { + const countTestTable = mysqlTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -3734,7 +3734,7 @@ export function tests(driver?: string) { test('$count separate with filters', async (ctx) => { const { db } = ctx.mysql; - const countTestTable = mysqlTable('users_distinct', { + const countTestTable = mysqlTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -3759,7 +3759,7 @@ export function tests(driver?: string) { test('$count embedded with filters', async (ctx) => { const { db } = ctx.mysql; - const countTestTable = mysqlTable('users_distinct', { + const countTestTable = mysqlTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); diff --git a/integration-tests/tests/pg/pg-common.ts b/integration-tests/tests/pg/pg-common.ts index 3f3dd75cc..384c3dab3 100644 --- a/integration-tests/tests/pg/pg-common.ts +++ b/integration-tests/tests/pg/pg-common.ts @@ -4664,7 +4664,7 @@ export function tests() { test('$count separate', async (ctx) => { const { db } = ctx.pg; - const countTestTable = pgTable('users_distinct', { + const countTestTable = pgTable('count_test', { id: integer('id').notNull(), name: text('name').notNull(), }); @@ -4689,7 +4689,7 @@ export function tests() { test('$count embedded', async (ctx) => { const { db } = ctx.pg; - const countTestTable = pgTable('users_distinct', { + const countTestTable = pgTable('count_test', { id: integer('id').notNull(), name: text('name').notNull(), }); @@ -4721,7 +4721,7 @@ export function tests() { test('$count separate reuse', async (ctx) => { const { db } = ctx.pg; - const countTestTable = pgTable('users_distinct', { + const countTestTable = pgTable('count_test', { id: integer('id').notNull(), name: text('name').notNull(), }); @@ -4758,7 +4758,7 @@ export function tests() { test('$count embedded reuse', async (ctx) => { const { db } = ctx.pg; - const countTestTable = pgTable('users_distinct', { + const countTestTable = pgTable('count_test', { id: integer('id').notNull(), name: text('name').notNull(), }); @@ -4817,7 +4817,7 @@ export function tests() { test('$count separate with filters', async (ctx) => { const { db } = ctx.pg; - const countTestTable = pgTable('users_distinct', { + const countTestTable = pgTable('count_test', { id: integer('id').notNull(), name: text('name').notNull(), }); @@ -4842,7 +4842,7 @@ export function tests() { test('$count embedded with filters', async (ctx) => { const { db } = ctx.pg; - const countTestTable = pgTable('users_distinct', { + const countTestTable = pgTable('count_test', { id: integer('id').notNull(), name: text('name').notNull(), }); diff --git a/integration-tests/tests/sqlite/sqlite-common.ts b/integration-tests/tests/sqlite/sqlite-common.ts index ed13f5b7b..5711b5d1d 100644 --- a/integration-tests/tests/sqlite/sqlite-common.ts +++ b/integration-tests/tests/sqlite/sqlite-common.ts @@ -2683,7 +2683,7 @@ export function tests() { test('$count separate', async (ctx) => { const { db } = ctx.sqlite; - const countTestTable = sqliteTable('users_distinct', { + const countTestTable = sqliteTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -2708,7 +2708,7 @@ export function tests() { test('$count embedded', async (ctx) => { const { db } = ctx.sqlite; - const countTestTable = sqliteTable('users_distinct', { + const countTestTable = sqliteTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -2740,7 +2740,7 @@ export function tests() { test('$count separate reuse', async (ctx) => { const { db } = ctx.sqlite; - const countTestTable = sqliteTable('users_distinct', { + const countTestTable = sqliteTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -2777,7 +2777,7 @@ export function tests() { test('$count embedded reuse', async (ctx) => { const { db } = ctx.sqlite; - const countTestTable = sqliteTable('users_distinct', { + const countTestTable = sqliteTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -2836,7 +2836,7 @@ export function tests() { test('$count separate with filters', async (ctx) => { const { db } = ctx.sqlite; - const countTestTable = sqliteTable('users_distinct', { + const countTestTable = sqliteTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); @@ -2861,7 +2861,7 @@ export function tests() { test('$count embedded with filters', async (ctx) => { const { db } = ctx.sqlite; - const countTestTable = sqliteTable('users_distinct', { + const countTestTable = sqliteTable('count_test', { id: int('id').notNull(), name: text('name').notNull(), }); From f0275007cd028ed8870b08fa9016cc1a38e75671 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 17:52:29 +0300 Subject: [PATCH 42/56] Fixed faulty test cases; Fixed lack of count type conversion in mysql; added all method functionality to pg proxy driver; added websocket instance to monodriver neon serverless config --- drizzle-orm/src/monodriver.ts | 15 +++++++++++++-- .../src/mysql-core/query-builders/count.ts | 6 ++++-- drizzle-orm/src/pg-proxy/session.ts | 4 +++- integration-tests/tests/mysql/mysql-common.ts | 3 +-- integration-tests/tests/pg/pg-common.ts | 3 +-- integration-tests/tests/sqlite/sqlite-common.ts | 3 +-- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 47e1881e1..0706f271a 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -209,9 +209,14 @@ export async function drizzle< & InitializerParams[TClient] & (TClient extends 'mysql2' ? MySql2DrizzleConfig : TClient extends 'aws-data-api-pg' ? DrizzleAwsDataApiPgConfig + : TClient extends 'neon-serverless' ? DrizzleConfig & { + ws?: any; + } : DrizzleConfig), ): Promise> { - const { connection, ...drizzleConfig } = params; + const { connection, ws, ...drizzleConfig } = params as typeof params & { + ws?: any; + }; switch (client) { case 'node-postgres': { @@ -320,10 +325,16 @@ export async function drizzle< return db; } case 'neon-serverless': { - const { Pool } = await import('@neondatabase/serverless').catch(() => importError('@neondatabase/serverless')); + const { Pool, neonConfig } = await import('@neondatabase/serverless').catch(() => + importError('@neondatabase/serverless') + ); const { drizzle } = await import('./neon-serverless'); const instance = new Pool(connection as NeonServerlessConfig); + if (ws) { + neonConfig.webSocketConstructor = ws; + } + const db = drizzle(instance, drizzleConfig) as any; db.$client = instance; diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index 751ba61c7..b69d92777 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -27,7 +27,9 @@ export class MySqlCountBuilder< source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters}`; + return sql`select cast(count(*) as UNSIGNED) as count from ${source}${ + sql.raw(' where ').if(filters) + }${filters}`; } constructor( @@ -53,7 +55,7 @@ export class MySqlCountBuilder< onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { return Promise.resolve(this.session.all(this.sql)).then((it) => { - return (<[{ 'count(*)': number }]> it)[0]['count(*)']; + return (<[{ count: number }]> it)[0]['count']; }) .then( onfulfilled, diff --git a/drizzle-orm/src/pg-proxy/session.ts b/drizzle-orm/src/pg-proxy/session.ts index eb6a1b1a3..b5adad36c 100644 --- a/drizzle-orm/src/pg-proxy/session.ts +++ b/drizzle-orm/src/pg-proxy/session.ts @@ -130,7 +130,9 @@ export class PreparedQuery extends PreparedQueryB }); } - async all() {} + async all() { + return this.execute(); + } /** @internal */ isResponseInArrayMode(): boolean { diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index 7a02a251d..7224b06f4 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -3706,8 +3706,6 @@ export function tests(driver?: string) { await db.execute(sql`drop table ${countTestTable}`); - await db.execute(sql`drop table ${countTestTable}`); - expect(count1).toStrictEqual([ { count: 4 }, { count: 4 }, @@ -3784,6 +3782,7 @@ export function tests(driver?: string) { { count: 3 }, { count: 3 }, { count: 3 }, + { count: 3 }, ]); }); }); diff --git a/integration-tests/tests/pg/pg-common.ts b/integration-tests/tests/pg/pg-common.ts index 384c3dab3..b4afdcf64 100644 --- a/integration-tests/tests/pg/pg-common.ts +++ b/integration-tests/tests/pg/pg-common.ts @@ -4789,8 +4789,6 @@ export function tests() { await db.execute(sql`drop table ${countTestTable}`); - await db.execute(sql`drop table ${countTestTable}`); - expect(count1).toStrictEqual([ { count: 4 }, { count: 4 }, @@ -4867,6 +4865,7 @@ export function tests() { { count: 3 }, { count: 3 }, { count: 3 }, + { count: 3 }, ]); }); }); diff --git a/integration-tests/tests/sqlite/sqlite-common.ts b/integration-tests/tests/sqlite/sqlite-common.ts index 5711b5d1d..e8ddb86e6 100644 --- a/integration-tests/tests/sqlite/sqlite-common.ts +++ b/integration-tests/tests/sqlite/sqlite-common.ts @@ -2808,8 +2808,6 @@ export function tests() { await db.run(sql`drop table ${countTestTable}`); - await db.run(sql`drop table ${countTestTable}`); - expect(count1).toStrictEqual([ { count: 4 }, { count: 4 }, @@ -2886,6 +2884,7 @@ export function tests() { { count: 3 }, { count: 3 }, { count: 3 }, + { count: 3 }, ]); }); }); From 890375583ec038870e2b6ff79dc6236b9e6dee76 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 18:06:26 +0300 Subject: [PATCH 43/56] Switched mysql count to execute --- drizzle-orm/src/mysql-core/query-builders/count.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index b69d92777..a54a9eaf8 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -54,7 +54,7 @@ export class MySqlCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.all(this.sql)).then((it) => { + return Promise.resolve(this.session.execute(this.sql)).then((it) => { return (<[{ count: number }]> it)[0]['count']; }) .then( From a171311771930fe4cae5578dd7d93174652ef224 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Wed, 4 Sep 2024 18:08:50 +0300 Subject: [PATCH 44/56] Added cast to embedded --- drizzle-orm/src/mysql-core/query-builders/count.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index a54a9eaf8..01f9dfcd0 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -20,7 +20,7 @@ export class MySqlCountBuilder< source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`(select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters})`; + return sql`(select cast(count(*) as UNSIGNED) from ${source}${sql.raw(' where ').if(filters)}${filters})`; } private static buildCount( From 4f9c076e26a383c9e1c6a50eb01292a3007be2c8 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Thu, 5 Sep 2024 12:55:11 +0300 Subject: [PATCH 45/56] Switched to `.execute` for mysql, pg; reverted pg proxy session changes; fixed misplaced mysql tests; removed unused args --- drizzle-orm/src/mysql-core/db.ts | 2 +- .../src/mysql-core/query-builders/count.ts | 4 +-- drizzle-orm/src/pg-core/db.ts | 2 +- .../src/pg-core/query-builders/count.ts | 6 ++-- drizzle-orm/src/pg-proxy/session.ts | 1 - drizzle-orm/src/sqlite-core/db.ts | 2 +- .../src/sqlite-core/query-builders/count.ts | 2 -- integration-tests/tests/mysql/mysql-common.ts | 36 +++++++++---------- 8 files changed, 24 insertions(+), 31 deletions(-) diff --git a/drizzle-orm/src/mysql-core/db.ts b/drizzle-orm/src/mysql-core/db.ts index 419359022..8934c0edf 100644 --- a/drizzle-orm/src/mysql-core/db.ts +++ b/drizzle-orm/src/mysql-core/db.ts @@ -140,7 +140,7 @@ export class MySqlDatabase< source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, filters?: SQL, ) { - return new MySqlCountBuilder({ source, filters, dialect: this.dialect, session: this.session }); + return new MySqlCountBuilder({ source, filters, session: this.session }); } /** diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index 01f9dfcd0..1d5ac2a57 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -1,7 +1,6 @@ import { entityKind, sql } from '~/index.ts'; import type { SQLWrapper } from '~/sql/sql.ts'; import { SQL } from '~/sql/sql.ts'; -import type { MySqlDialect } from '../dialect.ts'; import type { MySqlSession } from '../session.ts'; import type { MySqlTable } from '../table.ts'; import type { MySqlViewBase } from '../view-base.ts'; @@ -36,7 +35,6 @@ export class MySqlCountBuilder< readonly params: { source: MySqlTable | MySqlViewBase | SQL | SQLWrapper; filters?: SQL; - dialect: MySqlDialect; session: TSession; }, ) { @@ -55,7 +53,7 @@ export class MySqlCountBuilder< onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { return Promise.resolve(this.session.execute(this.sql)).then((it) => { - return (<[{ count: number }]> it)[0]['count']; + return (<[[{ count: number }]]> it)[0][0]['count']; }) .then( onfulfilled, diff --git a/drizzle-orm/src/pg-core/db.ts b/drizzle-orm/src/pg-core/db.ts index 85dc797c9..62b64fb8f 100644 --- a/drizzle-orm/src/pg-core/db.ts +++ b/drizzle-orm/src/pg-core/db.ts @@ -141,7 +141,7 @@ export class PgDatabase< source: PgTable | PgViewBase | SQL | SQLWrapper, filters?: SQL, ) { - return new PgCountBuilder({ source, filters, dialect: this.dialect, session: this.session }); + return new PgCountBuilder({ source, filters, session: this.session }); } /** diff --git a/drizzle-orm/src/pg-core/query-builders/count.ts b/drizzle-orm/src/pg-core/query-builders/count.ts index 7ccd722a0..cfc4d583b 100644 --- a/drizzle-orm/src/pg-core/query-builders/count.ts +++ b/drizzle-orm/src/pg-core/query-builders/count.ts @@ -1,7 +1,6 @@ import { entityKind, sql } from '~/index.ts'; import type { SQLWrapper } from '~/sql/sql.ts'; import { SQL } from '~/sql/sql.ts'; -import type { PgDialect } from '../dialect.ts'; import type { PgSession } from '../session.ts'; import type { PgTable } from '../table.ts'; @@ -26,14 +25,13 @@ export class PgCountBuilder< source: PgTable | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`select count(*)::int from ${source}${sql.raw(' where ').if(filters)}${filters};`; + return sql`select count(*)::int as count from ${source}${sql.raw(' where ').if(filters)}${filters};`; } constructor( readonly params: { source: PgTable | SQL | SQLWrapper; filters?: SQL; - dialect: PgDialect; session: TSession; }, ) { @@ -51,7 +49,7 @@ export class PgCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.all(this.sql)).then((it) => { + return Promise.resolve(this.session.execute(this.sql)).then((it) => { return (<[{ count: number }]> it)[0]['count'] as number; }) .then( diff --git a/drizzle-orm/src/pg-proxy/session.ts b/drizzle-orm/src/pg-proxy/session.ts index b5adad36c..1a30c0a3c 100644 --- a/drizzle-orm/src/pg-proxy/session.ts +++ b/drizzle-orm/src/pg-proxy/session.ts @@ -131,7 +131,6 @@ export class PreparedQuery extends PreparedQueryB } async all() { - return this.execute(); } /** @internal */ diff --git a/drizzle-orm/src/sqlite-core/db.ts b/drizzle-orm/src/sqlite-core/db.ts index 75b088f6d..7ae2736e0 100644 --- a/drizzle-orm/src/sqlite-core/db.ts +++ b/drizzle-orm/src/sqlite-core/db.ts @@ -140,7 +140,7 @@ export class BaseSQLiteDatabase< source: SQLiteTable | SQLiteViewBase | SQL | SQLWrapper, filters?: SQL, ) { - return new SQLiteCountBuilder({ source, filters, dialect: this.dialect, session: this.session }); + return new SQLiteCountBuilder({ source, filters, session: this.session }); } /** diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.ts b/drizzle-orm/src/sqlite-core/query-builders/count.ts index ed6cd9a1d..f6434ee81 100644 --- a/drizzle-orm/src/sqlite-core/query-builders/count.ts +++ b/drizzle-orm/src/sqlite-core/query-builders/count.ts @@ -1,7 +1,6 @@ import { entityKind, sql } from '~/index.ts'; import type { SQLWrapper } from '~/sql/sql.ts'; import { SQL } from '~/sql/sql.ts'; -import type { SQLiteDialect } from '../dialect.ts'; import type { SQLiteSession } from '../session.ts'; import type { SQLiteTable } from '../table.ts'; import type { SQLiteView } from '../view.ts'; @@ -34,7 +33,6 @@ export class SQLiteCountBuilder< readonly params: { source: SQLiteTable | SQLiteView | SQL | SQLWrapper; filters?: SQL; - dialect: SQLiteDialect; session: TSession; }, ) { diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index 7224b06f4..8a2fb768b 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -3785,29 +3785,29 @@ export function tests(driver?: string) { { count: 3 }, ]); }); - }); - test('limit 0', async (ctx) => { - const { db } = ctx.mysql; + test('limit 0', async (ctx) => { + const { db } = ctx.mysql; - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select() - .from(usersTable) - .limit(0); + await db.insert(usersTable).values({ name: 'John' }); + const users = await db + .select() + .from(usersTable) + .limit(0); - expect(users).toEqual([]); - }); + expect(users).toEqual([]); + }); - test('limit -1', async (ctx) => { - const { db } = ctx.mysql; + test('limit -1', async (ctx) => { + const { db } = ctx.mysql; - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select() - .from(usersTable) - .limit(-1); + await db.insert(usersTable).values({ name: 'John' }); + const users = await db + .select() + .from(usersTable) + .limit(-1); - expect(users.length).toBeGreaterThan(0); + expect(users.length).toBeGreaterThan(0); + }); }); } From 99795fa6683210aac55598962d05f292767cefd0 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Thu, 5 Sep 2024 14:03:27 +0300 Subject: [PATCH 46/56] Moved type conversion drom db to ORM; improved types --- drizzle-orm/src/mysql-core/query-builders/count.ts | 12 ++++++------ drizzle-orm/src/pg-core/query-builders/count.ts | 10 ++++++---- drizzle-orm/src/sqlite-core/query-builders/count.ts | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index 1d5ac2a57..cf6188c2b 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -19,16 +19,14 @@ export class MySqlCountBuilder< source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`(select cast(count(*) as UNSIGNED) from ${source}${sql.raw(' where ').if(filters)}${filters})`; + return sql`(select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters})`; } private static buildCount( source: MySqlTable | MySqlViewBase | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`select cast(count(*) as UNSIGNED) as count from ${source}${ - sql.raw(' where ').if(filters) - }${filters}`; + return sql`select count(*) as count from ${source}${sql.raw(' where ').if(filters)}${filters}`; } constructor( @@ -40,6 +38,8 @@ export class MySqlCountBuilder< ) { super(MySqlCountBuilder.buildEmbeddedCount(params.source, params.filters).queryChunks); + this.mapWith(Number); + this.session = params.session; this.sql = MySqlCountBuilder.buildCount( @@ -52,8 +52,8 @@ export class MySqlCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.execute(this.sql)).then((it) => { - return (<[[{ count: number }]]> it)[0][0]['count']; + return Promise.resolve(this.session.execute<[[{ count: string }]]>(this.sql)).then((it) => { + return Number(it[0][0]['count']); }) .then( onfulfilled, diff --git a/drizzle-orm/src/pg-core/query-builders/count.ts b/drizzle-orm/src/pg-core/query-builders/count.ts index cfc4d583b..ca1f6b61d 100644 --- a/drizzle-orm/src/pg-core/query-builders/count.ts +++ b/drizzle-orm/src/pg-core/query-builders/count.ts @@ -18,14 +18,14 @@ export class PgCountBuilder< source: PgTable | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`(select count(*)::int from ${source}${sql.raw(' where ').if(filters)}${filters})`; + return sql`(select count(*) from ${source}${sql.raw(' where ').if(filters)}${filters})`; } private static buildCount( source: PgTable | SQL | SQLWrapper, filters?: SQL, ): SQL { - return sql`select count(*)::int as count from ${source}${sql.raw(' where ').if(filters)}${filters};`; + return sql`select count(*) as count from ${source}${sql.raw(' where ').if(filters)}${filters};`; } constructor( @@ -37,6 +37,8 @@ export class PgCountBuilder< ) { super(PgCountBuilder.buildEmbeddedCount(params.source, params.filters).queryChunks); + this.mapWith(Number); + this.session = params.session; this.sql = PgCountBuilder.buildCount( @@ -49,8 +51,8 @@ export class PgCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.execute(this.sql)).then((it) => { - return (<[{ count: number }]> it)[0]['count'] as number; + return Promise.resolve(this.session.execute<[{ count: string }]>(this.sql)).then((it) => { + return Number(it[0]['count']); }) .then( onfulfilled, diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.ts b/drizzle-orm/src/sqlite-core/query-builders/count.ts index f6434ee81..073562b8b 100644 --- a/drizzle-orm/src/sqlite-core/query-builders/count.ts +++ b/drizzle-orm/src/sqlite-core/query-builders/count.ts @@ -50,7 +50,7 @@ export class SQLiteCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.values(this.sql)).then((it) => it[0]![0] as number).then( + return Promise.resolve(this.session.values<[[number]]>(this.sql)).then((it) => it[0][0]).then( onfulfilled, onrejected, ); From 0105349d09192e76c927de12775c3d854372c290 Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Thu, 5 Sep 2024 15:39:48 +0300 Subject: [PATCH 47/56] Moved pg, mysql counts to sessions to resolve return shape conflicts --- drizzle-orm/src/mysql-core/query-builders/count.ts | 4 ++-- drizzle-orm/src/mysql-core/session.ts | 8 ++++++++ drizzle-orm/src/neon-http/session.ts | 10 +++++++++- drizzle-orm/src/node-postgres/session.ts | 9 ++++++++- drizzle-orm/src/pg-core/query-builders/count.ts | 4 ++-- drizzle-orm/src/pg-core/session.ts | 8 ++++++++ drizzle-orm/src/pglite/session.ts | 9 ++++++++- drizzle-orm/src/tidb-serverless/session.ts | 8 ++++++++ 8 files changed, 53 insertions(+), 7 deletions(-) diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index cf6188c2b..4e734135f 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -52,8 +52,8 @@ export class MySqlCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.execute<[[{ count: string }]]>(this.sql)).then((it) => { - return Number(it[0][0]['count']); + return Promise.resolve(this.session.count(this.sql)).then((it) => { + return it; }) .then( onfulfilled, diff --git a/drizzle-orm/src/mysql-core/session.ts b/drizzle-orm/src/mysql-core/session.ts index 6b6269639..021d4276d 100644 --- a/drizzle-orm/src/mysql-core/session.ts +++ b/drizzle-orm/src/mysql-core/session.ts @@ -86,6 +86,14 @@ export abstract class MySqlSession< abstract all(query: SQL): Promise; + async count(sql: SQL): Promise { + const res = await this.execute<[[{ count: string }]]>(sql); + + return Number( + res[0][0]['count'], + ); + } + abstract transaction( transaction: (tx: MySqlTransaction) => Promise, config?: MySqlTransactionConfig, diff --git a/drizzle-orm/src/neon-http/session.ts b/drizzle-orm/src/neon-http/session.ts index 6d7685116..a36b7222b 100644 --- a/drizzle-orm/src/neon-http/session.ts +++ b/drizzle-orm/src/neon-http/session.ts @@ -10,7 +10,7 @@ import type { PgQueryResultHKT, PgTransactionConfig, PreparedQueryConfig } from import { PgPreparedQuery as PgPreparedQuery, PgSession } from '~/pg-core/session.ts'; import type { RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; import type { PreparedQuery } from '~/session.ts'; -import { fillPlaceholders, type Query } from '~/sql/sql.ts'; +import { fillPlaceholders, type Query, type SQL } from '~/sql/sql.ts'; import { mapResultRow } from '~/utils.ts'; export type NeonHttpClient = NeonQueryFunction; @@ -161,6 +161,14 @@ export class NeonHttpSession< return this.client(query, params, { arrayMode: false, fullResults: true }); } + override async count(sql: SQL): Promise { + const res = await this.execute<[{ count: string }]>(sql); + + return Number( + res[0]['count'], + ); + } + override async transaction( _transaction: (tx: NeonTransaction) => Promise, // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/drizzle-orm/src/node-postgres/session.ts b/drizzle-orm/src/node-postgres/session.ts index 91a21312a..ef6779354 100644 --- a/drizzle-orm/src/node-postgres/session.ts +++ b/drizzle-orm/src/node-postgres/session.ts @@ -8,7 +8,7 @@ import type { SelectedFieldsOrdered } from '~/pg-core/query-builders/select.type import type { PgQueryResultHKT, PgTransactionConfig, PreparedQueryConfig } from '~/pg-core/session.ts'; import { PgPreparedQuery, PgSession } from '~/pg-core/session.ts'; import type { RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; -import { fillPlaceholders, type Query, sql } from '~/sql/sql.ts'; +import { fillPlaceholders, type Query, type SQL, sql } from '~/sql/sql.ts'; import { tracer } from '~/tracing.ts'; import { type Assume, mapResultRow } from '~/utils.ts'; @@ -164,6 +164,13 @@ export class NodePgSession< } } } + + override async count(sql: SQL): Promise { + const res = await this.execute<{ rows: [{ count: string }] }>(sql); + return Number( + res['rows'][0]['count'], + ); + } } export class NodePgTransaction< diff --git a/drizzle-orm/src/pg-core/query-builders/count.ts b/drizzle-orm/src/pg-core/query-builders/count.ts index ca1f6b61d..6867b0950 100644 --- a/drizzle-orm/src/pg-core/query-builders/count.ts +++ b/drizzle-orm/src/pg-core/query-builders/count.ts @@ -51,8 +51,8 @@ export class PgCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.execute<[{ count: string }]>(this.sql)).then((it) => { - return Number(it[0]['count']); + return Promise.resolve(this.session.count(this.sql)).then((it) => { + return it; }) .then( onfulfilled, diff --git a/drizzle-orm/src/pg-core/session.ts b/drizzle-orm/src/pg-core/session.ts index 434ebc086..ea820f2d8 100644 --- a/drizzle-orm/src/pg-core/session.ts +++ b/drizzle-orm/src/pg-core/session.ts @@ -86,6 +86,14 @@ export abstract class PgSession< ).all(); } + async count(sql: SQL): Promise { + const res = await this.execute<[{ count: string }]>(sql); + + return Number( + res[0]['count'], + ); + } + abstract transaction( transaction: (tx: PgTransaction) => Promise, config?: PgTransactionConfig, diff --git a/drizzle-orm/src/pglite/session.ts b/drizzle-orm/src/pglite/session.ts index c7a1dbb5d..ebf7701a6 100644 --- a/drizzle-orm/src/pglite/session.ts +++ b/drizzle-orm/src/pglite/session.ts @@ -7,7 +7,7 @@ import type { SelectedFieldsOrdered } from '~/pg-core/query-builders/select.type import type { PgQueryResultHKT, PgTransactionConfig, PreparedQueryConfig } from '~/pg-core/session.ts'; import { PgPreparedQuery, PgSession } from '~/pg-core/session.ts'; import type { RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts'; -import { fillPlaceholders, type Query, sql } from '~/sql/sql.ts'; +import { fillPlaceholders, type Query, type SQL, sql } from '~/sql/sql.ts'; import { type Assume, mapResultRow } from '~/utils.ts'; import { types } from '@electric-sql/pglite'; @@ -140,6 +140,13 @@ export class PgliteSession< return transaction(tx); }) as Promise; } + + override async count(sql: SQL): Promise { + const res = await this.execute<{ rows: [{ count: string }] }>(sql); + return Number( + res['rows'][0]['count'], + ); + } } export class PgliteTransaction< diff --git a/drizzle-orm/src/tidb-serverless/session.ts b/drizzle-orm/src/tidb-serverless/session.ts index 64a8d61d7..b01b9f948 100644 --- a/drizzle-orm/src/tidb-serverless/session.ts +++ b/drizzle-orm/src/tidb-serverless/session.ts @@ -139,6 +139,14 @@ export class TiDBServerlessSession< return this.client.execute(querySql.sql, querySql.params) as Promise; } + override async count(sql: SQL): Promise { + const res = await this.execute<{ rows: [{ count: string }] }>(sql); + + return Number( + res['rows'][0]['count'], + ); + } + override async transaction( transaction: (tx: TiDBServerlessTransaction) => Promise, ): Promise { From 865fc077cc2ee7ecfc6aa889e550d2cf1edd800e Mon Sep 17 00:00:00 2001 From: sukairo-02 Date: Thu, 5 Sep 2024 16:26:58 +0300 Subject: [PATCH 48/56] Fixed neon-http, planetscale; removed leftover lines; switched sqlite to session-based count --- drizzle-orm/src/mysql-core/query-builders/count.ts | 4 +--- drizzle-orm/src/neon-http/session.ts | 4 ++-- drizzle-orm/src/pg-core/query-builders/count.ts | 4 +--- drizzle-orm/src/planetscale-serverless/session.ts | 8 ++++++++ drizzle-orm/src/sqlite-core/query-builders/count.ts | 2 +- drizzle-orm/src/sqlite-core/session.ts | 6 ++++++ 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drizzle-orm/src/mysql-core/query-builders/count.ts b/drizzle-orm/src/mysql-core/query-builders/count.ts index 4e734135f..645bb4753 100644 --- a/drizzle-orm/src/mysql-core/query-builders/count.ts +++ b/drizzle-orm/src/mysql-core/query-builders/count.ts @@ -52,9 +52,7 @@ export class MySqlCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.count(this.sql)).then((it) => { - return it; - }) + return Promise.resolve(this.session.count(this.sql)) .then( onfulfilled, onrejected, diff --git a/drizzle-orm/src/neon-http/session.ts b/drizzle-orm/src/neon-http/session.ts index a36b7222b..4dd768d3e 100644 --- a/drizzle-orm/src/neon-http/session.ts +++ b/drizzle-orm/src/neon-http/session.ts @@ -162,10 +162,10 @@ export class NeonHttpSession< } override async count(sql: SQL): Promise { - const res = await this.execute<[{ count: string }]>(sql); + const res = await this.execute<{ rows: [{ count: string }] }>(sql); return Number( - res[0]['count'], + res['rows'][0]['count'], ); } diff --git a/drizzle-orm/src/pg-core/query-builders/count.ts b/drizzle-orm/src/pg-core/query-builders/count.ts index 6867b0950..c93cbb18d 100644 --- a/drizzle-orm/src/pg-core/query-builders/count.ts +++ b/drizzle-orm/src/pg-core/query-builders/count.ts @@ -51,9 +51,7 @@ export class PgCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.count(this.sql)).then((it) => { - return it; - }) + return Promise.resolve(this.session.count(this.sql)) .then( onfulfilled, onrejected, diff --git a/drizzle-orm/src/planetscale-serverless/session.ts b/drizzle-orm/src/planetscale-serverless/session.ts index f2275b7f2..987529d7c 100644 --- a/drizzle-orm/src/planetscale-serverless/session.ts +++ b/drizzle-orm/src/planetscale-serverless/session.ts @@ -164,6 +164,14 @@ export class PlanetscaleSession< ) => eQuery.rows as T[]); } + override async count(sql: SQL): Promise { + const res = await this.execute<{ rows: [{ count: string }] }>(sql); + + return Number( + res['rows'][0]['count'], + ); + } + override transaction( transaction: (tx: PlanetScaleTransaction) => Promise, ): Promise { diff --git a/drizzle-orm/src/sqlite-core/query-builders/count.ts b/drizzle-orm/src/sqlite-core/query-builders/count.ts index 073562b8b..1b19eed07 100644 --- a/drizzle-orm/src/sqlite-core/query-builders/count.ts +++ b/drizzle-orm/src/sqlite-core/query-builders/count.ts @@ -50,7 +50,7 @@ export class SQLiteCountBuilder< onfulfilled?: ((value: number) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { - return Promise.resolve(this.session.values<[[number]]>(this.sql)).then((it) => it[0][0]).then( + return Promise.resolve(this.session.count(this.sql)).then( onfulfilled, onrejected, ); diff --git a/drizzle-orm/src/sqlite-core/session.ts b/drizzle-orm/src/sqlite-core/session.ts index 4ac987b4a..d291b6fdf 100644 --- a/drizzle-orm/src/sqlite-core/session.ts +++ b/drizzle-orm/src/sqlite-core/session.ts @@ -187,6 +187,12 @@ export abstract class SQLiteSession< >; } + async count(sql: SQL) { + const result = await this.values(sql) as [[number]]; + + return result[0][0]; + } + /** @internal */ extractRawValuesValueFromBatchResult(_result: unknown): unknown { throw new Error('Not implemented'); From b60aa989454a55251c4929bc464112bbb7948822 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Thu, 5 Sep 2024 17:34:50 +0300 Subject: [PATCH 49/56] changed console.log outputs for connection to Sqlite returned removed console.log in mysql push --- drizzle-kit/src/cli/commands/push.ts | 1 + drizzle-kit/src/cli/connections.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drizzle-kit/src/cli/commands/push.ts b/drizzle-kit/src/cli/commands/push.ts index 1be844715..0cb3a8d28 100644 --- a/drizzle-kit/src/cli/commands/push.ts +++ b/drizzle-kit/src/cli/commands/push.ts @@ -78,6 +78,7 @@ export const mysqlPush = async ( }); if (verbose) { + console.log(); console.log( withStyle.warning('You are about to execute current statements:'), ); diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index 54945386e..fccc7def8 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -671,7 +671,7 @@ export const connectToSQLite = async ( } console.log( - "Please install 'better-sqlite3' for Drizzle Kit to connect to SQLite databases", + "Please install either 'better-sqlite3' or '@libsql/client' for Drizzle Kit to connect to SQLite databases", ); process.exit(1); }; From 388d422f478e3d4d918f423fa9d1e68d759de3f5 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Fri, 6 Sep 2024 16:02:15 +0300 Subject: [PATCH 50/56] Add draft for beta release --- changelogs/drizzle-kit/0.25.0.md | 0 changelogs/drizzle-orm/0.34.0.md | 141 +++++++++++++++++++ drizzle-kit/package.json | 2 +- drizzle-orm/package.json | 2 +- integration-tests/tests/pg/awsdatapi.test.ts | 14 +- integration-tests/tests/pg/neon-http.test.ts | 14 +- integration-tests/tests/pg/pg-common.ts | 8 +- integration-tests/tests/pg/vercel-pg.test.ts | 14 +- 8 files changed, 168 insertions(+), 27 deletions(-) create mode 100644 changelogs/drizzle-kit/0.25.0.md create mode 100644 changelogs/drizzle-orm/0.34.0.md diff --git a/changelogs/drizzle-kit/0.25.0.md b/changelogs/drizzle-kit/0.25.0.md new file mode 100644 index 000000000..e69de29bb diff --git a/changelogs/drizzle-orm/0.34.0.md b/changelogs/drizzle-orm/0.34.0.md new file mode 100644 index 000000000..4ceb1a630 --- /dev/null +++ b/changelogs/drizzle-orm/0.34.0.md @@ -0,0 +1,141 @@ +// Libsql and Sqlite migration updates + +SQLite generate statements updates. Starting from this release, we won't generate comments like this: + +```sql + '/*\n SQLite does not support "Changing existing column type" out of the box, we do not generate automatic migration for that, so it has to be done manually' + + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' + + '\n https://www.sqlite.org/lang_altertable.html' + + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' + + "\n\n Due to that we don't generate migration automatically and it has to be done manually" + + '\n*/' +``` + +We will generate a set of statements instead and you will decide if it's good to make data moving statements instead. Here is an example of generated sql file you'll get now: + +```sql +``` + +LibSQL generate statements updates. As long as libsql support more alter statements than SQLite we can generate more statements without recreating you schema and moving all the data, which can be potentially dangeourose for production envs + +LibSQL and Turso will now get a separate dialect in drizzle config file, means, that we will evolve Turso and LibSQL separately from SQLite and will try to support as much features Turso/LibSQL would have as possible + +Breaking change! All the users, who wants to get max from Turso and LibSQL DDL statements, shouls remove `driver: turso` from drizzle.config and add `dialect: turso` instead + +With updated LibSQL migrartion staretegy you would have a possibility to: + +- **Change Data Type**: You can set a new data type for existing columns. +- **Set and Drop Default Values**: You can add or remove default values for existing columns. +- **Set and Drop Not Null Constraint**: You can add or remov the `NOT NULL` constraint on existing columns. +- **Add References to Existing Columns**: You can add foreign key references to existing columns. + + +### Code Example: + +```sql +PRAGMA foreign_keys=OFF; + +CREATE TABLE `__new_table` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `name` text NOT NULL +); + +INSERT INTO `__new_table`("id", "name") SELECT "id", "name" FROM `table`; + +DROP TABLE `table`; + +ALTER TABLE `__new_table` RENAME TO `table`; + +PRAGMA foreign_keys=ON; +``` + +### LIMITATIONS + +- Dropping or altering index will cause table recreation + +This is because LibSQL does not support dropping this type of index: + +```sql +CREATE TABLE `users` ( + `id` integer NOT NULL, + `name` integer, + `age` integer PRIMARY KEY NOT NULL + FOREIGN KEY (`name`) REFERENCES `users1`("id") ON UPDATE no action ON DELETE no action +); +``` + +- If the table has indexes, altering columns will cause table recreation. + Drizzle-Kit will drop those indexes, modify the columns, and then recreate the indexes. +- Adding or dropping `composite foreign keys` is not supported and will cause table recreation. + +### NOTES: + +- You can create reference on any column type, but if you want to insert values than primary column should have unique index or primary key + +```sql +CREATE TABLE parent(a PRIMARY KEY, b UNIQUE, c, d, e, f); +CREATE UNIQUE INDEX i1 ON parent(c, d); +CREATE INDEX i2 ON parent(e); +CREATE UNIQUE INDEX i3 ON parent(f COLLATE nocase); + +CREATE TABLE child1(f, g REFERENCES parent(a)); -- Ok +CREATE TABLE child2(h, i REFERENCES parent(b)); -- Ok +CREATE TABLE child3(j, k, FOREIGN KEY(j, k) REFERENCES parent(c, d)); -- Ok +CREATE TABLE child4(l, m REFERENCES parent(e)); -- Error! +CREATE TABLE child5(n, o REFERENCES parent(f)); -- Error! +CREATE TABLE child6(p, q, FOREIGN KEY(p, q) REFERENCES parent(b, c)); -- Error! +CREATE TABLE child7(r REFERENCES parent(c)); -- Error! +``` + +> NOTE: The foreign key for table *child5* is an error because even though the parent key column has a unique index, the index uses a different collating sequence + +See more: https://www.sqlite.org/foreignkeys.html + +// monodriver + +before on example with node-postgres + +```ts +const client = new Pool({ url: '' }); +drizzle(client, { logger: true }); +``` + +will become + +```ts +await drizzle('', { client: '', logger: true }) +await drizzle('', { client: {}, logger: true }) +``` + +> Note that first example with client is still available and not deprecated. You ca use it, if you don't want to await drizzle object. New way of defining drizzle is done to make it easiser to import from 1 place and get an autocmplete with all the possible clients + +// db count feature + +to count entities in table you would need to do this +```ts +const res = await db.select({ count: sql`count(*)` }).from(users); +const count = res[0].count; +``` + +a new api will look like +```ts +// how many users are in the database +const count: number = await db.$count(users); + +// how many users with the name "Dan" are in the database +const count: number = await db.$count(users, eq(name, "Dan")); +``` + +This can also work as a subquery and inside relational queries +```ts +const users = await db.select({ + ...users, + postsCount: db.$count(posts, eq(posts.authorId, users.id)) +}); + +const users = await db.query.users.findMany({ + extras: { + postsCount: db.$count(posts, eq(posts.authorId, users.id)) + } +}) +``` diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index 75c3d65dc..66f19e6be 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -1,6 +1,6 @@ { "name": "drizzle-kit", - "version": "0.24.2", + "version": "0.25.0", "homepage": "https://orm.drizzle.team", "keywords": [ "drizzle", diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json index 11970a77e..333521a48 100644 --- a/drizzle-orm/package.json +++ b/drizzle-orm/package.json @@ -1,6 +1,6 @@ { "name": "drizzle-orm", - "version": "0.33.0", + "version": "0.34.0", "description": "Drizzle ORM package for SQL databases", "type": "module", "scripts": { diff --git a/integration-tests/tests/pg/awsdatapi.test.ts b/integration-tests/tests/pg/awsdatapi.test.ts index 8ee39cf12..3bb884c0c 100644 --- a/integration-tests/tests/pg/awsdatapi.test.ts +++ b/integration-tests/tests/pg/awsdatapi.test.ts @@ -799,19 +799,18 @@ test('migrator : default migration strategy', async () => { }); test('migrator : migrate with custom schema', async () => { - const customSchema = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); await migrate(db, { migrationsFolder: './drizzle2/pg', - migrationsSchema: customSchema, + migrationsSchema: 'custom_migrations', }); // test if the custom migrations table was created const { rows } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`, + sql`select * from custom_migrations."__drizzle_migrations";`, ); expect(rows).toBeTruthy(); expect(rows!.length).toBeGreaterThan(0); @@ -824,7 +823,7 @@ test('migrator : migrate with custom schema', async () => { await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); await db.execute( - sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`, + sql`drop table custom_migrations."__drizzle_migrations"`, ); }); @@ -858,7 +857,6 @@ test('migrator : migrate with custom table', async () => { test('migrator : migrate with custom table and custom schema', async () => { const customTable = randomString(); - const customSchema = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); @@ -866,12 +864,12 @@ test('migrator : migrate with custom table and custom schema', async () => { await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable, - migrationsSchema: customSchema, + migrationsSchema: 'custom_migrations', }); // test if the custom migrations table was created const { rows } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${ + sql`select * from custom_migrations.${ sql.identifier( customTable, ) @@ -888,7 +886,7 @@ test('migrator : migrate with custom table and custom schema', async () => { await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); await db.execute( - sql`drop table ${sql.identifier(customSchema)}.${ + sql`drop table custom_migrations.${ sql.identifier( customTable, ) diff --git a/integration-tests/tests/pg/neon-http.test.ts b/integration-tests/tests/pg/neon-http.test.ts index 1476e9628..319c84f40 100644 --- a/integration-tests/tests/pg/neon-http.test.ts +++ b/integration-tests/tests/pg/neon-http.test.ts @@ -68,15 +68,14 @@ test('migrator : default migration strategy', async () => { }); test('migrator : migrate with custom schema', async () => { - const customSchema = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); + await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: 'custom_migrations' }); // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); + const { rowCount } = await db.execute(sql`select * from custom_migrations."__drizzle_migrations";`); expect(rowCount && rowCount > 0).toBeTruthy(); // test if the migrated table are working as expected @@ -86,7 +85,7 @@ test('migrator : migrate with custom schema', async () => { await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); + await db.execute(sql`drop table custom_migrations."__drizzle_migrations"`); }); test('migrator : migrate with custom table', async () => { @@ -113,7 +112,6 @@ test('migrator : migrate with custom table', async () => { test('migrator : migrate with custom table and custom schema', async () => { const customTable = randomString(); - const customSchema = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); @@ -121,12 +119,12 @@ test('migrator : migrate with custom table and custom schema', async () => { await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable, - migrationsSchema: customSchema, + migrationsSchema: 'custom_migrations', }); // test if the custom migrations table was created const { rowCount } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, + sql`select * from custom_migrations.${sql.identifier(customTable)};`, ); expect(rowCount && rowCount > 0).toBeTruthy(); @@ -137,7 +135,7 @@ test('migrator : migrate with custom table and custom schema', async () => { await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); + await db.execute(sql`drop table custom_migrations.${sql.identifier(customTable)}`); }); test('all date and time columns without timezone first case mode string', async () => { diff --git a/integration-tests/tests/pg/pg-common.ts b/integration-tests/tests/pg/pg-common.ts index b4afdcf64..8550f5ae4 100644 --- a/integration-tests/tests/pg/pg-common.ts +++ b/integration-tests/tests/pg/pg-common.ts @@ -74,7 +74,7 @@ import { } from 'drizzle-orm/pg-core'; import getPort from 'get-port'; import { v4 as uuidV4 } from 'uuid'; -import { afterAll, beforeEach, describe, expect, test } from 'vitest'; +import { afterAll, afterEach, beforeEach, describe, expect, test } from 'vitest'; import { Expect } from '~/utils'; import type { schema } from './neon-http-batch.test'; // eslint-disable-next-line @typescript-eslint/no-import-type-side-effects @@ -246,6 +246,7 @@ export function tests() { await db.execute(sql`drop schema if exists public cascade`); await db.execute(sql`drop schema if exists ${mySchema} cascade`); await db.execute(sql`create schema public`); + await db.execute(sql`create schema if not exists custom_migrations`); await db.execute(sql`create schema ${mySchema}`); // public users await db.execute( @@ -377,6 +378,11 @@ export function tests() { ); }); + afterEach(async (ctx) => { + const { db } = ctx.pg; + await db.execute(sql`drop schema if exists custom_migrations cascade`); + }); + async function setupSetOperationTest(db: PgDatabase) { await db.execute(sql`drop table if exists users2`); await db.execute(sql`drop table if exists cities`); diff --git a/integration-tests/tests/pg/vercel-pg.test.ts b/integration-tests/tests/pg/vercel-pg.test.ts index 3f1248d9b..ecf1d22ac 100644 --- a/integration-tests/tests/pg/vercel-pg.test.ts +++ b/integration-tests/tests/pg/vercel-pg.test.ts @@ -77,15 +77,14 @@ test('migrator : default migration strategy', async () => { }); test('migrator : migrate with custom schema', async () => { - const customSchema = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); + await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: 'custom_migrations' }); // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); + const { rowCount } = await db.execute(sql`select * from custom_migrations."__drizzle_migrations";`); expect(rowCount && rowCount > 0).toBeTruthy(); // test if the migrated table are working as expected @@ -95,7 +94,7 @@ test('migrator : migrate with custom schema', async () => { await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); + await db.execute(sql`drop table custom_migrations."__drizzle_migrations"`); }); test('migrator : migrate with custom table', async () => { @@ -122,7 +121,6 @@ test('migrator : migrate with custom table', async () => { test('migrator : migrate with custom table and custom schema', async () => { const customTable = randomString(); - const customSchema = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); @@ -130,12 +128,12 @@ test('migrator : migrate with custom table and custom schema', async () => { await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable, - migrationsSchema: customSchema, + migrationsSchema: 'custom_migrations', }); // test if the custom migrations table was created const { rowCount } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, + sql`select * from custom_migrations.${sql.identifier(customTable)};`, ); expect(rowCount && rowCount > 0).toBeTruthy(); @@ -146,7 +144,7 @@ test('migrator : migrate with custom table and custom schema', async () => { await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); + await db.execute(sql`drop table custom_migrations.${sql.identifier(customTable)}`); }); test('all date and time columns without timezone first case mode string', async () => { From 6d413dff2d4b4caf82c8407cd691e7724d92f88c Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Mon, 9 Sep 2024 11:17:29 +0300 Subject: [PATCH 51/56] Add release notes for orm and kit --- changelogs/drizzle-kit/0.25.0.md | 95 ++++++++++++++++++++++++++++ changelogs/drizzle-orm/0.34.0.md | 103 +++++++++++++++++-------------- 2 files changed, 153 insertions(+), 45 deletions(-) diff --git a/changelogs/drizzle-kit/0.25.0.md b/changelogs/drizzle-kit/0.25.0.md index e69de29bb..0c1c5ea6e 100644 --- a/changelogs/drizzle-kit/0.25.0.md +++ b/changelogs/drizzle-kit/0.25.0.md @@ -0,0 +1,95 @@ +## Libsql and Sqlite migration updates + +### SQLite "generate" and "push" statements updates + +Starting from this release, we will no longer generate comments like this: + +```sql + '/*\n SQLite does not support "Changing existing column type" out of the box, we do not generate automatic migration for that, so it has to be done manually' + + '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php' + + '\n https://www.sqlite.org/lang_altertable.html' + + '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3' + + "\n\n Due to that we don't generate migration automatically and it has to be done manually" + + '\n*/' +``` + +We will generate a set of statements, and you can decide if it's appropriate to create data-moving statements instead. Here is an example of the SQL file you'll receive now: + +```sql +PRAGMA foreign_keys=OFF; +--> statement-breakpoint +CREATE TABLE `__new_worker` ( + `id` integer PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `salary` text NOT NULL, + `job_id` integer, + FOREIGN KEY (`job_id`) REFERENCES `job`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +INSERT INTO `__new_worker`("id", "name", "salary", "job_id") SELECT "id", "name", "salary", "job_id" FROM `worker`; +--> statement-breakpoint +DROP TABLE `worker`; +--> statement-breakpoint +ALTER TABLE `__new_worker` RENAME TO `worker`; +--> statement-breakpoint +PRAGMA foreign_keys=ON; +``` + +### LibSQL "generate" and "push" statements updates + +Since LibSQL supports more ALTER statements than SQLite, we can generate more statements without recreating your schema and moving all the data, which can be potentially dangerous for production environments. + +LibSQL and Turso will now have a separate dialect in the Drizzle config file, meaning that we will evolve Turso and LibSQL independently from SQLite and will aim to support as many features as Turso/LibSQL offer. + +> **Breaking change!** All users who want to get the most out of Turso and LibSQL DDL statements should remove `driver: turso` from the `drizzle.config` file and add `dialect: turso` instead + +With the updated LibSQL migration strategy, you will have the ability to: + +- **Change Data Type**: Set a new data type for existing columns. +- **Set and Drop Default Values**: Add or remove default values for existing columns. +- **Set and Drop NOT NULL**: Add or remove the NOT NULL constraint on existing columns. +- **Add References to Existing Columns**: Add foreign key references to existing columns + +You can find more information in the [LibSQL documentation](https://github.com/tursodatabase/libsql/blob/main/libsql-sqlite3/doc/libsql_extensions.md#altering-columns) + +### LIMITATIONS + +- Dropping or altering an index will cause table recreation. + +This is because LibSQL does not support dropping this type of index. + +```sql +CREATE TABLE `users` ( + `id` integer NOT NULL, + `name` integer, + `age` integer PRIMARY KEY NOT NULL + FOREIGN KEY (`name`) REFERENCES `users1`("id") ON UPDATE no action ON DELETE no action +); +``` + +- If the table has indexes, altering columns will cause table recreation. +- Drizzle-Kit will drop the indexes, modify the columns, and then recreate the indexes. +- Adding or dropping composite foreign keys is not supported and will cause table recreation + +### NOTES: + +- You can create a reference on any column type, but if you want to insert values, the referenced column must have a unique index or primary key. + +```sql +CREATE TABLE parent(a PRIMARY KEY, b UNIQUE, c, d, e, f); +CREATE UNIQUE INDEX i1 ON parent(c, d); +CREATE INDEX i2 ON parent(e); +CREATE UNIQUE INDEX i3 ON parent(f COLLATE nocase); + +CREATE TABLE child1(f, g REFERENCES parent(a)); -- Ok +CREATE TABLE child2(h, i REFERENCES parent(b)); -- Ok +CREATE TABLE child3(j, k, FOREIGN KEY(j, k) REFERENCES parent(c, d)); -- Ok +CREATE TABLE child4(l, m REFERENCES parent(e)); -- Error! +CREATE TABLE child5(n, o REFERENCES parent(f)); -- Error! +CREATE TABLE child6(p, q, FOREIGN KEY(p, q) REFERENCES parent(b, c)); -- Error! +CREATE TABLE child7(r REFERENCES parent(c)); -- Error! +``` + +> **NOTE**: The foreign key for the table child5 is an error because, although the parent key column has a unique index, the index uses a different collating sequence. + +See more: https://www.sqlite.org/foreignkeys.html \ No newline at end of file diff --git a/changelogs/drizzle-orm/0.34.0.md b/changelogs/drizzle-orm/0.34.0.md index 4ceb1a630..55eb0411f 100644 --- a/changelogs/drizzle-orm/0.34.0.md +++ b/changelogs/drizzle-orm/0.34.0.md @@ -1,6 +1,8 @@ -// Libsql and Sqlite migration updates +## Libsql and Sqlite migration updates -SQLite generate statements updates. Starting from this release, we won't generate comments like this: +### SQLite "generate" and "push" statements updates + +Starting from this release, we will no longer generate comments like this: ```sql '/*\n SQLite does not support "Changing existing column type" out of the box, we do not generate automatic migration for that, so it has to be done manually' @@ -11,66 +13,67 @@ SQLite generate statements updates. Starting from this release, we won't generat + '\n*/' ``` -We will generate a set of statements instead and you will decide if it's good to make data moving statements instead. Here is an example of generated sql file you'll get now: +We will generate a set of statements, and you can decide if it's appropriate to create data-moving statements instead. Here is an example of the SQL file you'll receive now: ```sql +PRAGMA foreign_keys=OFF; +--> statement-breakpoint +CREATE TABLE `__new_worker` ( + `id` integer PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `salary` text NOT NULL, + `job_id` integer, + FOREIGN KEY (`job_id`) REFERENCES `job`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +INSERT INTO `__new_worker`("id", "name", "salary", "job_id") SELECT "id", "name", "salary", "job_id" FROM `worker`; +--> statement-breakpoint +DROP TABLE `worker`; +--> statement-breakpoint +ALTER TABLE `__new_worker` RENAME TO `worker`; +--> statement-breakpoint +PRAGMA foreign_keys=ON; ``` -LibSQL generate statements updates. As long as libsql support more alter statements than SQLite we can generate more statements without recreating you schema and moving all the data, which can be potentially dangeourose for production envs - -LibSQL and Turso will now get a separate dialect in drizzle config file, means, that we will evolve Turso and LibSQL separately from SQLite and will try to support as much features Turso/LibSQL would have as possible - -Breaking change! All the users, who wants to get max from Turso and LibSQL DDL statements, shouls remove `driver: turso` from drizzle.config and add `dialect: turso` instead - -With updated LibSQL migrartion staretegy you would have a possibility to: - -- **Change Data Type**: You can set a new data type for existing columns. -- **Set and Drop Default Values**: You can add or remove default values for existing columns. -- **Set and Drop Not Null Constraint**: You can add or remov the `NOT NULL` constraint on existing columns. -- **Add References to Existing Columns**: You can add foreign key references to existing columns. - +### LibSQL "generate" and "push" statements updates -### Code Example: +Since LibSQL supports more ALTER statements than SQLite, we can generate more statements without recreating your schema and moving all the data, which can be potentially dangerous for production environments. -```sql -PRAGMA foreign_keys=OFF; - -CREATE TABLE `__new_table` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text NOT NULL -); +LibSQL and Turso will now have a separate dialect in the Drizzle config file, meaning that we will evolve Turso and LibSQL independently from SQLite and will aim to support as many features as Turso/LibSQL offer. -INSERT INTO `__new_table`("id", "name") SELECT "id", "name" FROM `table`; +> **Breaking change!** All users who want to get the most out of Turso and LibSQL DDL statements should remove `driver: turso` from the `drizzle.config` file and add `dialect: turso` instead -DROP TABLE `table`; +With the updated LibSQL migration strategy, you will have the ability to: -ALTER TABLE `__new_table` RENAME TO `table`; +- **Change Data Type**: Set a new data type for existing columns. +- **Set and Drop Default Values**: Add or remove default values for existing columns. +- **Set and Drop NOT NULL**: Add or remove the NOT NULL constraint on existing columns. +- **Add References to Existing Columns**: Add foreign key references to existing columns -PRAGMA foreign_keys=ON; -``` +You can find more information in the [LibSQL documentation](https://github.com/tursodatabase/libsql/blob/main/libsql-sqlite3/doc/libsql_extensions.md#altering-columns) ### LIMITATIONS -- Dropping or altering index will cause table recreation +- Dropping or altering an index will cause table recreation. -This is because LibSQL does not support dropping this type of index: +This is because LibSQL does not support dropping this type of index. ```sql CREATE TABLE `users` ( - `id` integer NOT NULL, - `name` integer, - `age` integer PRIMARY KEY NOT NULL - FOREIGN KEY (`name`) REFERENCES `users1`("id") ON UPDATE no action ON DELETE no action + `id` integer NOT NULL, + `name` integer, + `age` integer PRIMARY KEY NOT NULL + FOREIGN KEY (`name`) REFERENCES `users1`("id") ON UPDATE no action ON DELETE no action ); ``` - If the table has indexes, altering columns will cause table recreation. - Drizzle-Kit will drop those indexes, modify the columns, and then recreate the indexes. -- Adding or dropping `composite foreign keys` is not supported and will cause table recreation. +- Drizzle-Kit will drop the indexes, modify the columns, and then recreate the indexes. +- Adding or dropping composite foreign keys is not supported and will cause table recreation ### NOTES: -- You can create reference on any column type, but if you want to insert values than primary column should have unique index or primary key +- You can create a reference on any column type, but if you want to insert values, the referenced column must have a unique index or primary key. ```sql CREATE TABLE parent(a PRIMARY KEY, b UNIQUE, c, d, e, f); @@ -87,17 +90,27 @@ CREATE TABLE child6(p, q, FOREIGN KEY(p, q) REFERENCES parent(b, c)); -- Error! CREATE TABLE child7(r REFERENCES parent(c)); -- Error! ``` -> NOTE: The foreign key for table *child5* is an error because even though the parent key column has a unique index, the index uses a different collating sequence +> **NOTE**: The foreign key for the table child5 is an error because, although the parent key column has a unique index, the index uses a different collating sequence. See more: https://www.sqlite.org/foreignkeys.html -// monodriver +## A new and easy way to start using drizzle before on example with node-postgres ```ts +// 1, current const client = new Pool({ url: '' }); drizzle(client, { logger: true }); + +// 2 +await drizzle('neon', { client: 'postgresql://...', logger: true }) + +// 3 +await drizzle('neon', { client: { url: 'postgresql://...' }, logger: true }) + +// 4 +await drizzle('neon', 'postgresql://...') ``` will become @@ -107,17 +120,17 @@ await drizzle('', { client: '', logger: true }) await drizzle('', { client: {}, logger: true }) ``` -> Note that first example with client is still available and not deprecated. You ca use it, if you don't want to await drizzle object. New way of defining drizzle is done to make it easiser to import from 1 place and get an autocmplete with all the possible clients +> Note that the first example with the client is still available and not deprecated. You can use it if you don't want to await the drizzle object. The new way of defining drizzle is designed to make it easier to import from one place and get autocomplete for all the available clients -// db count feature +## New "count" API -to count entities in table you would need to do this +Befor this release to count entities in a table, you would need to do this: ```ts const res = await db.select({ count: sql`count(*)` }).from(users); const count = res[0].count; ``` -a new api will look like +The new API will look like this: ```ts // how many users are in the database const count: number = await db.$count(users); @@ -126,7 +139,7 @@ const count: number = await db.$count(users); const count: number = await db.$count(users, eq(name, "Dan")); ``` -This can also work as a subquery and inside relational queries +This can also work as a subquery and within relational queries ```ts const users = await db.select({ ...users, From d5a0238754a47542e5b642173b57b7e851bcc1d8 Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Wed, 11 Sep 2024 12:03:26 +0300 Subject: [PATCH 52/56] Update release notes --- changelogs/drizzle-kit/0.25.0.md | 61 ++++++++++++-- changelogs/drizzle-orm/0.34.0.md | 135 +++++++++++++++++++++++++++---- 2 files changed, 173 insertions(+), 23 deletions(-) diff --git a/changelogs/drizzle-kit/0.25.0.md b/changelogs/drizzle-kit/0.25.0.md index 0c1c5ea6e..fc4b36c83 100644 --- a/changelogs/drizzle-kit/0.25.0.md +++ b/changelogs/drizzle-kit/0.25.0.md @@ -1,4 +1,55 @@ -## Libsql and Sqlite migration updates +## Breaking changes and migrate guide for Turso users + +If you are using Turso and libsql, you will need to upgrade your `drizzle.config` and `@libsql/client` package. + +1. This version of drizzle-orm will only work with `@libsql/client@0.10.0` or higher if you are using the `migrate` function. For other use cases, you can continue using previous versions(But the suggestion is to upgrade) +To install the latest version, use the command: + +```bash +npm i @libsql/client@latest +``` + +2. Previously, we had a common `drizzle.config` for SQLite and Turso users, which allowed a shared strategy for both dialects. Starting with this release, we are introducing the turso dialect in drizzle-kit. We will evolve and improve Turso as a separate dialect with its own migration strategies. + +**Before** + +```ts +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "sqlite", + schema: "./schema.ts", + out: "./drizzle", + dbCredentials: { + url: "database.db", + }, + breakpoints: true, + verbose: true, + strict: true, +}); +``` + +**After** + +```ts +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "turso", + schema: "./schema.ts", + out: "./drizzle", + dbCredentials: { + url: "database.db", + }, + breakpoints: true, + verbose: true, + strict: true, +}); +``` + +If you are using only SQLite, you can use `dialect: "sqlite"` + +## LibSQL/Turso and Sqlite migration updates ### SQLite "generate" and "push" statements updates @@ -35,14 +86,12 @@ ALTER TABLE `__new_worker` RENAME TO `worker`; PRAGMA foreign_keys=ON; ``` -### LibSQL "generate" and "push" statements updates +### LibSQL/Turso "generate" and "push" statements updates Since LibSQL supports more ALTER statements than SQLite, we can generate more statements without recreating your schema and moving all the data, which can be potentially dangerous for production environments. LibSQL and Turso will now have a separate dialect in the Drizzle config file, meaning that we will evolve Turso and LibSQL independently from SQLite and will aim to support as many features as Turso/LibSQL offer. -> **Breaking change!** All users who want to get the most out of Turso and LibSQL DDL statements should remove `driver: turso` from the `drizzle.config` file and add `dialect: turso` instead - With the updated LibSQL migration strategy, you will have the ability to: - **Change Data Type**: Set a new data type for existing columns. @@ -56,7 +105,7 @@ You can find more information in the [LibSQL documentation](https://github.com/t - Dropping or altering an index will cause table recreation. -This is because LibSQL does not support dropping this type of index. +This is because LibSQL/Turso does not support dropping this type of index. ```sql CREATE TABLE `users` ( @@ -71,7 +120,7 @@ CREATE TABLE `users` ( - Drizzle-Kit will drop the indexes, modify the columns, and then recreate the indexes. - Adding or dropping composite foreign keys is not supported and will cause table recreation -### NOTES: +### NOTES - You can create a reference on any column type, but if you want to insert values, the referenced column must have a unique index or primary key. diff --git a/changelogs/drizzle-orm/0.34.0.md b/changelogs/drizzle-orm/0.34.0.md index 55eb0411f..490422628 100644 --- a/changelogs/drizzle-orm/0.34.0.md +++ b/changelogs/drizzle-orm/0.34.0.md @@ -1,4 +1,55 @@ -## Libsql and Sqlite migration updates +## Breaking changes and migrate guide for Turso users + +If you are using Turso and libsql, you will need to upgrade your `drizzle.config` and `@libsql/client` package. + +1. This version of drizzle-orm will only work with `@libsql/client@0.10.0` or higher if you are using the `migrate` function. For other use cases, you can continue using previous versions(But the suggestion is to upgrade) +To install the latest version, use the command: + +```bash +npm i @libsql/client@latest +``` + +2. Previously, we had a common `drizzle.config` for SQLite and Turso users, which allowed a shared strategy for both dialects. Starting with this release, we are introducing the turso dialect in drizzle-kit. We will evolve and improve Turso as a separate dialect with its own migration strategies. + +**Before** + +```ts +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "sqlite", + schema: "./schema.ts", + out: "./drizzle", + dbCredentials: { + url: "database.db", + }, + breakpoints: true, + verbose: true, + strict: true, +}); +``` + +**After** + +```ts +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "turso", + schema: "./schema.ts", + out: "./drizzle", + dbCredentials: { + url: "database.db", + }, + breakpoints: true, + verbose: true, + strict: true, +}); +``` + +If you are using only SQLite, you can use `dialect: "sqlite"` + +## LibSQL/Turso and Sqlite migration updates ### SQLite "generate" and "push" statements updates @@ -35,14 +86,12 @@ ALTER TABLE `__new_worker` RENAME TO `worker`; PRAGMA foreign_keys=ON; ``` -### LibSQL "generate" and "push" statements updates +### LibSQL/Turso "generate" and "push" statements updates Since LibSQL supports more ALTER statements than SQLite, we can generate more statements without recreating your schema and moving all the data, which can be potentially dangerous for production environments. LibSQL and Turso will now have a separate dialect in the Drizzle config file, meaning that we will evolve Turso and LibSQL independently from SQLite and will aim to support as many features as Turso/LibSQL offer. -> **Breaking change!** All users who want to get the most out of Turso and LibSQL DDL statements should remove `driver: turso` from the `drizzle.config` file and add `dialect: turso` instead - With the updated LibSQL migration strategy, you will have the ability to: - **Change Data Type**: Set a new data type for existing columns. @@ -56,7 +105,7 @@ You can find more information in the [LibSQL documentation](https://github.com/t - Dropping or altering an index will cause table recreation. -This is because LibSQL does not support dropping this type of index. +This is because LibSQL/Turso does not support dropping this type of index. ```sql CREATE TABLE `users` ( @@ -71,7 +120,7 @@ CREATE TABLE `users` ( - Drizzle-Kit will drop the indexes, modify the columns, and then recreate the indexes. - Adding or dropping composite foreign keys is not supported and will cause table recreation -### NOTES: +### NOTES - You can create a reference on any column type, but if you want to insert values, the referenced column must have a unique index or primary key. @@ -96,28 +145,77 @@ See more: https://www.sqlite.org/foreignkeys.html ## A new and easy way to start using drizzle -before on example with node-postgres +Current and the only way to do, is to define client yourself and pass it to drizzle ```ts -// 1, current const client = new Pool({ url: '' }); drizzle(client, { logger: true }); +``` + +But we want to introduce you to a new API, which is a simplified method in addition to the existing one. + +Most clients will have a few options to connect, starting with the easiest and most common one, and allowing you to control your client connection as needed. -// 2 -await drizzle('neon', { client: 'postgresql://...', logger: true }) +Let's use `node-postgres` as an example, but the same pattern can be applied to all other clients -// 3 -await drizzle('neon', { client: { url: 'postgresql://...' }, logger: true }) +```ts +// Finally, one import for all available clients and dialects! +import { drizzle } from 'drizzle-orm' + +// Choose a client and use a connection URL — nothing else is needed! +const db1 = await drizzle("node-postgres", process.env.POSTGRES_URL); + +// If you need to pass a logger, schema, or other configurations, you can use an object and specify the client-specific URL in the connection +const db2 = await drizzle("node-postgres", { + connection: process.env.POSTGRES_URL, + logger: true +}); + +// And finally, if you need to use full client/driver-specific types in connections, you can use a URL or host/port/etc. as an object inferred from the underlying client connection types +const db3 = await drizzle("node-postgres", { + connection: { + connectionString: process.env.POSTGRES_URL, + }, +}); -// 4 -await drizzle('neon', 'postgresql://...') +const db4 = await drizzle("node-postgres", { + connection: { + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + database: process.env.DB_NAME, + ssl: true, + }, +}); ``` -will become +A few clients will have a slightly different API due to their specific behavior. Let's take a look at them: + +For `aws-data-api-pg`, Drizzle will require `resourceArn`, `database`, and `secretArn`, along with any other AWS Data API client types for the connection, such as credentials, region, etc. ```ts -await drizzle('', { client: '', logger: true }) -await drizzle('', { client: {}, logger: true }) +drizzle("aws-data-api-pg", { + connection: { + resourceArn: "", + database: "", + secretArn: "", + }, +}); +``` + +For `d1`, the Cloudflare Worker types as described in the [documentation](https://developers.cloudflare.com/d1/get-started/) here will be required. + +```ts +drizzle("d1", { + connection: env.DB // Cloudflare Worker Types +}) +``` + +For `vercel-postgres`, nothing is needed since Vercel automatically retrieves the `POSTGRES_URL` from the `.env` file. You can check this [documentation](https://vercel.com/docs/storage/vercel-postgres/quickstart) for more info + +```ts +drizzle("vercel-postgres") ``` > Note that the first example with the client is still available and not deprecated. You can use it if you don't want to await the drizzle object. The new way of defining drizzle is designed to make it easier to import from one place and get autocomplete for all the available clients @@ -125,12 +223,14 @@ await drizzle('', { client: {}, logger: true }) ## New "count" API Befor this release to count entities in a table, you would need to do this: + ```ts const res = await db.select({ count: sql`count(*)` }).from(users); const count = res[0].count; ``` The new API will look like this: + ```ts // how many users are in the database const count: number = await db.$count(users); @@ -140,6 +240,7 @@ const count: number = await db.$count(users, eq(name, "Dan")); ``` This can also work as a subquery and within relational queries + ```ts const users = await db.select({ ...users, From c38333a6a9f97923c04f83557d52690f8256df25 Mon Sep 17 00:00:00 2001 From: prodrigues Date: Thu, 8 Aug 2024 13:21:35 +0100 Subject: [PATCH 53/56] add singlestore dialect to drizzle-kit --- drizzle-kit/src/cli/commands/migrate.ts | 52 ++++++++++++ drizzle-kit/src/cli/commands/utils.ts | 7 ++ drizzle-kit/src/cli/connections.ts | 80 +++++++++++++++++++ drizzle-kit/src/introspect-singlestore.ts | 11 +-- drizzle-kit/src/jsonStatements.ts | 1 + drizzle-kit/src/migrationPreparator.ts | 1 + drizzle-kit/src/schemaValidator.ts | 1 + .../src/serializer/singlestoreSerializer.ts | 25 +++--- drizzle-kit/src/serializer/studio.ts | 10 +-- pnpm-lock.yaml | 25 +----- 10 files changed, 166 insertions(+), 47 deletions(-) diff --git a/drizzle-kit/src/cli/commands/migrate.ts b/drizzle-kit/src/cli/commands/migrate.ts index de1d8bc45..6fd9120ae 100644 --- a/drizzle-kit/src/cli/commands/migrate.ts +++ b/drizzle-kit/src/cli/commands/migrate.ts @@ -396,6 +396,58 @@ export const prepareAndMigrateMysql = async (config: GenerateConfig) => { } }; +// Not needed for now +function mySingleStoreSchemaSuggestions( + curSchema: TypeOf, + prevSchema: TypeOf, +) { + const suggestions: string[] = []; + const usedSuggestions: string[] = []; + const suggestionTypes = { + // TODO: Check if SingleStore has serial type + serial: withStyle.errorWarning( + `We deprecated the use of 'serial' for SingleStore starting from version 0.20.0. In SingleStore, 'serial' is simply an alias for 'bigint unsigned not null auto_increment unique,' which creates all constraints and indexes for you. This may make the process less explicit for both users and drizzle-kit push commands`, + ), + }; + + for (const table of Object.values(curSchema.tables)) { + for (const column of Object.values(table.columns)) { + if (column.type === 'serial') { + if (!usedSuggestions.includes('serial')) { + suggestions.push(suggestionTypes['serial']); + } + + const uniqueForSerial = Object.values( + prevSchema.tables[table.name].uniqueConstraints, + ).find((it) => it.columns[0] === column.name); + + suggestions.push( + `\n` + + withStyle.suggestion( + `We are suggesting to change ${ + chalk.blue( + column.name, + ) + } column in ${ + chalk.blueBright( + table.name, + ) + } table from serial to bigint unsigned\n\n${ + chalk.blueBright( + `bigint("${column.name}", { mode: "number", unsigned: true }).notNull().autoincrement().unique(${ + uniqueForSerial?.name ? `"${uniqueForSerial?.name}"` : '' + })`, + ) + }`, + ), + ); + } + } + } + + return suggestions; +} + // Intersect with prepareAnMigrate export const prepareSingleStorePush = async ( schemaPath: string | string[], diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index 5e5681b2c..f01ee5e0b 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -29,6 +29,7 @@ import { postgresCredentials, printConfigConnectionIssues as printIssuesPg, } from '../validations/postgres'; +import { printConfigConnectionIssues as printIssuesSingleStore, singlestoreCredentials, SingleStoreCredentials } from '../validations/singlestore'; import { printConfigConnectionIssues as printIssuesSingleStore, SingleStoreCredentials, @@ -221,6 +222,9 @@ export const preparePushConfig = async ( | { dialect: 'turso'; credentials: LibSQLCredentials; + } | { + dialect: 'singlestore'; + credentials: SingleStoreCredentials; } | { dialect: 'singlestore'; @@ -406,6 +410,9 @@ export const preparePullConfig = async ( | { dialect: 'turso'; credentials: LibSQLCredentials; + } | { + dialect: 'singlestore'; + credentials: SingleStoreCredentials; } ) & { out: string; diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index fccc7def8..41bcebeb7 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -21,6 +21,7 @@ import { withStyle } from './validations/outputs'; import type { PostgresCredentials } from './validations/postgres'; import { SingleStoreCredentials } from './validations/singlestore'; import type { SqliteCredentials } from './validations/sqlite'; +import { SingleStoreCredentials } from './validations/singlestore'; export const preparePostgresDB = async ( credentials: PostgresCredentials, @@ -731,6 +732,85 @@ export const connectToLibSQL = async (credentials: LibSQLCredentials): Promise< "Please install '@libsql/client' for Drizzle Kit to connect to LibSQL databases", ); process.exit(1); +} + +const parseSingleStoreCredentials = (credentials: SingleStoreCredentials) => { + if ('url' in credentials) { + const url = credentials.url; + + const connectionUrl = new URL(url); + const pathname = connectionUrl.pathname; + + const database = pathname.split('/')[pathname.split('/').length - 1]; + if (!database) { + console.error( + 'You should specify a database name in connection string (singlestore://USER:PASSWORD@HOST:PORT/DATABASE)', + ); + process.exit(1); + } + return { database, url }; + } else { + return { + database: credentials.database, + credentials, + }; + } +}; + +export const connectToSingleStore = async ( + it: SingleStoreCredentials, +): Promise<{ + db: DB; + proxy: Proxy; + database: string; + migrate: (config: MigrationConfig) => Promise; +}> => { + const result = parseSingleStoreCredentials(it); + + if (await checkPackage('singlestore')) { + const { createConnection } = await import('mysql2/promise'); + const { drizzle } = await import('drizzle-orm/singlestore'); + const { migrate } = await import('drizzle-orm/singlestore/migrator'); + + const connection = result.url + ? await createConnection(result.url) + : await createConnection(result.credentials!); // needed for some reason! + + const db = drizzle(connection); + const migrateFn = async (config: MigrationConfig) => { + return migrate(db, config); + }; + + await connection.connect(); + const query: DB['query'] = async ( + sql: string, + params?: any[], + ): Promise => { + const res = await connection.execute(sql, params); + return res[0] as any; + }; + + const proxy: Proxy = async (params: ProxyParams) => { + const result = await connection.query({ + sql: params.sql, + values: params.params, + rowsAsArray: params.mode === 'array', + }); + return result[0] as any[]; + }; + + return { + db: { query }, + proxy, + database: result.database, + migrate: migrateFn, + }; + } + + console.error( + "To connect to SingleStore database - please install 'mysql2' drivers", + ); + process.exit(1); }; const parseSingleStoreCredentials = (credentials: SingleStoreCredentials) => { diff --git a/drizzle-kit/src/introspect-singlestore.ts b/drizzle-kit/src/introspect-singlestore.ts index 8aa6e3dd7..b9efc537a 100644 --- a/drizzle-kit/src/introspect-singlestore.ts +++ b/drizzle-kit/src/introspect-singlestore.ts @@ -386,15 +386,16 @@ const column = ( ? `${casing(name)}: timestamp("${name}", ${params})` : `${casing(name)}: timestamp("${name}")`; - // TODO: check if SingleStore has defaultNow() or now() - defaultValue = defaultValue === 'now()' || defaultValue === '(CURRENT_TIMESTAMP)' + + // TODO: check if SingleStore has defaultNow() or now() + defaultValue = defaultValue === 'now()' || defaultValue === '(CURRENT_TIMESTAMP)' ? '.defaultNow()' : defaultValue ? `.default(${mapColumnDefault(defaultValue, isExpression)})` : ''; - - out += defaultValue; - + + out += defaultValue; + // TODO: check if SingleStore has onUpdateNow() let onUpdateNow = onUpdate ? '.onUpdateNow()' : ''; out += onUpdateNow; diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index c4c51d4b4..cd3179657 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -3,6 +3,7 @@ import { getNewTableName } from './cli/commands/sqlitePushUtils'; import { warning } from './cli/views'; import { CommonSquashedSchema } from './schemaValidator'; import { MySqlKitInternals, MySqlSchema, MySqlSquasher } from './serializer/mysqlSchema'; +import { SingleStoreKitInternals, SingleStoreSchema, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { Index, PgSchema, PgSquasher } from './serializer/pgSchema'; import { SingleStoreKitInternals, SingleStoreSchema, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { diff --git a/drizzle-kit/src/migrationPreparator.ts b/drizzle-kit/src/migrationPreparator.ts index 4e5664290..3d1ddbea3 100644 --- a/drizzle-kit/src/migrationPreparator.ts +++ b/drizzle-kit/src/migrationPreparator.ts @@ -5,6 +5,7 @@ import { dryMySql, MySqlSchema, mysqlSchema } from './serializer/mysqlSchema'; import { dryPg, PgSchema, pgSchema, PgSchemaInternal } from './serializer/pgSchema'; import { drySingleStore, SingleStoreSchema, singlestoreSchema } from './serializer/singlestoreSchema'; import { drySQLite, SQLiteSchema, sqliteSchema } from './serializer/sqliteSchema'; +import { drySingleStore, singlestoreSchema, SingleStoreSchema } from './serializer/singlestoreSchema'; export const prepareMySqlDbPushSnapshot = async ( prev: MySqlSchema, diff --git a/drizzle-kit/src/schemaValidator.ts b/drizzle-kit/src/schemaValidator.ts index e91b5ab11..4065e948f 100644 --- a/drizzle-kit/src/schemaValidator.ts +++ b/drizzle-kit/src/schemaValidator.ts @@ -3,6 +3,7 @@ import { mysqlSchema, mysqlSchemaSquashed } from './serializer/mysqlSchema'; import { pgSchema, pgSchemaSquashed } from './serializer/pgSchema'; import { singlestoreSchema, singlestoreSchemaSquashed } from './serializer/singlestoreSchema'; import { sqliteSchema, SQLiteSchemaSquashed } from './serializer/sqliteSchema'; +import { singlestoreSchema, singlestoreSchemaSquashed } from './serializer/singlestoreSchema'; export const dialects = ['postgresql', 'mysql', 'sqlite', 'turso', 'singlestore'] as const; export const dialect = enumType(dialects); diff --git a/drizzle-kit/src/serializer/singlestoreSerializer.ts b/drizzle-kit/src/serializer/singlestoreSerializer.ts index f275273f4..3f36f9c21 100644 --- a/drizzle-kit/src/serializer/singlestoreSerializer.ts +++ b/drizzle-kit/src/serializer/singlestoreSerializer.ts @@ -1,19 +1,18 @@ import chalk from 'chalk'; -import { getTableName, is } from 'drizzle-orm'; -import { SQL } from 'drizzle-orm'; +import { is, SQL } from 'drizzle-orm'; import { AnySingleStoreTable, + getTableConfig, type PrimaryKey as PrimaryKeyORM, SingleStoreDialect, uniqueKeyName, } from 'drizzle-orm/singlestore-core'; -import { getTableConfig } from 'drizzle-orm/singlestore-core'; import { RowDataPacket } from 'mysql2/promise'; import { withStyle } from '../cli/validations/outputs'; import { IntrospectStage, IntrospectStatus } from '../cli/views'; -import type { DB } from '../utils'; import { sqlToStr } from '.'; +import type { DB } from '../utils'; import { Column, Index, @@ -574,15 +573,15 @@ export const fromDatabase = async ( }; } } else { - if (typeof tableInResult.indexes[constraintName] !== 'undefined') { - tableInResult.indexes[constraintName]!.columns.push(columnName); - } else { - tableInResult.indexes[constraintName] = { - name: constraintName, - columns: [columnName], - isUnique: isUnique, - }; - } + if (typeof tableInResult.indexes[constraintName] !== 'undefined') { + tableInResult.indexes[constraintName]!.columns.push(columnName); + } else { + tableInResult.indexes[constraintName] = { + name: constraintName, + columns: [columnName], + isUnique: isUnique, + }; + } } } diff --git a/drizzle-kit/src/serializer/studio.ts b/drizzle-kit/src/serializer/studio.ts index 12ea8207c..14f9a0d2e 100644 --- a/drizzle-kit/src/serializer/studio.ts +++ b/drizzle-kit/src/serializer/studio.ts @@ -13,14 +13,14 @@ import { Relations, TablesRelationalConfig, } from 'drizzle-orm'; -import { AnyMySqlTable, getTableConfig as mysqlTableConfig, MySqlTable } from 'drizzle-orm/mysql-core'; -import { AnyPgTable, getTableConfig as pgTableConfig, PgTable } from 'drizzle-orm/pg-core'; +import { AnyMySqlTable, MySqlTable, getTableConfig as mysqlTableConfig } from 'drizzle-orm/mysql-core'; +import { AnyPgTable, PgTable, getTableConfig as pgTableConfig } from 'drizzle-orm/pg-core'; import { AnySingleStoreTable, - getTableConfig as singlestoreTableConfig, SingleStoreTable, + getTableConfig as singlestoreTableConfig, } from 'drizzle-orm/singlestore-core'; -import { AnySQLiteTable, getTableConfig as sqliteTableConfig, SQLiteTable } from 'drizzle-orm/sqlite-core'; +import { AnySQLiteTable, SQLiteTable, getTableConfig as sqliteTableConfig } from 'drizzle-orm/sqlite-core'; import fs from 'fs'; import { Hono } from 'hono'; import { cors } from 'hono/cors'; @@ -29,12 +29,12 @@ import { LibSQLCredentials } from 'src/cli/validations/libsql'; import { assertUnreachable } from 'src/global'; import superjson from 'superjson'; import { z } from 'zod'; +import { prepareFilenames } from '.'; import { safeRegister } from '../cli/commands/utils'; import type { MysqlCredentials } from '../cli/validations/mysql'; import type { PostgresCredentials } from '../cli/validations/postgres'; import type { SingleStoreCredentials } from '../cli/validations/singlestore'; import type { SqliteCredentials } from '../cli/validations/sqlite'; -import { prepareFilenames } from '.'; type CustomDefault = { schema: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dfb3ae80d..4b41d4480 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7311,10 +7311,6 @@ packages: resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} engines: {node: '>=16.14'} - lru-cache@9.1.2: - resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} - engines: {node: 14 || >=16.14} - lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} @@ -7628,10 +7624,6 @@ packages: resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} engines: {node: '>=0.8.0'} - mysql2@3.11.0: - resolution: {integrity: sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==} - engines: {node: '>= 8.0'} - mysql2@3.3.3: resolution: {integrity: sha512-MxDQJztArk4JFX1PKVjDhIXRzAmVJfuqZrVU+my6NeYBAA/XZRaDw5q7vga8TNvgyy3Lv3rivBFBBuJFbsdjaw==} engines: {node: '>= 8.0'} @@ -18600,8 +18592,6 @@ snapshots: lru-cache@8.0.5: {} - lru-cache@9.1.2: {} - lru-queue@0.1.0: dependencies: es5-ext: 0.10.62 @@ -19040,19 +19030,6 @@ snapshots: rimraf: 2.4.5 optional: true - mysql2@3.11.0: - dependencies: - aws-ssl-profiles: 1.1.1 - denque: 2.1.0 - generate-function: 2.3.1 - iconv-lite: 0.6.3 - long: 5.2.3 - lru-cache: 8.0.5 - named-placeholders: 1.1.3 - seq-queue: 0.0.5 - sqlstring: 2.3.3 - optional: true - mysql2@3.3.3: dependencies: denque: 2.1.0 @@ -19465,7 +19442,7 @@ snapshots: path-scurry@1.10.1: dependencies: - lru-cache: 9.1.2 + lru-cache: 10.2.2 minipass: 5.0.0 path-scurry@1.11.1: From f86f5a164918e0733666edfdf8b6119d3a1c51fa Mon Sep 17 00:00:00 2001 From: prodrigues Date: Thu, 8 Aug 2024 13:51:05 +0100 Subject: [PATCH 54/56] create new singlestore-schemas.test.ts file --- drizzle-kit/src/cli/commands/utils.ts | 8 +-- drizzle-kit/src/cli/connections.ts | 80 --------------------------- drizzle-kit/src/jsonStatements.ts | 1 - drizzle-kit/tests/schemaDiffer.ts | 3 +- 4 files changed, 2 insertions(+), 90 deletions(-) diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index f01ee5e0b..d80b736fe 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -16,8 +16,7 @@ import { Prefix, wrapParam, } from '../validations/common'; -import { LibSQLCredentials, libSQLCredentials } from '../validations/libsql'; -import { printConfigConnectionIssues as printIssuesLibSql } from '../validations/libsql'; +import { LibSQLCredentials, libSQLCredentials, printConfigConnectionIssues as printIssuesLibSql } from '../validations/libsql'; import { MysqlCredentials, mysqlCredentials, @@ -30,11 +29,6 @@ import { printConfigConnectionIssues as printIssuesPg, } from '../validations/postgres'; import { printConfigConnectionIssues as printIssuesSingleStore, singlestoreCredentials, SingleStoreCredentials } from '../validations/singlestore'; -import { - printConfigConnectionIssues as printIssuesSingleStore, - SingleStoreCredentials, - singlestoreCredentials, -} from '../validations/singlestore'; import { printConfigConnectionIssues as printIssuesSqlite, SqliteCredentials, diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index 41bcebeb7..f17351dfd 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -21,7 +21,6 @@ import { withStyle } from './validations/outputs'; import type { PostgresCredentials } from './validations/postgres'; import { SingleStoreCredentials } from './validations/singlestore'; import type { SqliteCredentials } from './validations/sqlite'; -import { SingleStoreCredentials } from './validations/singlestore'; export const preparePostgresDB = async ( credentials: PostgresCredentials, @@ -812,82 +811,3 @@ export const connectToSingleStore = async ( ); process.exit(1); }; - -const parseSingleStoreCredentials = (credentials: SingleStoreCredentials) => { - if ('url' in credentials) { - const url = credentials.url; - - const connectionUrl = new URL(url); - const pathname = connectionUrl.pathname; - - const database = pathname.split('/')[pathname.split('/').length - 1]; - if (!database) { - console.error( - 'You should specify a database name in connection string (singlestore://USER:PASSWORD@HOST:PORT/DATABASE)', - ); - process.exit(1); - } - return { database, url }; - } else { - return { - database: credentials.database, - credentials, - }; - } -}; - -export const connectToSingleStore = async ( - it: SingleStoreCredentials, -): Promise<{ - db: DB; - proxy: Proxy; - database: string; - migrate: (config: MigrationConfig) => Promise; -}> => { - const result = parseSingleStoreCredentials(it); - - if (await checkPackage('singlestore')) { - const { createConnection } = await import('mysql2/promise'); - const { drizzle } = await import('drizzle-orm/singlestore'); - const { migrate } = await import('drizzle-orm/singlestore/migrator'); - - const connection = result.url - ? await createConnection(result.url) - : await createConnection(result.credentials!); // needed for some reason! - - const db = drizzle(connection); - const migrateFn = async (config: MigrationConfig) => { - return migrate(db, config); - }; - - await connection.connect(); - const query: DB['query'] = async ( - sql: string, - params?: any[], - ): Promise => { - const res = await connection.execute(sql, params); - return res[0] as any; - }; - - const proxy: Proxy = async (params: ProxyParams) => { - const result = await connection.query({ - sql: params.sql, - values: params.params, - rowsAsArray: params.mode === 'array', - }); - return result[0] as any[]; - }; - - return { - db: { query }, - proxy, - database: result.database, - migrate: migrateFn, - }; - } - - console.error( - "To connect to SingleStore database - please install 'mysql2' drivers", - ); - process.exit(1); -}; diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index cd3179657..c4c51d4b4 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -3,7 +3,6 @@ import { getNewTableName } from './cli/commands/sqlitePushUtils'; import { warning } from './cli/views'; import { CommonSquashedSchema } from './schemaValidator'; import { MySqlKitInternals, MySqlSchema, MySqlSquasher } from './serializer/mysqlSchema'; -import { SingleStoreKitInternals, SingleStoreSchema, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { Index, PgSchema, PgSquasher } from './serializer/pgSchema'; import { SingleStoreKitInternals, SingleStoreSchema, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { diff --git a/drizzle-kit/tests/schemaDiffer.ts b/drizzle-kit/tests/schemaDiffer.ts index 07da0ed05..da16dac87 100644 --- a/drizzle-kit/tests/schemaDiffer.ts +++ b/drizzle-kit/tests/schemaDiffer.ts @@ -4,8 +4,7 @@ import { Database } from 'better-sqlite3'; import { is } from 'drizzle-orm'; import { MySqlSchema, MySqlTable } from 'drizzle-orm/mysql-core'; import { isPgEnum, isPgSequence, PgEnum, PgSchema, PgSequence, PgTable } from 'drizzle-orm/pg-core'; -import { SingleStoreSchema } from 'drizzle-orm/singlestore-core'; -import { SingleStoreTable } from 'drizzle-orm/singlestore-core'; +import { SingleStoreSchema, SingleStoreTable } from 'drizzle-orm/singlestore-core'; import { SQLiteTable } from 'drizzle-orm/sqlite-core'; import * as fs from 'fs'; import { Connection } from 'mysql2/promise'; From f32c8b872808bb875394448484ae3d656a0aa61f Mon Sep 17 00:00:00 2001 From: prodrigues Date: Tue, 13 Aug 2024 11:20:06 +0100 Subject: [PATCH 55/56] lint fix on drizzle-kit files --- drizzle-kit/src/cli/commands/utils.ts | 18 +- drizzle-kit/src/cli/connections.ts | 2 +- drizzle-kit/src/cli/schema.ts | 41 +++- drizzle-kit/src/introspect-singlestore.ts | 11 +- drizzle-kit/src/jsonStatements.ts | 6 +- drizzle-kit/src/migrationPreparator.ts | 1 - drizzle-kit/src/schemaValidator.ts | 1 - .../src/serializer/singlestoreSerializer.ts | 20 +- drizzle-kit/src/serializer/studio.ts | 10 +- drizzle-kit/src/snapshotsDiffer.ts | 4 +- drizzle-kit/tests/schemaDiffer.ts | 14 -- pnpm-lock.yaml | 221 ++++++------------ 12 files changed, 152 insertions(+), 197 deletions(-) diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index d80b736fe..d206fd235 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -16,7 +16,11 @@ import { Prefix, wrapParam, } from '../validations/common'; -import { LibSQLCredentials, libSQLCredentials, printConfigConnectionIssues as printIssuesLibSql } from '../validations/libsql'; +import { + LibSQLCredentials, + libSQLCredentials, + printConfigConnectionIssues as printIssuesLibSql, +} from '../validations/libsql'; import { MysqlCredentials, mysqlCredentials, @@ -28,7 +32,11 @@ import { postgresCredentials, printConfigConnectionIssues as printIssuesPg, } from '../validations/postgres'; -import { printConfigConnectionIssues as printIssuesSingleStore, singlestoreCredentials, SingleStoreCredentials } from '../validations/singlestore'; +import { + printConfigConnectionIssues as printIssuesSingleStore, + SingleStoreCredentials, + singlestoreCredentials, +} from '../validations/singlestore'; import { printConfigConnectionIssues as printIssuesSqlite, SqliteCredentials, @@ -216,7 +224,8 @@ export const preparePushConfig = async ( | { dialect: 'turso'; credentials: LibSQLCredentials; - } | { + } + | { dialect: 'singlestore'; credentials: SingleStoreCredentials; } @@ -404,7 +413,8 @@ export const preparePullConfig = async ( | { dialect: 'turso'; credentials: LibSQLCredentials; - } | { + } + | { dialect: 'singlestore'; credentials: SingleStoreCredentials; } diff --git a/drizzle-kit/src/cli/connections.ts b/drizzle-kit/src/cli/connections.ts index f17351dfd..fccc7def8 100644 --- a/drizzle-kit/src/cli/connections.ts +++ b/drizzle-kit/src/cli/connections.ts @@ -731,7 +731,7 @@ export const connectToLibSQL = async (credentials: LibSQLCredentials): Promise< "Please install '@libsql/client' for Drizzle Kit to connect to LibSQL databases", ); process.exit(1); -} +}; const parseSingleStoreCredentials = (credentials: SingleStoreCredentials) => { if ('url' in credentials) { diff --git a/drizzle-kit/src/cli/schema.ts b/drizzle-kit/src/cli/schema.ts index c7d5b88f3..5316a9e90 100644 --- a/drizzle-kit/src/cli/schema.ts +++ b/drizzle-kit/src/cli/schema.ts @@ -24,8 +24,9 @@ import { mkdirSync } from 'fs'; import { renderWithTask } from 'hanji'; import { dialects } from 'src/schemaValidator'; import { assertUnreachable } from '../global'; -import { drizzleForLibSQL, type Setup } from '../serializer/studio'; +import { drizzleForLibSQL, drizzleForSingleStore, prepareSingleStoreSchema, type Setup } from '../serializer/studio'; import { certs } from '../utils/certs'; +import { prepareAndMigrateSingleStore } from './commands/migrate'; import { grey, MigrateProgress } from './views'; const optionDialect = string('dialect') @@ -89,6 +90,8 @@ export const generate = command({ await prepareAndMigrateSqlite(opts); } else if (dialect === 'turso') { await prepareAndMigrateLibSQL(opts); + } else if (dialect === 'singlestore') { + await prepareAndMigrateSingleStore(opts); } else { assertUnreachable(dialect); } @@ -173,6 +176,17 @@ export const migrate = command({ migrationsSchema: schema, }), ); + } else if (dialect === 'singlestore') { + const { connectToSingleStore } = await import('./connections'); + const { migrate } = await connectToSingleStore(credentials); + await renderWithTask( + new MigrateProgress(), + migrate({ + migrationsFolder: out, + migrationsTable: table, + migrationsSchema: schema, + }), + ); } else { assertUnreachable(dialect); } @@ -328,6 +342,16 @@ export const push = command({ tablesFilter, force, ); + } else if (dialect === 'singlestore') { + const { singlestorePush } = await import('./commands/push'); + await singlestorePush( + schemaPath, + credentials, + tablesFilter, + strict, + verbose, + force, + ); } else { assertUnreachable(dialect); } @@ -517,6 +541,16 @@ export const pull = command({ tablesFilter, prefix, ); + } else if (dialect === 'singlestore') { + const { introspectSingleStore } = await import('./commands/introspect'); + await introspectSingleStore( + casing, + out, + breakpoints, + credentials, + tablesFilter, + prefix, + ); } else { assertUnreachable(dialect); } @@ -622,6 +656,11 @@ export const studio = command({ ? await prepareSQLiteSchema(schemaPath) : { schema: {}, relations: {}, files: [] }; setup = await drizzleForLibSQL(credentials, schema, relations, files); + } else if (dialect === 'singlestore') { + const { schema, relations, files } = schemaPath + ? await prepareSingleStoreSchema(schemaPath) + : { schema: {}, relations: {}, files: [] }; + setup = await drizzleForSingleStore(credentials, schema, relations, files); } else { assertUnreachable(dialect); } diff --git a/drizzle-kit/src/introspect-singlestore.ts b/drizzle-kit/src/introspect-singlestore.ts index b9efc537a..8aa6e3dd7 100644 --- a/drizzle-kit/src/introspect-singlestore.ts +++ b/drizzle-kit/src/introspect-singlestore.ts @@ -386,16 +386,15 @@ const column = ( ? `${casing(name)}: timestamp("${name}", ${params})` : `${casing(name)}: timestamp("${name}")`; - - // TODO: check if SingleStore has defaultNow() or now() - defaultValue = defaultValue === 'now()' || defaultValue === '(CURRENT_TIMESTAMP)' + // TODO: check if SingleStore has defaultNow() or now() + defaultValue = defaultValue === 'now()' || defaultValue === '(CURRENT_TIMESTAMP)' ? '.defaultNow()' : defaultValue ? `.default(${mapColumnDefault(defaultValue, isExpression)})` : ''; - - out += defaultValue; - + + out += defaultValue; + // TODO: check if SingleStore has onUpdateNow() let onUpdateNow = onUpdate ? '.onUpdateNow()' : ''; out += onUpdateNow; diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index c4c51d4b4..b27785d9a 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -6,8 +6,10 @@ import { MySqlKitInternals, MySqlSchema, MySqlSquasher } from './serializer/mysq import { Index, PgSchema, PgSquasher } from './serializer/pgSchema'; import { SingleStoreKitInternals, SingleStoreSchema, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { - SQLiteKitInternals, SQLiteSchemaInternal, - SQLiteSchemaSquashed, SQLiteSquasher + SQLiteKitInternals, + SQLiteSchemaInternal, + SQLiteSchemaSquashed, + SQLiteSquasher, } from './serializer/sqliteSchema'; import { AlteredColumn, Column, Sequence, Table } from './snapshotsDiffer'; diff --git a/drizzle-kit/src/migrationPreparator.ts b/drizzle-kit/src/migrationPreparator.ts index 3d1ddbea3..4e5664290 100644 --- a/drizzle-kit/src/migrationPreparator.ts +++ b/drizzle-kit/src/migrationPreparator.ts @@ -5,7 +5,6 @@ import { dryMySql, MySqlSchema, mysqlSchema } from './serializer/mysqlSchema'; import { dryPg, PgSchema, pgSchema, PgSchemaInternal } from './serializer/pgSchema'; import { drySingleStore, SingleStoreSchema, singlestoreSchema } from './serializer/singlestoreSchema'; import { drySQLite, SQLiteSchema, sqliteSchema } from './serializer/sqliteSchema'; -import { drySingleStore, singlestoreSchema, SingleStoreSchema } from './serializer/singlestoreSchema'; export const prepareMySqlDbPushSnapshot = async ( prev: MySqlSchema, diff --git a/drizzle-kit/src/schemaValidator.ts b/drizzle-kit/src/schemaValidator.ts index 4065e948f..e91b5ab11 100644 --- a/drizzle-kit/src/schemaValidator.ts +++ b/drizzle-kit/src/schemaValidator.ts @@ -3,7 +3,6 @@ import { mysqlSchema, mysqlSchemaSquashed } from './serializer/mysqlSchema'; import { pgSchema, pgSchemaSquashed } from './serializer/pgSchema'; import { singlestoreSchema, singlestoreSchemaSquashed } from './serializer/singlestoreSchema'; import { sqliteSchema, SQLiteSchemaSquashed } from './serializer/sqliteSchema'; -import { singlestoreSchema, singlestoreSchemaSquashed } from './serializer/singlestoreSchema'; export const dialects = ['postgresql', 'mysql', 'sqlite', 'turso', 'singlestore'] as const; export const dialect = enumType(dialects); diff --git a/drizzle-kit/src/serializer/singlestoreSerializer.ts b/drizzle-kit/src/serializer/singlestoreSerializer.ts index 3f36f9c21..fc91becf2 100644 --- a/drizzle-kit/src/serializer/singlestoreSerializer.ts +++ b/drizzle-kit/src/serializer/singlestoreSerializer.ts @@ -11,8 +11,8 @@ import { RowDataPacket } from 'mysql2/promise'; import { withStyle } from '../cli/validations/outputs'; import { IntrospectStage, IntrospectStatus } from '../cli/views'; -import { sqlToStr } from '.'; import type { DB } from '../utils'; +import { sqlToStr } from '.'; import { Column, Index, @@ -573,15 +573,15 @@ export const fromDatabase = async ( }; } } else { - if (typeof tableInResult.indexes[constraintName] !== 'undefined') { - tableInResult.indexes[constraintName]!.columns.push(columnName); - } else { - tableInResult.indexes[constraintName] = { - name: constraintName, - columns: [columnName], - isUnique: isUnique, - }; - } + if (typeof tableInResult.indexes[constraintName] !== 'undefined') { + tableInResult.indexes[constraintName]!.columns.push(columnName); + } else { + tableInResult.indexes[constraintName] = { + name: constraintName, + columns: [columnName], + isUnique: isUnique, + }; + } } } diff --git a/drizzle-kit/src/serializer/studio.ts b/drizzle-kit/src/serializer/studio.ts index 14f9a0d2e..12ea8207c 100644 --- a/drizzle-kit/src/serializer/studio.ts +++ b/drizzle-kit/src/serializer/studio.ts @@ -13,14 +13,14 @@ import { Relations, TablesRelationalConfig, } from 'drizzle-orm'; -import { AnyMySqlTable, MySqlTable, getTableConfig as mysqlTableConfig } from 'drizzle-orm/mysql-core'; -import { AnyPgTable, PgTable, getTableConfig as pgTableConfig } from 'drizzle-orm/pg-core'; +import { AnyMySqlTable, getTableConfig as mysqlTableConfig, MySqlTable } from 'drizzle-orm/mysql-core'; +import { AnyPgTable, getTableConfig as pgTableConfig, PgTable } from 'drizzle-orm/pg-core'; import { AnySingleStoreTable, - SingleStoreTable, getTableConfig as singlestoreTableConfig, + SingleStoreTable, } from 'drizzle-orm/singlestore-core'; -import { AnySQLiteTable, SQLiteTable, getTableConfig as sqliteTableConfig } from 'drizzle-orm/sqlite-core'; +import { AnySQLiteTable, getTableConfig as sqliteTableConfig, SQLiteTable } from 'drizzle-orm/sqlite-core'; import fs from 'fs'; import { Hono } from 'hono'; import { cors } from 'hono/cors'; @@ -29,12 +29,12 @@ import { LibSQLCredentials } from 'src/cli/validations/libsql'; import { assertUnreachable } from 'src/global'; import superjson from 'superjson'; import { z } from 'zod'; -import { prepareFilenames } from '.'; import { safeRegister } from '../cli/commands/utils'; import type { MysqlCredentials } from '../cli/validations/mysql'; import type { PostgresCredentials } from '../cli/validations/postgres'; import type { SingleStoreCredentials } from '../cli/validations/singlestore'; import type { SqliteCredentials } from '../cli/validations/sqlite'; +import { prepareFilenames } from '.'; type CustomDefault = { schema: string; diff --git a/drizzle-kit/src/snapshotsDiffer.ts b/drizzle-kit/src/snapshotsDiffer.ts index ec8a5c1b8..6f27a2505 100644 --- a/drizzle-kit/src/snapshotsDiffer.ts +++ b/drizzle-kit/src/snapshotsDiffer.ts @@ -10,7 +10,7 @@ import { string, TypeOf, union, - ZodTypeAny + ZodTypeAny, } from 'zod'; import { applyJsonDiff, diffColumns, diffSchemasOrTables } from './jsonDiffer'; import { fromJson } from './sqlgenerator'; @@ -56,11 +56,11 @@ import { prepareDeleteCompositePrimaryKeyPg, prepareDeleteCompositePrimaryKeySingleStore, prepareDeleteCompositePrimaryKeySqlite, + prepareDeleteSchemasJson as prepareDropSchemasJson, prepareDeleteUniqueConstraintPg as prepareDeleteUniqueConstraint, prepareDropEnumJson, prepareDropIndexesJson, prepareDropReferencesJson, - prepareDeleteSchemasJson as prepareDropSchemasJson, prepareDropSequenceJson, prepareDropTableJson, prepareLibSQLCreateReferencesJson, diff --git a/drizzle-kit/tests/schemaDiffer.ts b/drizzle-kit/tests/schemaDiffer.ts index da16dac87..3223ca5e7 100644 --- a/drizzle-kit/tests/schemaDiffer.ts +++ b/drizzle-kit/tests/schemaDiffer.ts @@ -1099,13 +1099,6 @@ export async function diffTestSchemasPushLibSQL( run: async (query: string) => { await client.execute(query); }, - batch: async ( - queries: { query: string; values?: any[] | undefined }[], - ) => { - await client.batch( - queries.map((it) => ({ sql: it.query, args: it.values ?? [] })), - ); - }, }, undefined, ); @@ -1165,13 +1158,6 @@ export async function diffTestSchemasPushLibSQL( run: async (query: string) => { await client.execute(query); }, - batch: async ( - queries: { query: string; values?: any[] | undefined }[], - ) => { - await client.batch( - queries.map((it) => ({ sql: it.query, args: it.values ?? [] })), - ); - }, }, statements, sn1, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b41d4480..2870df804 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.10.0)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.11.0)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7) + version: drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.10.0)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.3.3)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7) eslint: specifier: ^8.50.0 version: 8.50.0 @@ -313,7 +313,7 @@ importers: version: 0.9.0 '@op-engineering/op-sqlite': specifier: ^2.0.16 - version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + version: 2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) '@opentelemetry/api': specifier: ^1.4.1 version: 1.8.0 @@ -361,7 +361,7 @@ importers: version: 10.1.0 expo-sqlite: specifier: ^13.2.0 - version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + version: 13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) knex: specifier: ^2.4.2 version: 2.5.1(better-sqlite3@8.7.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7) @@ -4630,10 +4630,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-ssl-profiles@1.1.1: - resolution: {integrity: sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ==} - engines: {node: '>= 6.0.0'} - axios@1.6.8: resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} @@ -7095,12 +7091,10 @@ packages: libsql@0.3.19: resolution: {integrity: sha512-Aj5cQ5uk/6fHdmeW0TiXK42FqUlwx7ytmMLPSaUQPin5HKKKuUPD62MAbN4OEweGBBI7q1BekoEN4gPUEL6MZA==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] libsql@0.4.1: resolution: {integrity: sha512-qZlR9Yu1zMBeLChzkE/cKfoKV3Esp9cn9Vx5Zirn4AVhDWPcjYhKwbtJcMuHehgk3mH+fJr9qW+3vesBWbQpBg==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lighthouse-logger@1.4.2: @@ -10206,7 +10200,7 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) @@ -10293,13 +10287,13 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.583.0': + '@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 '@aws-sdk/client-sts': 3.583.0 '@aws-sdk/core': 3.582.0 - '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)) + '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 '@aws-sdk/middleware-logger': 3.577.0 '@aws-sdk/middleware-recursion-detection': 3.577.0 @@ -10336,6 +10330,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso@3.478.0': @@ -10606,7 +10601,7 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -10647,52 +10642,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0 - '@aws-sdk/core': 3.582.0 - '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)) - '@aws-sdk/middleware-host-header': 3.577.0 - '@aws-sdk/middleware-logger': 3.577.0 - '@aws-sdk/middleware-recursion-detection': 3.577.0 - '@aws-sdk/middleware-user-agent': 3.583.0 - '@aws-sdk/region-config-resolver': 3.577.0 - '@aws-sdk/types': 3.577.0 - '@aws-sdk/util-endpoints': 3.583.0 - '@aws-sdk/util-user-agent-browser': 3.577.0 - '@aws-sdk/util-user-agent-node': 3.577.0 - '@smithy/config-resolver': 3.0.0 - '@smithy/core': 2.0.1 - '@smithy/fetch-http-handler': 3.0.1 - '@smithy/hash-node': 3.0.0 - '@smithy/invalid-dependency': 3.0.0 - '@smithy/middleware-content-length': 3.0.0 - '@smithy/middleware-endpoint': 3.0.0 - '@smithy/middleware-retry': 3.0.1 - '@smithy/middleware-serde': 3.0.0 - '@smithy/middleware-stack': 3.0.0 - '@smithy/node-config-provider': 3.0.0 - '@smithy/node-http-handler': 3.0.0 - '@smithy/protocol-http': 4.0.0 - '@smithy/smithy-client': 3.0.1 - '@smithy/types': 3.0.0 - '@smithy/url-parser': 3.0.0 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.1 - '@smithy/util-defaults-mode-node': 3.0.1 - '@smithy/util-endpoints': 2.0.0 - '@smithy/util-middleware': 3.0.0 - '@smithy/util-retry': 3.0.0 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.2 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - '@aws-sdk/core@3.477.0': dependencies: '@smithy/core': 1.4.2 @@ -10933,25 +10882,6 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0))': - dependencies: - '@aws-sdk/credential-provider-env': 3.577.0 - '@aws-sdk/credential-provider-http': 3.582.0 - '@aws-sdk/credential-provider-ini': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) - '@aws-sdk/credential-provider-process': 3.577.0 - '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) - '@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)) - '@aws-sdk/types': 3.577.0 - '@smithy/credential-provider-imds': 3.0.0 - '@smithy/property-provider': 3.0.0 - '@smithy/shared-ini-file-loader': 3.0.0 - '@smithy/types': 3.0.0 - tslib: 2.6.2 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - '@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: '@aws-sdk/credential-provider-env': 3.577.0 @@ -11069,14 +10999,6 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.6.2 - '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0))': - dependencies: - '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) - '@aws-sdk/types': 3.577.0 - '@smithy/property-provider': 3.0.0 - '@smithy/types': 3.0.0 - tslib: 2.6.2 - '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0)': dependencies: '@aws-sdk/client-sts': 3.583.0 @@ -11280,7 +11202,7 @@ snapshots: '@aws-sdk/token-providers@3.568.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/types': 3.567.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 @@ -11289,7 +11211,7 @@ snapshots: '@aws-sdk/token-providers@3.577.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) '@aws-sdk/types': 3.577.0 '@smithy/property-provider': 3.0.0 '@smithy/shared-ini-file-loader': 3.0.0 @@ -12961,7 +12883,7 @@ snapshots: mv: 2.1.1 safe-json-stringify: 1.2.0 - '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)': + '@expo/cli@0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3)': dependencies: '@babel/runtime': 7.24.6 '@expo/code-signing-certificates': 0.0.5 @@ -12979,7 +12901,7 @@ snapshots: '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) '@expo/spawn-async': 1.7.2 '@expo/xcpretty': 4.3.1 - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/dev-middleware': 0.74.83(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 @@ -13553,10 +13475,10 @@ snapshots: rimraf: 3.0.2 optional: true - '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@op-engineering/op-sqlite@2.0.22(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': dependencies: react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) '@opentelemetry/api@1.8.0': {} @@ -13693,7 +13615,7 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) @@ -13703,7 +13625,7 @@ snapshots: nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -13730,14 +13652,14 @@ snapshots: dependencies: joi: 17.13.1 - '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native-community/cli@13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@react-native-community/cli-clean': 13.6.6(encoding@0.1.13) '@react-native-community/cli-config': 13.6.6(encoding@0.1.13) '@react-native-community/cli-debugger-ui': 13.6.6 '@react-native-community/cli-doctor': 13.6.6(encoding@0.1.13) '@react-native-community/cli-hermes': 13.6.6(encoding@0.1.13) - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) '@react-native-community/cli-types': 13.6.6 chalk: 4.1.2 @@ -13826,16 +13748,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: - '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli-server-api': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-tools': 13.6.6(encoding@0.1.13) - '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/dev-middleware': 0.74.83(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/metro-babel-transformer': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.9 node-fetch: 2.7.0(encoding@0.1.13) querystring: 0.2.1 @@ -13850,7 +13772,7 @@ snapshots: '@react-native/debugger-frontend@0.74.83': {} - '@react-native/dev-middleware@0.74.83(bufferutil@4.0.8)(encoding@0.1.13)': + '@react-native/dev-middleware@0.74.83(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.74.83 @@ -13864,7 +13786,7 @@ snapshots: selfsigned: 2.4.1 serve-static: 1.15.0 temp-dir: 2.0.0 - ws: 6.2.2(bufferutil@4.0.8) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -13887,12 +13809,12 @@ snapshots: '@react-native/normalize-colors@0.74.83': {} - '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@react-native/virtualized-lists@0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3) optionalDependencies: '@types/react': 18.3.1 @@ -15469,9 +15391,6 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - aws-ssl-profiles@1.1.1: - optional: true - axios@1.6.8: dependencies: follow-redirects: 1.15.6 @@ -16357,7 +16276,7 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.10.0)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.11.0)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7): + drizzle-orm@0.27.2(@aws-sdk/client-rds-data@3.583.0)(@cloudflare/workers-types@4.20240524.0)(@libsql/client@0.10.0)(@neondatabase/serverless@0.9.3)(@opentelemetry/api@1.8.0)(@planetscale/database@1.18.0)(@types/better-sqlite3@7.6.10)(@types/pg@8.11.6)(@types/sql.js@1.4.9)(@vercel/postgres@0.8.0)(better-sqlite3@9.6.0)(bun-types@1.0.3)(knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7))(kysely@0.25.0)(mysql2@3.3.3)(pg@8.11.5)(postgres@3.4.4)(sql.js@1.10.3)(sqlite3@5.1.7): optionalDependencies: '@aws-sdk/client-rds-data': 3.583.0 '@cloudflare/workers-types': 4.20240524.0 @@ -16371,9 +16290,9 @@ snapshots: '@vercel/postgres': 0.8.0 better-sqlite3: 9.6.0 bun-types: 1.0.3 - knex: 2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7) + knex: 2.5.1(better-sqlite3@9.6.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7) kysely: 0.25.0 - mysql2: 3.11.0 + mysql2: 3.3.3 pg: 8.11.5 postgres: 3.4.4 sql.js: 1.10.3 @@ -17198,35 +17117,35 @@ snapshots: expand-template@2.0.3: {} - expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-asset@10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@react-native/assets-registry': 0.74.83 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) - expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) + expo-constants: 16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) invariant: 2.2.4 md5-file: 3.2.3 transitivePeerDependencies: - supports-color - expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-constants@16.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/config': 9.0.2 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) transitivePeerDependencies: - supports-color - expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-file-system@17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-font@12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) fontfaceobserver: 2.3.0 - expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-keep-awake@13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) expo-modules-autolinking@1.11.1: dependencies: @@ -17240,24 +17159,24 @@ snapshots: dependencies: invariant: 2.2.4 - expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)): + expo-sqlite@13.4.0(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)): dependencies: '@expo/websql': 1.0.1 - expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + expo: 51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) - expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13): + expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/runtime': 7.24.6 - '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1) + '@expo/cli': 0.18.13(bufferutil@4.0.8)(encoding@0.1.13)(expo-modules-autolinking@1.11.1)(utf-8-validate@6.0.3) '@expo/config': 9.0.2 '@expo/config-plugins': 8.0.4 '@expo/metro-config': 0.18.4 '@expo/vector-icons': 14.0.2 babel-preset-expo: 11.0.6(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) - expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)) + expo-asset: 10.0.6(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-file-system: 17.0.1(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-font: 12.0.5(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) + expo-keep-awake: 13.0.2(expo@51.0.8(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3)) expo-modules-autolinking: 1.11.1 expo-modules-core: 1.12.11 fbemitter: 3.0.0(encoding@0.1.13) @@ -18364,7 +18283,7 @@ snapshots: transitivePeerDependencies: - supports-color - knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.11.0)(pg@8.11.5)(sqlite3@5.1.7): + knex@2.5.1(better-sqlite3@9.6.0)(mysql2@3.3.3)(pg@8.11.5)(sqlite3@5.1.7): dependencies: colorette: 2.0.19 commander: 10.0.1 @@ -18382,7 +18301,7 @@ snapshots: tildify: 2.0.0 optionalDependencies: better-sqlite3: 9.6.0 - mysql2: 3.11.0 + mysql2: 3.3.3 pg: 8.11.5 sqlite3: 5.1.7 transitivePeerDependencies: @@ -18731,12 +18650,12 @@ snapshots: metro-core: 0.80.9 rimraf: 3.0.2 - metro-config@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro-config@0.80.9(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.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-cache: 0.80.9 metro-core: 0.80.9 metro-runtime: 0.80.9 @@ -18812,13 +18731,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro-transform-worker@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/core': 7.24.6 '@babel/generator': 7.24.6 '@babel/parser': 7.24.6 '@babel/types': 7.24.6 - metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 @@ -18832,7 +18751,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13): + metro@0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3): dependencies: '@babel/code-frame': 7.24.6 '@babel/core': 7.24.6 @@ -18858,7 +18777,7 @@ snapshots: metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 metro-cache-key: 0.80.9 - metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-config: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) metro-core: 0.80.9 metro-file-map: 0.80.9 metro-resolver: 0.80.9 @@ -18866,7 +18785,7 @@ snapshots: metro-source-map: 0.80.9 metro-symbolicate: 0.80.9 metro-transform-plugins: 0.80.9 - metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13) + metro-transform-worker: 0.80.9(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) mime-types: 2.1.35 node-fetch: 2.7.0(encoding@0.1.13) nullthrows: 1.1.1 @@ -18875,7 +18794,7 @@ snapshots: source-map: 0.5.7 strip-ansi: 6.0.1 throat: 5.0.0 - ws: 7.5.9(bufferutil@4.0.8) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -19748,10 +19667,10 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-devtools-core@5.2.0(bufferutil@4.0.8): + react-devtools-core@5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: shell-quote: 1.8.1 - ws: 7.5.9(bufferutil@4.0.8) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -19764,19 +19683,19 @@ snapshots: react-is@18.3.1: {} - react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1): + react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native-community/cli': 13.6.6(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native-community/cli-platform-android': 13.6.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 13.6.6(encoding@0.1.13) '@react-native/assets-registry': 0.74.83 '@react-native/codegen': 0.74.83(@babel/preset-env@7.24.6(@babel/core@7.24.6)) - '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.74.83(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.3) '@react-native/gradle-plugin': 0.74.83 '@react-native/js-polyfills': 0.74.83 '@react-native/normalize-colors': 0.74.83 - '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + '@react-native/virtualized-lists': 0.74.83(@types/react@18.3.1)(react-native@0.74.1(@babel/core@7.24.6)(@babel/preset-env@7.24.6(@babel/core@7.24.6))(@types/react@18.3.1)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@6.0.3))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -19795,14 +19714,14 @@ snapshots: pretty-format: 26.6.2 promise: 8.3.0 react: 18.3.1 - react-devtools-core: 5.2.0(bufferutil@4.0.8) + react-devtools-core: 5.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) react-refresh: 0.14.2 react-shallow-renderer: 16.15.0(react@18.3.1) 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) + ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) yargs: 17.7.2 optionalDependencies: '@types/react': 18.3.1 @@ -21655,15 +21574,17 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.0.2 - ws@6.2.2(bufferutil@4.0.8): + 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(bufferutil@4.0.8): + 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.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3): optionalDependencies: From 28876e6d53e0141f3bf6922c507d3021bbc51557 Mon Sep 17 00:00:00 2001 From: prodrigues Date: Fri, 20 Sep 2024 14:12:50 +0100 Subject: [PATCH 56/56] adding missing driver declaration in drizzle-orm --- drizzle-kit/src/cli/commands/singlestoreUp.ts | 1 + drizzle-kit/src/cli/commands/utils.ts | 6 ++--- drizzle-kit/src/cli/schema.ts | 27 +++++++++++-------- drizzle-orm/src/monodriver.ts | 20 +++++++++++++- drizzle-orm/src/monomigrator.ts | 9 ++++++- 5 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 drizzle-kit/src/cli/commands/singlestoreUp.ts diff --git a/drizzle-kit/src/cli/commands/singlestoreUp.ts b/drizzle-kit/src/cli/commands/singlestoreUp.ts new file mode 100644 index 000000000..dc5004ed0 --- /dev/null +++ b/drizzle-kit/src/cli/commands/singlestoreUp.ts @@ -0,0 +1 @@ +export const upSinglestoreHandler = (out: string) => {}; diff --git a/drizzle-kit/src/cli/commands/utils.ts b/drizzle-kit/src/cli/commands/utils.ts index d206fd235..25e80ed4d 100644 --- a/drizzle-kit/src/cli/commands/utils.ts +++ b/drizzle-kit/src/cli/commands/utils.ts @@ -334,7 +334,7 @@ export const preparePushConfig = async ( if (config.dialect === 'singlestore') { const parsed = singlestoreCredentials.safeParse(config); if (!parsed.success) { - printIssuesPg(config); + printIssuesSingleStore(config); process.exit(1); } @@ -508,7 +508,7 @@ export const preparePullConfig = async ( if (dialect === 'singlestore') { const parsed = singlestoreCredentials.safeParse(config); if (!parsed.success) { - printIssuesPg(config); + printIssuesSingleStore(config); process.exit(1); } @@ -617,7 +617,7 @@ export const prepareStudioConfig = async (options: Record) => { if (dialect === 'singlestore') { const parsed = singlestoreCredentials.safeParse(flattened); if (!parsed.success) { - printIssuesPg(flattened as Record); + printIssuesSingleStore(flattened as Record); process.exit(1); } const credentials = parsed.data; diff --git a/drizzle-kit/src/cli/schema.ts b/drizzle-kit/src/cli/schema.ts index 5316a9e90..6b7bcb560 100644 --- a/drizzle-kit/src/cli/schema.ts +++ b/drizzle-kit/src/cli/schema.ts @@ -1,11 +1,20 @@ +import { boolean, command, number, string } from '@drizzle-team/brocli'; import chalk from 'chalk'; -import { checkHandler } from './commands/check'; -import { assertOrmCoreVersion, assertPackages, assertStudioNodeVersion, ormVersionGt } from './utils'; +import 'dotenv/config'; +import { mkdirSync } from 'fs'; +import { renderWithTask } from 'hanji'; +import { dialects } from 'src/schemaValidator'; import '../@types/utils'; +import { assertUnreachable } from '../global'; +import { drizzleForLibSQL, drizzleForSingleStore, prepareSingleStoreSchema, type Setup } from '../serializer/studio'; import { assertV1OutFolder } from '../utils'; +import { certs } from '../utils/certs'; +import { checkHandler } from './commands/check'; import { dropMigration } from './commands/drop'; +import { prepareAndMigrateSingleStore } from './commands/migrate'; import { upMysqlHandler } from './commands/mysqlUp'; import { upPgHandler } from './commands/pgUp'; +import { upSinglestoreHandler } from './commands/singlestoreUp'; import { upSqliteHandler } from './commands/sqliteUp'; import { prepareCheckParams, @@ -16,17 +25,9 @@ import { preparePushConfig, prepareStudioConfig, } from './commands/utils'; +import { assertOrmCoreVersion, assertPackages, assertStudioNodeVersion, ormVersionGt } from './utils'; import { assertCollisions, drivers, prefixes } from './validations/common'; import { withStyle } from './validations/outputs'; -import 'dotenv/config'; -import { boolean, command, number, string } from '@drizzle-team/brocli'; -import { mkdirSync } from 'fs'; -import { renderWithTask } from 'hanji'; -import { dialects } from 'src/schemaValidator'; -import { assertUnreachable } from '../global'; -import { drizzleForLibSQL, drizzleForSingleStore, prepareSingleStoreSchema, type Setup } from '../serializer/studio'; -import { certs } from '../utils/certs'; -import { prepareAndMigrateSingleStore } from './commands/migrate'; import { grey, MigrateProgress } from './views'; const optionDialect = string('dialect') @@ -407,6 +408,10 @@ export const up = command({ upMysqlHandler(out); } + if (dialect === 'singlestore') { + upSinglestoreHandler(out); + } + if (dialect === 'sqlite' || dialect === 'turso') { upSqliteHandler(out); } diff --git a/drizzle-orm/src/monodriver.ts b/drizzle-orm/src/monodriver.ts index 0706f271a..03fcdc58f 100644 --- a/drizzle-orm/src/monodriver.ts +++ b/drizzle-orm/src/monodriver.ts @@ -30,6 +30,7 @@ import type { NeonDatabase } from './neon-serverless/index.ts'; import type { NodePgDatabase } from './node-postgres/index.ts'; import type { PlanetScaleDatabase } from './planetscale-serverless/index.ts'; import type { PostgresJsDatabase } from './postgres-js/index.ts'; +import type { SingleStore2Database, SingleStore2DrizzleConfig } from './singlestore/driver.ts'; import type { TiDBServerlessDatabase } from './tidb-serverless/index.ts'; import type { DrizzleConfig } from './utils.ts'; import type { VercelPgDatabase } from './vercel-postgres/index.ts'; @@ -98,7 +99,8 @@ type DatabaseClient = | 'libsql' | 'd1' | 'bun:sqlite' - | 'better-sqlite3'; + | 'better-sqlite3' + | 'singlestore'; type ClientDrizzleInstanceMap> = { 'node-postgres': NodePgDatabase; @@ -114,6 +116,7 @@ type ClientDrizzleInstanceMap> = { d1: DrizzleD1Database; 'bun:sqlite': BunSQLiteDatabase; 'better-sqlite3': DrizzleBetterSQLite3Database; + singlestore: SingleStore2Database; }; type ClientInstanceMap = { @@ -135,6 +138,7 @@ type ClientInstanceMap = { d1: D1Database; 'bun:sqlite': BunDatabase; 'better-sqlite3': BetterSQLite3Database; + singlestore: SingleStore2Database; }; type InitializerParams = { @@ -177,6 +181,10 @@ type InitializerParams = { 'better-sqlite3': { connection?: BetterSQLite3DatabaseConfig; }; + singlestore: { + // This Mysql2Config is from the node package 'mysql2' and not the one from Drizzle + connection: Mysql2Config; + }; }; type DetermineClient< @@ -379,6 +387,16 @@ export async function drizzle< const db = drizzle(sql, drizzleConfig) as any; db.$client = sql; + return db; + } + case 'singlestore': { + const { createPool } = await import('mysql2/promise').catch(() => importError('mysql2/promise')); + const instance = createPool(connection as Mysql2Config); + const { drizzle } = await import('./mysql2'); + + const db = drizzle(instance, drizzleConfig as SingleStore2DrizzleConfig) as any; + db.$client = instance; + return db; } } diff --git a/drizzle-orm/src/monomigrator.ts b/drizzle-orm/src/monomigrator.ts index bee18ad46..d8681ebf9 100644 --- a/drizzle-orm/src/monomigrator.ts +++ b/drizzle-orm/src/monomigrator.ts @@ -12,6 +12,7 @@ import type { NeonDatabase } from './neon-serverless/index.ts'; import type { NodePgDatabase } from './node-postgres/index.ts'; import type { PlanetScaleDatabase } from './planetscale-serverless/index.ts'; import type { PostgresJsDatabase } from './postgres-js/index.ts'; +import type { SingleStore2Database } from './singlestore/driver.ts'; import type { TiDBServerlessDatabase } from './tidb-serverless/index.ts'; import type { VercelPgDatabase } from './vercel-postgres/index.ts'; @@ -29,7 +30,8 @@ export async function migrate( | PlanetScaleDatabase | PostgresJsDatabase | VercelPgDatabase - | TiDBServerlessDatabase, + | TiDBServerlessDatabase + | SingleStore2Database, config: | string | MigrationConfig, @@ -100,5 +102,10 @@ export async function migrate( return migrate(db as VercelPgDatabase, config as string | MigrationConfig); } + case 'SingleStore2Database': { + const { migrate } = await import('./singlestore/migrator'); + + return migrate(db as SingleStore2Database, config as MigrationConfig); + } } }