drizzle-orm 1.0.0-beta.21 → 1.0.0-beta.22-1b8fcb2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/aws-data-api/common/index.d.cts +1 -1
- package/aws-data-api/common/index.d.ts +1 -1
- package/cache/core/cache-effect.d.cts +1 -1
- package/cache/core/cache-effect.d.ts +1 -1
- package/cache/upstash/cache.d.cts +1 -1
- package/cache/upstash/cache.d.ts +1 -1
- package/effect-postgres/migrator.d.cts +1 -1
- package/effect-postgres/migrator.d.ts +1 -1
- package/mssql-core/columns/real.cjs +3 -0
- package/mssql-core/columns/real.cjs.map +1 -1
- package/mssql-core/columns/real.d.cts +1 -0
- package/mssql-core/columns/real.d.ts +1 -0
- package/mssql-core/columns/real.js +3 -0
- package/mssql-core/columns/real.js.map +1 -1
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +13 -1
- package/pg-core/effect/session.d.cts +1 -1
- package/pg-core/effect/session.d.ts +1 -1
- package/sqlite-proxy/migrator.cjs +2 -40
- package/sqlite-proxy/migrator.cjs.map +1 -1
- package/sqlite-proxy/migrator.internal.cjs +61 -0
- package/sqlite-proxy/migrator.internal.cjs.map +1 -0
- package/sqlite-proxy/migrator.internal.d.cts +26 -0
- package/sqlite-proxy/migrator.internal.d.ts +26 -0
- package/sqlite-proxy/migrator.internal.js +59 -0
- package/sqlite-proxy/migrator.internal.js.map +1 -0
- package/sqlite-proxy/migrator.js +2 -39
- package/sqlite-proxy/migrator.js.map +1 -1
- package/up-migrations/sqlite.cjs +16 -13
- package/up-migrations/sqlite.cjs.map +1 -1
- package/up-migrations/sqlite.d.cts +1 -1
- package/up-migrations/sqlite.d.ts +1 -1
- package/up-migrations/sqlite.js +16 -13
- package/up-migrations/sqlite.js.map +1 -1
|
@@ -4,7 +4,7 @@ import { Field, TypeHint } from "@aws-sdk/client-rds-data";
|
|
|
4
4
|
|
|
5
5
|
//#region src/aws-data-api/common/index.d.ts
|
|
6
6
|
declare const typeHint: { [K in TypeHint]: K };
|
|
7
|
-
declare function getValueFromDataApi(field: Field): string | number | boolean | string[] |
|
|
7
|
+
declare function getValueFromDataApi(field: Field): string | number | boolean | string[] | Uint8Array<ArrayBufferLike> | number[] | boolean[] | _aws_sdk_client_rds_data0.ArrayValue[] | null;
|
|
8
8
|
declare function typingsToAwsTypeHint(typings?: QueryTypingsValue): TypeHint | undefined;
|
|
9
9
|
declare function toValueParam(value: any, typings?: QueryTypingsValue): {
|
|
10
10
|
value: Field;
|
|
@@ -4,7 +4,7 @@ import { Field, TypeHint } from "@aws-sdk/client-rds-data";
|
|
|
4
4
|
|
|
5
5
|
//#region src/aws-data-api/common/index.d.ts
|
|
6
6
|
declare const typeHint: { [K in TypeHint]: K };
|
|
7
|
-
declare function getValueFromDataApi(field: Field): string | number | boolean | string[] |
|
|
7
|
+
declare function getValueFromDataApi(field: Field): string | number | boolean | string[] | Uint8Array<ArrayBufferLike> | number[] | boolean[] | _aws_sdk_client_rds_data0.ArrayValue[] | null;
|
|
8
8
|
declare function typingsToAwsTypeHint(typings?: QueryTypingsValue): TypeHint | undefined;
|
|
9
9
|
declare function toValueParam(value: any, typings?: QueryTypingsValue): {
|
|
10
10
|
value: Field;
|
|
@@ -8,7 +8,7 @@ import * as Schema$1 from "effect/Schema";
|
|
|
8
8
|
//#region src/cache/core/cache-effect.d.ts
|
|
9
9
|
declare const EffectCache_base: Effect.Service.Class<EffectCache, "drizzle-orm/EffectCache", {
|
|
10
10
|
readonly sync: () => {
|
|
11
|
-
strategy: () => "
|
|
11
|
+
strategy: () => "explicit" | "all";
|
|
12
12
|
get: (key: string, tables: string[], isTag: boolean, isAutoInvalidate?: boolean | undefined) => Effect.Effect<any[] | undefined, EffectCacheError, never>;
|
|
13
13
|
put: (hashedQuery: string, response: any, tables: string[], isTag: boolean, config?: CacheConfig | undefined) => Effect.Effect<void, EffectCacheError, never>;
|
|
14
14
|
onMutate: (params: MutationOption) => Effect.Effect<void, EffectCacheError, never>;
|
|
@@ -8,7 +8,7 @@ import * as Schema$1 from "effect/Schema";
|
|
|
8
8
|
//#region src/cache/core/cache-effect.d.ts
|
|
9
9
|
declare const EffectCache_base: Effect.Service.Class<EffectCache, "drizzle-orm/EffectCache", {
|
|
10
10
|
readonly sync: () => {
|
|
11
|
-
strategy: () => "
|
|
11
|
+
strategy: () => "explicit" | "all";
|
|
12
12
|
get: (key: string, tables: string[], isTag: boolean, isAutoInvalidate?: boolean | undefined) => Effect.Effect<any[] | undefined, EffectCacheError, never>;
|
|
13
13
|
put: (hashedQuery: string, response: any, tables: string[], isTag: boolean, config?: CacheConfig | undefined) => Effect.Effect<void, EffectCacheError, never>;
|
|
14
14
|
onMutate: (params: MutationOption) => Effect.Effect<void, EffectCacheError, never>;
|
|
@@ -44,7 +44,7 @@ declare class UpstashCache extends Cache {
|
|
|
44
44
|
private luaScripts;
|
|
45
45
|
private internalConfig;
|
|
46
46
|
constructor(redis: Redis, config?: CacheConfig, useGlobally?: boolean | undefined);
|
|
47
|
-
strategy(): "
|
|
47
|
+
strategy(): "explicit" | "all";
|
|
48
48
|
private toInternalConfig;
|
|
49
49
|
get(key: string, tables: string[], isTag?: boolean, isAutoInvalidate?: boolean): Promise<any[] | undefined>;
|
|
50
50
|
put(key: string, response: any, tables: string[], isTag?: boolean, config?: CacheConfig): Promise<void>;
|
package/cache/upstash/cache.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ declare class UpstashCache extends Cache {
|
|
|
44
44
|
private luaScripts;
|
|
45
45
|
private internalConfig;
|
|
46
46
|
constructor(redis: Redis, config?: CacheConfig, useGlobally?: boolean | undefined);
|
|
47
|
-
strategy(): "
|
|
47
|
+
strategy(): "explicit" | "all";
|
|
48
48
|
private toInternalConfig;
|
|
49
49
|
get(key: string, tables: string[], isTag?: boolean, isAutoInvalidate?: boolean): Promise<any[] | undefined>;
|
|
50
50
|
put(key: string, response: any, tables: string[], isTag?: boolean, config?: CacheConfig): Promise<void>;
|
|
@@ -6,7 +6,7 @@ import * as effect_Effect0 from "effect/Effect";
|
|
|
6
6
|
import * as _effect_sql_SqlError0 from "@effect/sql/SqlError";
|
|
7
7
|
|
|
8
8
|
//#region src/effect-postgres/migrator.d.ts
|
|
9
|
-
declare function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: EffectPgDatabase<TSchema, TRelations>, config: MigrationConfig): effect_Effect0.Effect<undefined,
|
|
9
|
+
declare function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: EffectPgDatabase<TSchema, TRelations>, config: MigrationConfig): effect_Effect0.Effect<undefined, EffectDrizzleQueryError | _effect_sql_SqlError0.SqlError | MigratorInitError, never>;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { migrate };
|
|
12
12
|
//# sourceMappingURL=migrator.d.cts.map
|
|
@@ -6,7 +6,7 @@ import { AnyRelations } from "../relations.js";
|
|
|
6
6
|
import * as _effect_sql_SqlError0 from "@effect/sql/SqlError";
|
|
7
7
|
|
|
8
8
|
//#region src/effect-postgres/migrator.d.ts
|
|
9
|
-
declare function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: EffectPgDatabase<TSchema, TRelations>, config: MigrationConfig): effect_Effect0.Effect<undefined,
|
|
9
|
+
declare function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: EffectPgDatabase<TSchema, TRelations>, config: MigrationConfig): effect_Effect0.Effect<undefined, EffectDrizzleQueryError | _effect_sql_SqlError0.SqlError | MigratorInitError, never>;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { migrate };
|
|
12
12
|
//# sourceMappingURL=migrator.d.ts.map
|
|
@@ -19,6 +19,9 @@ var MsSqlReal = class extends require_mssql_core_columns_common.MsSqlColumnWithI
|
|
|
19
19
|
getSQLType() {
|
|
20
20
|
return "real";
|
|
21
21
|
}
|
|
22
|
+
mapFromDriverValue(value) {
|
|
23
|
+
return parseFloat(value.toPrecision(7));
|
|
24
|
+
}
|
|
22
25
|
};
|
|
23
26
|
function real(name) {
|
|
24
27
|
return new MsSqlRealBuilder(name ?? "");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"real.cjs","names":["MsSqlColumnBuilderWithIdentity","entityKind","MsSqlColumnWithIdentity"],"sources":["../../../src/mssql-core/columns/real.ts"],"sourcesContent":["import type { ColumnBaseConfig } from '~/column.ts';\nimport { entityKind } from '~/entity.ts';\nimport type { AnyMsSqlTable } from '~/mssql-core/table.ts';\nimport { MsSqlColumnBuilderWithIdentity, MsSqlColumnWithIdentity } from './common.ts';\n\nexport class MsSqlRealBuilder extends MsSqlColumnBuilderWithIdentity<{\n\tdataType: 'number float';\n\tdata: number;\n\tdriverParam: number;\n}> {\n\tstatic override readonly [entityKind]: string = 'MsSqlRealBuilder';\n\n\tconstructor(name: string) {\n\t\tsuper(name, 'number float', 'MsSqlReal');\n\t}\n\n\t/** @internal */\n\toverride build<TTableName extends string>(\n\t\ttable: AnyMsSqlTable<{ name: TTableName }>,\n\t) {\n\t\treturn new MsSqlReal(table, this.config);\n\t}\n}\n\nexport class MsSqlReal<T extends ColumnBaseConfig<'number float'>> extends MsSqlColumnWithIdentity<T> {\n\tstatic override readonly [entityKind]: string = 'MsSqlReal';\n\n\tgetSQLType(): string {\n\t\treturn 'real';\n\t}\n}\n\nexport function real(name?: string) {\n\treturn new MsSqlRealBuilder(name ?? '');\n}\n"],"mappings":";;;;;;AAKA,IAAa,mBAAb,cAAsCA,iEAInC;CACF,QAA0BC,0BAAsB;CAEhD,YAAY,MAAc;AACzB,QAAM,MAAM,gBAAgB,YAAY;;;CAIzC,AAAS,MACR,OACC;AACD,SAAO,IAAI,UAAU,OAAO,KAAK,OAAO;;;AAI1C,IAAa,YAAb,cAA2EC,0DAA2B;CACrG,QAA0BD,0BAAsB;CAEhD,aAAqB;AACpB,SAAO;;;
|
|
1
|
+
{"version":3,"file":"real.cjs","names":["MsSqlColumnBuilderWithIdentity","entityKind","MsSqlColumnWithIdentity"],"sources":["../../../src/mssql-core/columns/real.ts"],"sourcesContent":["import type { ColumnBaseConfig } from '~/column.ts';\nimport { entityKind } from '~/entity.ts';\nimport type { AnyMsSqlTable } from '~/mssql-core/table.ts';\nimport { MsSqlColumnBuilderWithIdentity, MsSqlColumnWithIdentity } from './common.ts';\n\nexport class MsSqlRealBuilder extends MsSqlColumnBuilderWithIdentity<{\n\tdataType: 'number float';\n\tdata: number;\n\tdriverParam: number;\n}> {\n\tstatic override readonly [entityKind]: string = 'MsSqlRealBuilder';\n\n\tconstructor(name: string) {\n\t\tsuper(name, 'number float', 'MsSqlReal');\n\t}\n\n\t/** @internal */\n\toverride build<TTableName extends string>(\n\t\ttable: AnyMsSqlTable<{ name: TTableName }>,\n\t) {\n\t\treturn new MsSqlReal(table, this.config);\n\t}\n}\n\nexport class MsSqlReal<T extends ColumnBaseConfig<'number float'>> extends MsSqlColumnWithIdentity<T> {\n\tstatic override readonly [entityKind]: string = 'MsSqlReal';\n\n\tgetSQLType(): string {\n\t\treturn 'real';\n\t}\n\n\toverride mapFromDriverValue(value: number): number {\n\t\treturn parseFloat(value.toPrecision(7));\n\t}\n}\n\nexport function real(name?: string) {\n\treturn new MsSqlRealBuilder(name ?? '');\n}\n"],"mappings":";;;;;;AAKA,IAAa,mBAAb,cAAsCA,iEAInC;CACF,QAA0BC,0BAAsB;CAEhD,YAAY,MAAc;AACzB,QAAM,MAAM,gBAAgB,YAAY;;;CAIzC,AAAS,MACR,OACC;AACD,SAAO,IAAI,UAAU,OAAO,KAAK,OAAO;;;AAI1C,IAAa,YAAb,cAA2EC,0DAA2B;CACrG,QAA0BD,0BAAsB;CAEhD,aAAqB;AACpB,SAAO;;CAGR,AAAS,mBAAmB,OAAuB;AAClD,SAAO,WAAW,MAAM,YAAY,EAAE,CAAC;;;AAIzC,SAAgB,KAAK,MAAe;AACnC,QAAO,IAAI,iBAAiB,QAAQ,GAAG"}
|
|
@@ -14,6 +14,7 @@ declare class MsSqlRealBuilder extends MsSqlColumnBuilderWithIdentity<{
|
|
|
14
14
|
declare class MsSqlReal<T extends ColumnBaseConfig<'number float'>> extends MsSqlColumnWithIdentity<T> {
|
|
15
15
|
static readonly [entityKind]: string;
|
|
16
16
|
getSQLType(): string;
|
|
17
|
+
mapFromDriverValue(value: number): number;
|
|
17
18
|
}
|
|
18
19
|
declare function real(name?: string): MsSqlRealBuilder;
|
|
19
20
|
//#endregion
|
|
@@ -14,6 +14,7 @@ declare class MsSqlRealBuilder extends MsSqlColumnBuilderWithIdentity<{
|
|
|
14
14
|
declare class MsSqlReal<T extends ColumnBaseConfig<'number float'>> extends MsSqlColumnWithIdentity<T> {
|
|
15
15
|
static readonly [entityKind]: string;
|
|
16
16
|
getSQLType(): string;
|
|
17
|
+
mapFromDriverValue(value: number): number;
|
|
17
18
|
}
|
|
18
19
|
declare function real(name?: string): MsSqlRealBuilder;
|
|
19
20
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"real.js","names":[],"sources":["../../../src/mssql-core/columns/real.ts"],"sourcesContent":["import type { ColumnBaseConfig } from '~/column.ts';\nimport { entityKind } from '~/entity.ts';\nimport type { AnyMsSqlTable } from '~/mssql-core/table.ts';\nimport { MsSqlColumnBuilderWithIdentity, MsSqlColumnWithIdentity } from './common.ts';\n\nexport class MsSqlRealBuilder extends MsSqlColumnBuilderWithIdentity<{\n\tdataType: 'number float';\n\tdata: number;\n\tdriverParam: number;\n}> {\n\tstatic override readonly [entityKind]: string = 'MsSqlRealBuilder';\n\n\tconstructor(name: string) {\n\t\tsuper(name, 'number float', 'MsSqlReal');\n\t}\n\n\t/** @internal */\n\toverride build<TTableName extends string>(\n\t\ttable: AnyMsSqlTable<{ name: TTableName }>,\n\t) {\n\t\treturn new MsSqlReal(table, this.config);\n\t}\n}\n\nexport class MsSqlReal<T extends ColumnBaseConfig<'number float'>> extends MsSqlColumnWithIdentity<T> {\n\tstatic override readonly [entityKind]: string = 'MsSqlReal';\n\n\tgetSQLType(): string {\n\t\treturn 'real';\n\t}\n}\n\nexport function real(name?: string) {\n\treturn new MsSqlRealBuilder(name ?? '');\n}\n"],"mappings":";;;;AAKA,IAAa,mBAAb,cAAsC,+BAInC;CACF,QAA0B,cAAsB;CAEhD,YAAY,MAAc;AACzB,QAAM,MAAM,gBAAgB,YAAY;;;CAIzC,AAAS,MACR,OACC;AACD,SAAO,IAAI,UAAU,OAAO,KAAK,OAAO;;;AAI1C,IAAa,YAAb,cAA2E,wBAA2B;CACrG,QAA0B,cAAsB;CAEhD,aAAqB;AACpB,SAAO;;;
|
|
1
|
+
{"version":3,"file":"real.js","names":[],"sources":["../../../src/mssql-core/columns/real.ts"],"sourcesContent":["import type { ColumnBaseConfig } from '~/column.ts';\nimport { entityKind } from '~/entity.ts';\nimport type { AnyMsSqlTable } from '~/mssql-core/table.ts';\nimport { MsSqlColumnBuilderWithIdentity, MsSqlColumnWithIdentity } from './common.ts';\n\nexport class MsSqlRealBuilder extends MsSqlColumnBuilderWithIdentity<{\n\tdataType: 'number float';\n\tdata: number;\n\tdriverParam: number;\n}> {\n\tstatic override readonly [entityKind]: string = 'MsSqlRealBuilder';\n\n\tconstructor(name: string) {\n\t\tsuper(name, 'number float', 'MsSqlReal');\n\t}\n\n\t/** @internal */\n\toverride build<TTableName extends string>(\n\t\ttable: AnyMsSqlTable<{ name: TTableName }>,\n\t) {\n\t\treturn new MsSqlReal(table, this.config);\n\t}\n}\n\nexport class MsSqlReal<T extends ColumnBaseConfig<'number float'>> extends MsSqlColumnWithIdentity<T> {\n\tstatic override readonly [entityKind]: string = 'MsSqlReal';\n\n\tgetSQLType(): string {\n\t\treturn 'real';\n\t}\n\n\toverride mapFromDriverValue(value: number): number {\n\t\treturn parseFloat(value.toPrecision(7));\n\t}\n}\n\nexport function real(name?: string) {\n\treturn new MsSqlRealBuilder(name ?? '');\n}\n"],"mappings":";;;;AAKA,IAAa,mBAAb,cAAsC,+BAInC;CACF,QAA0B,cAAsB;CAEhD,YAAY,MAAc;AACzB,QAAM,MAAM,gBAAgB,YAAY;;;CAIzC,AAAS,MACR,OACC;AACD,SAAO,IAAI,UAAU,OAAO,KAAK,OAAO;;;AAI1C,IAAa,YAAb,cAA2E,wBAA2B;CACrG,QAA0B,cAAsB;CAEhD,aAAqB;AACpB,SAAO;;CAGR,AAAS,mBAAmB,OAAuB;AAClD,SAAO,WAAW,MAAM,YAAY,EAAE,CAAC;;;AAIzC,SAAgB,KAAK,MAAe;AACnC,QAAO,IAAI,iBAAiB,QAAQ,GAAG"}
|
package/package.cjs
CHANGED
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-orm",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.22-1b8fcb2",
|
|
4
4
|
"description": "Drizzle ORM package for SQL databases",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -1438,6 +1438,18 @@
|
|
|
1438
1438
|
"types": "./sqlite-proxy/index.d.ts",
|
|
1439
1439
|
"default": "./sqlite-proxy/index.js"
|
|
1440
1440
|
},
|
|
1441
|
+
"./sqlite-proxy/migrator.internal": {
|
|
1442
|
+
"import": {
|
|
1443
|
+
"types": "./sqlite-proxy/migrator.internal.d.ts",
|
|
1444
|
+
"default": "./sqlite-proxy/migrator.internal.js"
|
|
1445
|
+
},
|
|
1446
|
+
"require": {
|
|
1447
|
+
"types": "./sqlite-proxy/migrator.internal.d.cts",
|
|
1448
|
+
"default": "./sqlite-proxy/migrator.internal.cjs"
|
|
1449
|
+
},
|
|
1450
|
+
"types": "./sqlite-proxy/migrator.internal.d.ts",
|
|
1451
|
+
"default": "./sqlite-proxy/migrator.internal.js"
|
|
1452
|
+
},
|
|
1441
1453
|
"./sqlite-proxy/migrator": {
|
|
1442
1454
|
"import": {
|
|
1443
1455
|
"types": "./sqlite-proxy/migrator.d.ts",
|
|
@@ -56,7 +56,7 @@ declare abstract class PgEffectTransaction<TEffectHKT extends QueryEffectHKTBase
|
|
|
56
56
|
rollback(): EffectTransactionRollbackError;
|
|
57
57
|
setTransaction(config: PgTransactionConfig): QueryEffectKind<TEffectHKT, void>;
|
|
58
58
|
}
|
|
59
|
-
declare const migrate: <TEffectHKT extends QueryEffectHKTBase>(migrations: MigrationMeta[], session: PgEffectSession<TEffectHKT, PgQueryResultHKT, Record<string, never>, EmptyRelations, V1.ExtractTablesWithRelations<Record<string, never>>>, config: string | MigrationConfig) => Effect.Effect<undefined, MigratorInitError | TEffectHKT["error"]
|
|
59
|
+
declare const migrate: <TEffectHKT extends QueryEffectHKTBase>(migrations: MigrationMeta[], session: PgEffectSession<TEffectHKT, PgQueryResultHKT, Record<string, never>, EmptyRelations, V1.ExtractTablesWithRelations<Record<string, never>>>, config: string | MigrationConfig) => Effect.Effect<undefined, SqlError | MigratorInitError | TEffectHKT["error"], TEffectHKT["context"]>;
|
|
60
60
|
//#endregion
|
|
61
61
|
export { PgEffectPreparedQuery, PgEffectSession, PgEffectTransaction, migrate };
|
|
62
62
|
//# sourceMappingURL=session.d.cts.map
|
|
@@ -56,7 +56,7 @@ declare abstract class PgEffectTransaction<TEffectHKT extends QueryEffectHKTBase
|
|
|
56
56
|
rollback(): EffectTransactionRollbackError;
|
|
57
57
|
setTransaction(config: PgTransactionConfig): QueryEffectKind<TEffectHKT, void>;
|
|
58
58
|
}
|
|
59
|
-
declare const migrate: <TEffectHKT extends QueryEffectHKTBase>(migrations: MigrationMeta[], session: PgEffectSession<TEffectHKT, PgQueryResultHKT, Record<string, never>, EmptyRelations, V1.ExtractTablesWithRelations<Record<string, never>>>, config: string | MigrationConfig) => Effect.Effect<undefined, MigratorInitError | TEffectHKT["error"]
|
|
59
|
+
declare const migrate: <TEffectHKT extends QueryEffectHKTBase>(migrations: MigrationMeta[], session: PgEffectSession<TEffectHKT, PgQueryResultHKT, Record<string, never>, EmptyRelations, V1.ExtractTablesWithRelations<Record<string, never>>>, config: string | MigrationConfig) => Effect.Effect<undefined, SqlError | MigratorInitError | TEffectHKT["error"], TEffectHKT["context"]>;
|
|
60
60
|
//#endregion
|
|
61
61
|
export { PgEffectPreparedQuery, PgEffectSession, PgEffectTransaction, migrate };
|
|
62
62
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1,47 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const
|
|
3
|
-
let __sql_sql_ts = require("../sql/sql.cjs");
|
|
4
|
-
let __migrator_ts = require("../migrator.cjs");
|
|
5
|
-
let __migrator_utils_ts = require("../migrator.utils.cjs");
|
|
6
|
-
let __up_migrations_sqlite_ts = require("../up-migrations/sqlite.cjs");
|
|
2
|
+
const require_sqlite_proxy_migrator_internal = require('./migrator.internal.cjs');
|
|
7
3
|
|
|
8
4
|
//#region src/sqlite-proxy/migrator.ts
|
|
9
5
|
async function migrate(db, callback, config) {
|
|
10
|
-
|
|
11
|
-
const migrationsTable = typeof config === "string" ? "__drizzle_migrations" : config.migrationsTable ?? "__drizzle_migrations";
|
|
12
|
-
const { newDb } = await (0, __up_migrations_sqlite_ts.upgradeAsyncIfNeeded)(migrationsTable, db.session, migrations);
|
|
13
|
-
if (newDb) {
|
|
14
|
-
const migrationTableCreate = __sql_sql_ts.sql`
|
|
15
|
-
CREATE TABLE IF NOT EXISTS ${__sql_sql_ts.sql.identifier(migrationsTable)} (
|
|
16
|
-
id INTEGER PRIMARY KEY,
|
|
17
|
-
hash text NOT NULL,
|
|
18
|
-
created_at numeric,
|
|
19
|
-
name text,
|
|
20
|
-
applied_at TEXT
|
|
21
|
-
);`;
|
|
22
|
-
await db.run(migrationTableCreate);
|
|
23
|
-
}
|
|
24
|
-
const dbMigrations = (await db.values(__sql_sql_ts.sql`SELECT id, hash, created_at, name FROM ${__sql_sql_ts.sql.identifier(migrationsTable)}`)).map(([id, hash, created_at, name]) => ({
|
|
25
|
-
id,
|
|
26
|
-
hash,
|
|
27
|
-
created_at,
|
|
28
|
-
name
|
|
29
|
-
}));
|
|
30
|
-
if (typeof config === "object" && config.init) {
|
|
31
|
-
if (dbMigrations.length) return { exitCode: "databaseMigrations" };
|
|
32
|
-
if (migrations.length > 1) return { exitCode: "localMigrations" };
|
|
33
|
-
const [migration] = migrations;
|
|
34
|
-
if (!migration) return;
|
|
35
|
-
await callback([db.dialect.sqlToQuery(__sql_sql_ts.sql`insert into ${__sql_sql_ts.sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql]);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const migrationsToRun = (0, __migrator_utils_ts.getMigrationsToRun)({
|
|
39
|
-
localMigrations: migrations,
|
|
40
|
-
dbMigrations
|
|
41
|
-
});
|
|
42
|
-
const queriesToRun = [];
|
|
43
|
-
for (const migration of migrationsToRun) queriesToRun.push(...migration.sql, db.dialect.sqlToQuery(__sql_sql_ts.sql`insert into ${__sql_sql_ts.sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql);
|
|
44
|
-
await callback(queriesToRun);
|
|
6
|
+
return require_sqlite_proxy_migrator_internal.migrateInternal(db, callback, config, "transaction");
|
|
45
7
|
}
|
|
46
8
|
|
|
47
9
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrator.cjs","names":["
|
|
1
|
+
{"version":3,"file":"migrator.cjs","names":["migrateInternal"],"sources":["../../src/sqlite-proxy/migrator.ts"],"sourcesContent":["import type { MigrationConfig } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport type { SqliteRemoteDatabase } from './driver.ts';\nimport { migrateInternal } from './migrator.internal.ts';\n\nexport type ProxyMigrator = (migrationQueries: string[]) => Promise<void>;\n\nexport async function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(\n\tdb: SqliteRemoteDatabase<TSchema, TRelations>,\n\tcallback: ProxyMigrator,\n\tconfig: MigrationConfig,\n) {\n\treturn migrateInternal(db, callback, config, 'transaction');\n}\n"],"mappings":";;;;AAOA,eAAsB,QACrB,IACA,UACA,QACC;AACD,QAAOA,uDAAgB,IAAI,UAAU,QAAQ,cAAc"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let __sql_sql_ts = require("../sql/sql.cjs");
|
|
4
|
+
let __migrator_ts = require("../migrator.cjs");
|
|
5
|
+
let __migrator_utils_ts = require("../migrator.utils.cjs");
|
|
6
|
+
let __up_migrations_sqlite_ts = require("../up-migrations/sqlite.cjs");
|
|
7
|
+
|
|
8
|
+
//#region src/sqlite-proxy/migrator.internal.ts
|
|
9
|
+
/**
|
|
10
|
+
* - `migrate` - Public API for end users. Uses 'transaction' mode by default
|
|
11
|
+
* - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter
|
|
12
|
+
*
|
|
13
|
+
* Why `mode` parameter exists:
|
|
14
|
+
* - 'transaction':
|
|
15
|
+
* Used by normal sqlite proxy driver
|
|
16
|
+
*
|
|
17
|
+
* - 'run':
|
|
18
|
+
* Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements
|
|
19
|
+
* @see "drizzle-kit/src/cli/connections.ts"
|
|
20
|
+
*/
|
|
21
|
+
async function migrateInternal(db, callback, config, mode) {
|
|
22
|
+
const migrations = (0, __migrator_ts.readMigrationFiles)(config);
|
|
23
|
+
const migrationsTable = typeof config === "string" ? "__drizzle_migrations" : config.migrationsTable ?? "__drizzle_migrations";
|
|
24
|
+
const { newDb } = await (0, __up_migrations_sqlite_ts.upgradeAsyncIfNeeded)(migrationsTable, db.session, migrations, mode);
|
|
25
|
+
if (newDb) {
|
|
26
|
+
const migrationTableCreate = __sql_sql_ts.sql`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS ${__sql_sql_ts.sql.identifier(migrationsTable)} (
|
|
28
|
+
id INTEGER PRIMARY KEY,
|
|
29
|
+
hash text NOT NULL,
|
|
30
|
+
created_at numeric,
|
|
31
|
+
name text,
|
|
32
|
+
applied_at TEXT
|
|
33
|
+
);`;
|
|
34
|
+
await db.run(migrationTableCreate);
|
|
35
|
+
}
|
|
36
|
+
const dbMigrations = (await db.values(__sql_sql_ts.sql`SELECT id, hash, created_at, name FROM ${__sql_sql_ts.sql.identifier(migrationsTable)}`)).map(([id, hash, created_at, name]) => ({
|
|
37
|
+
id,
|
|
38
|
+
hash,
|
|
39
|
+
created_at,
|
|
40
|
+
name
|
|
41
|
+
}));
|
|
42
|
+
if (typeof config === "object" && config.init) {
|
|
43
|
+
if (dbMigrations.length) return { exitCode: "databaseMigrations" };
|
|
44
|
+
if (migrations.length > 1) return { exitCode: "localMigrations" };
|
|
45
|
+
const [migration] = migrations;
|
|
46
|
+
if (!migration) return;
|
|
47
|
+
await callback([db.dialect.sqlToQuery(__sql_sql_ts.sql`insert into ${__sql_sql_ts.sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql]);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const migrationsToRun = (0, __migrator_utils_ts.getMigrationsToRun)({
|
|
51
|
+
localMigrations: migrations,
|
|
52
|
+
dbMigrations
|
|
53
|
+
});
|
|
54
|
+
const queriesToRun = [];
|
|
55
|
+
for (const migration of migrationsToRun) queriesToRun.push(...migration.sql, db.dialect.sqlToQuery(__sql_sql_ts.sql`insert into ${__sql_sql_ts.sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql);
|
|
56
|
+
await callback(queriesToRun);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
exports.migrateInternal = migrateInternal;
|
|
61
|
+
//# sourceMappingURL=migrator.internal.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.internal.cjs","names":["sql"],"sources":["../../src/sqlite-proxy/migrator.internal.ts"],"sourcesContent":["import type { MigrationConfig } from '~/migrator.ts';\nimport { readMigrationFiles } from '~/migrator.ts';\nimport { getMigrationsToRun } from '~/migrator.utils.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { sql } from '~/sql/sql.ts';\nimport { upgradeAsyncIfNeeded } from '~/up-migrations/sqlite.ts';\nimport type { SqliteRemoteDatabase } from './driver.ts';\nimport type { ProxyMigrator } from './migrator.ts';\n\n/**\n * - `migrate` - Public API for end users. Uses 'transaction' mode by default\n * - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter\n *\n * Why `mode` parameter exists:\n * - 'transaction':\n * Used by normal sqlite proxy driver\n *\n * - 'run':\n * Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements\n * @see \"drizzle-kit/src/cli/connections.ts\"\n */\nexport async function migrateInternal<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(\n\tdb: SqliteRemoteDatabase<TSchema, TRelations>,\n\tcallback: ProxyMigrator,\n\tconfig: MigrationConfig,\n\tmode: 'transaction' | 'run',\n) {\n\tconst migrations = readMigrationFiles(config);\n\n\tconst migrationsTable = typeof config === 'string'\n\t\t? '__drizzle_migrations'\n\t\t: config.migrationsTable ?? '__drizzle_migrations';\n\n\t// Detect DB version and upgrade table schema if needed\n\tconst { newDb } = await upgradeAsyncIfNeeded(migrationsTable, db.session, migrations, mode);\n\n\tif (newDb) {\n\t\tconst migrationTableCreate = sql`\n\t\tCREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\thash text NOT NULL,\n\t\t\tcreated_at numeric,\n\t\t\tname text,\n\t\t\tapplied_at TEXT\n\t\t);`;\n\n\t\tawait db.run(migrationTableCreate);\n\t}\n\n\tconst dbMigrations = (await db.values<[number, string, string, string | null]>(\n\t\tsql`SELECT id, hash, created_at, name FROM ${sql.identifier(migrationsTable)}`,\n\t)).map(([id, hash, created_at, name]) => ({ id, hash, created_at, name }));\n\n\tif (typeof config === 'object' && config.init) {\n\t\tif (dbMigrations.length) {\n\t\t\treturn { exitCode: 'databaseMigrations' as const };\n\t\t}\n\n\t\tif (migrations.length > 1) {\n\t\t\treturn { exitCode: 'localMigrations' as const };\n\t\t}\n\n\t\tconst [migration] = migrations;\n\n\t\tif (!migration) return;\n\n\t\tawait callback(\n\t\t\t[\n\t\t\t\tdb.dialect.sqlToQuery(\n\t\t\t\t\tsql`insert into ${\n\t\t\t\t\t\tsql.identifier(migrationsTable)\n\t\t\t\t\t} (\"hash\", \"created_at\", \"name\", \"applied_at\") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${\n\t\t\t\t\t\tnew Date().toISOString()\n\t\t\t\t\t})`\n\t\t\t\t\t\t.inlineParams(),\n\t\t\t\t).sql,\n\t\t\t],\n\t\t);\n\n\t\treturn;\n\t}\n\n\tconst migrationsToRun = getMigrationsToRun({ localMigrations: migrations, dbMigrations });\n\tconst queriesToRun: string[] = [];\n\tfor (const migration of migrationsToRun) {\n\t\tqueriesToRun.push(\n\t\t\t...migration.sql,\n\t\t\tdb.dialect.sqlToQuery(\n\t\t\t\tsql`insert into ${\n\t\t\t\t\tsql.identifier(migrationsTable)\n\t\t\t\t} (\"hash\", \"created_at\", \"name\", \"applied_at\") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${\n\t\t\t\t\tnew Date().toISOString()\n\t\t\t\t})`\n\t\t\t\t\t.inlineParams(),\n\t\t\t).sql,\n\t\t);\n\t}\n\n\tawait callback(queriesToRun);\n\n\treturn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,gBACrB,IACA,UACA,QACA,MACC;CACD,MAAM,mDAAgC,OAAO;CAE7C,MAAM,kBAAkB,OAAO,WAAW,WACvC,yBACA,OAAO,mBAAmB;CAG7B,MAAM,EAAE,UAAU,0DAA2B,iBAAiB,GAAG,SAAS,YAAY,KAAK;AAE3F,KAAI,OAAO;EACV,MAAM,uBAAuB,gBAAG;+BACHA,iBAAI,WAAW,gBAAgB,CAAC;;;;;;;AAQ7D,QAAM,GAAG,IAAI,qBAAqB;;CAGnC,MAAM,gBAAgB,MAAM,GAAG,OAC9B,gBAAG,0CAA0CA,iBAAI,WAAW,gBAAgB,GAC5E,EAAE,KAAK,CAAC,IAAI,MAAM,YAAY,WAAW;EAAE;EAAI;EAAM;EAAY;EAAM,EAAE;AAE1E,KAAI,OAAO,WAAW,YAAY,OAAO,MAAM;AAC9C,MAAI,aAAa,OAChB,QAAO,EAAE,UAAU,sBAA+B;AAGnD,MAAI,WAAW,SAAS,EACvB,QAAO,EAAE,UAAU,mBAA4B;EAGhD,MAAM,CAAC,aAAa;AAEpB,MAAI,CAAC,UAAW;AAEhB,QAAM,SACL,CACC,GAAG,QAAQ,WACV,gBAAG,eACFA,iBAAI,WAAW,gBAAgB,CAC/B,uDAAuD,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,qBACpH,IAAI,MAAM,EAAC,aAAa,CACxB,GACC,cAAc,CAChB,CAAC,IACF,CACD;AAED;;CAGD,MAAM,8DAAqC;EAAE,iBAAiB;EAAY;EAAc,CAAC;CACzF,MAAM,eAAyB,EAAE;AACjC,MAAK,MAAM,aAAa,gBACvB,cAAa,KACZ,GAAG,UAAU,KACb,GAAG,QAAQ,WACV,gBAAG,eACFA,iBAAI,WAAW,gBAAgB,CAC/B,uDAAuD,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,qBACpH,IAAI,MAAM,EAAC,aAAa,CACxB,GACC,cAAc,CAChB,CAAC,IACF;AAGF,OAAM,SAAS,aAAa"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SqliteRemoteDatabase } from "./driver.cjs";
|
|
2
|
+
import { ProxyMigrator } from "./migrator.cjs";
|
|
3
|
+
import { MigrationConfig } from "../migrator.cjs";
|
|
4
|
+
import { AnyRelations } from "../relations.cjs";
|
|
5
|
+
|
|
6
|
+
//#region src/sqlite-proxy/migrator.internal.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* - `migrate` - Public API for end users. Uses 'transaction' mode by default
|
|
9
|
+
* - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter
|
|
10
|
+
*
|
|
11
|
+
* Why `mode` parameter exists:
|
|
12
|
+
* - 'transaction':
|
|
13
|
+
* Used by normal sqlite proxy driver
|
|
14
|
+
*
|
|
15
|
+
* - 'run':
|
|
16
|
+
* Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements
|
|
17
|
+
* @see "drizzle-kit/src/cli/connections.ts"
|
|
18
|
+
*/
|
|
19
|
+
declare function migrateInternal<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: SqliteRemoteDatabase<TSchema, TRelations>, callback: ProxyMigrator, config: MigrationConfig, mode: 'transaction' | 'run'): Promise<{
|
|
20
|
+
exitCode: "databaseMigrations";
|
|
21
|
+
} | {
|
|
22
|
+
exitCode: "localMigrations";
|
|
23
|
+
} | undefined>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { migrateInternal };
|
|
26
|
+
//# sourceMappingURL=migrator.internal.d.cts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SqliteRemoteDatabase } from "./driver.js";
|
|
2
|
+
import { ProxyMigrator } from "./migrator.js";
|
|
3
|
+
import { MigrationConfig } from "../migrator.js";
|
|
4
|
+
import { AnyRelations } from "../relations.js";
|
|
5
|
+
|
|
6
|
+
//#region src/sqlite-proxy/migrator.internal.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* - `migrate` - Public API for end users. Uses 'transaction' mode by default
|
|
9
|
+
* - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter
|
|
10
|
+
*
|
|
11
|
+
* Why `mode` parameter exists:
|
|
12
|
+
* - 'transaction':
|
|
13
|
+
* Used by normal sqlite proxy driver
|
|
14
|
+
*
|
|
15
|
+
* - 'run':
|
|
16
|
+
* Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements
|
|
17
|
+
* @see "drizzle-kit/src/cli/connections.ts"
|
|
18
|
+
*/
|
|
19
|
+
declare function migrateInternal<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: SqliteRemoteDatabase<TSchema, TRelations>, callback: ProxyMigrator, config: MigrationConfig, mode: 'transaction' | 'run'): Promise<{
|
|
20
|
+
exitCode: "databaseMigrations";
|
|
21
|
+
} | {
|
|
22
|
+
exitCode: "localMigrations";
|
|
23
|
+
} | undefined>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { migrateInternal };
|
|
26
|
+
//# sourceMappingURL=migrator.internal.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { sql } from "../sql/sql.js";
|
|
2
|
+
import { readMigrationFiles } from "../migrator.js";
|
|
3
|
+
import { getMigrationsToRun } from "../migrator.utils.js";
|
|
4
|
+
import { upgradeAsyncIfNeeded } from "../up-migrations/sqlite.js";
|
|
5
|
+
|
|
6
|
+
//#region src/sqlite-proxy/migrator.internal.ts
|
|
7
|
+
/**
|
|
8
|
+
* - `migrate` - Public API for end users. Uses 'transaction' mode by default
|
|
9
|
+
* - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter
|
|
10
|
+
*
|
|
11
|
+
* Why `mode` parameter exists:
|
|
12
|
+
* - 'transaction':
|
|
13
|
+
* Used by normal sqlite proxy driver
|
|
14
|
+
*
|
|
15
|
+
* - 'run':
|
|
16
|
+
* Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements
|
|
17
|
+
* @see "drizzle-kit/src/cli/connections.ts"
|
|
18
|
+
*/
|
|
19
|
+
async function migrateInternal(db, callback, config, mode) {
|
|
20
|
+
const migrations = readMigrationFiles(config);
|
|
21
|
+
const migrationsTable = typeof config === "string" ? "__drizzle_migrations" : config.migrationsTable ?? "__drizzle_migrations";
|
|
22
|
+
const { newDb } = await upgradeAsyncIfNeeded(migrationsTable, db.session, migrations, mode);
|
|
23
|
+
if (newDb) {
|
|
24
|
+
const migrationTableCreate = sql`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
|
|
26
|
+
id INTEGER PRIMARY KEY,
|
|
27
|
+
hash text NOT NULL,
|
|
28
|
+
created_at numeric,
|
|
29
|
+
name text,
|
|
30
|
+
applied_at TEXT
|
|
31
|
+
);`;
|
|
32
|
+
await db.run(migrationTableCreate);
|
|
33
|
+
}
|
|
34
|
+
const dbMigrations = (await db.values(sql`SELECT id, hash, created_at, name FROM ${sql.identifier(migrationsTable)}`)).map(([id, hash, created_at, name]) => ({
|
|
35
|
+
id,
|
|
36
|
+
hash,
|
|
37
|
+
created_at,
|
|
38
|
+
name
|
|
39
|
+
}));
|
|
40
|
+
if (typeof config === "object" && config.init) {
|
|
41
|
+
if (dbMigrations.length) return { exitCode: "databaseMigrations" };
|
|
42
|
+
if (migrations.length > 1) return { exitCode: "localMigrations" };
|
|
43
|
+
const [migration] = migrations;
|
|
44
|
+
if (!migration) return;
|
|
45
|
+
await callback([db.dialect.sqlToQuery(sql`insert into ${sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql]);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const migrationsToRun = getMigrationsToRun({
|
|
49
|
+
localMigrations: migrations,
|
|
50
|
+
dbMigrations
|
|
51
|
+
});
|
|
52
|
+
const queriesToRun = [];
|
|
53
|
+
for (const migration of migrationsToRun) queriesToRun.push(...migration.sql, db.dialect.sqlToQuery(sql`insert into ${sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql);
|
|
54
|
+
await callback(queriesToRun);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { migrateInternal };
|
|
59
|
+
//# sourceMappingURL=migrator.internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.internal.js","names":[],"sources":["../../src/sqlite-proxy/migrator.internal.ts"],"sourcesContent":["import type { MigrationConfig } from '~/migrator.ts';\nimport { readMigrationFiles } from '~/migrator.ts';\nimport { getMigrationsToRun } from '~/migrator.utils.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { sql } from '~/sql/sql.ts';\nimport { upgradeAsyncIfNeeded } from '~/up-migrations/sqlite.ts';\nimport type { SqliteRemoteDatabase } from './driver.ts';\nimport type { ProxyMigrator } from './migrator.ts';\n\n/**\n * - `migrate` - Public API for end users. Uses 'transaction' mode by default\n * - `migrateInternal` - Internal API used by drizzle-kit. Accepts a `mode` parameter\n *\n * Why `mode` parameter exists:\n * - 'transaction':\n * Used by normal sqlite proxy driver\n *\n * - 'run':\n * Executes statements individually without transaction. Required for Drizzle Kit D1 migrate, which uses sqlite proxy to run statements\n * @see \"drizzle-kit/src/cli/connections.ts\"\n */\nexport async function migrateInternal<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(\n\tdb: SqliteRemoteDatabase<TSchema, TRelations>,\n\tcallback: ProxyMigrator,\n\tconfig: MigrationConfig,\n\tmode: 'transaction' | 'run',\n) {\n\tconst migrations = readMigrationFiles(config);\n\n\tconst migrationsTable = typeof config === 'string'\n\t\t? '__drizzle_migrations'\n\t\t: config.migrationsTable ?? '__drizzle_migrations';\n\n\t// Detect DB version and upgrade table schema if needed\n\tconst { newDb } = await upgradeAsyncIfNeeded(migrationsTable, db.session, migrations, mode);\n\n\tif (newDb) {\n\t\tconst migrationTableCreate = sql`\n\t\tCREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (\n\t\t\tid INTEGER PRIMARY KEY,\n\t\t\thash text NOT NULL,\n\t\t\tcreated_at numeric,\n\t\t\tname text,\n\t\t\tapplied_at TEXT\n\t\t);`;\n\n\t\tawait db.run(migrationTableCreate);\n\t}\n\n\tconst dbMigrations = (await db.values<[number, string, string, string | null]>(\n\t\tsql`SELECT id, hash, created_at, name FROM ${sql.identifier(migrationsTable)}`,\n\t)).map(([id, hash, created_at, name]) => ({ id, hash, created_at, name }));\n\n\tif (typeof config === 'object' && config.init) {\n\t\tif (dbMigrations.length) {\n\t\t\treturn { exitCode: 'databaseMigrations' as const };\n\t\t}\n\n\t\tif (migrations.length > 1) {\n\t\t\treturn { exitCode: 'localMigrations' as const };\n\t\t}\n\n\t\tconst [migration] = migrations;\n\n\t\tif (!migration) return;\n\n\t\tawait callback(\n\t\t\t[\n\t\t\t\tdb.dialect.sqlToQuery(\n\t\t\t\t\tsql`insert into ${\n\t\t\t\t\t\tsql.identifier(migrationsTable)\n\t\t\t\t\t} (\"hash\", \"created_at\", \"name\", \"applied_at\") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${\n\t\t\t\t\t\tnew Date().toISOString()\n\t\t\t\t\t})`\n\t\t\t\t\t\t.inlineParams(),\n\t\t\t\t).sql,\n\t\t\t],\n\t\t);\n\n\t\treturn;\n\t}\n\n\tconst migrationsToRun = getMigrationsToRun({ localMigrations: migrations, dbMigrations });\n\tconst queriesToRun: string[] = [];\n\tfor (const migration of migrationsToRun) {\n\t\tqueriesToRun.push(\n\t\t\t...migration.sql,\n\t\t\tdb.dialect.sqlToQuery(\n\t\t\t\tsql`insert into ${\n\t\t\t\t\tsql.identifier(migrationsTable)\n\t\t\t\t} (\"hash\", \"created_at\", \"name\", \"applied_at\") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${\n\t\t\t\t\tnew Date().toISOString()\n\t\t\t\t})`\n\t\t\t\t\t.inlineParams(),\n\t\t\t).sql,\n\t\t);\n\t}\n\n\tawait callback(queriesToRun);\n\n\treturn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,eAAsB,gBACrB,IACA,UACA,QACA,MACC;CACD,MAAM,aAAa,mBAAmB,OAAO;CAE7C,MAAM,kBAAkB,OAAO,WAAW,WACvC,yBACA,OAAO,mBAAmB;CAG7B,MAAM,EAAE,UAAU,MAAM,qBAAqB,iBAAiB,GAAG,SAAS,YAAY,KAAK;AAE3F,KAAI,OAAO;EACV,MAAM,uBAAuB,GAAG;+BACH,IAAI,WAAW,gBAAgB,CAAC;;;;;;;AAQ7D,QAAM,GAAG,IAAI,qBAAqB;;CAGnC,MAAM,gBAAgB,MAAM,GAAG,OAC9B,GAAG,0CAA0C,IAAI,WAAW,gBAAgB,GAC5E,EAAE,KAAK,CAAC,IAAI,MAAM,YAAY,WAAW;EAAE;EAAI;EAAM;EAAY;EAAM,EAAE;AAE1E,KAAI,OAAO,WAAW,YAAY,OAAO,MAAM;AAC9C,MAAI,aAAa,OAChB,QAAO,EAAE,UAAU,sBAA+B;AAGnD,MAAI,WAAW,SAAS,EACvB,QAAO,EAAE,UAAU,mBAA4B;EAGhD,MAAM,CAAC,aAAa;AAEpB,MAAI,CAAC,UAAW;AAEhB,QAAM,SACL,CACC,GAAG,QAAQ,WACV,GAAG,eACF,IAAI,WAAW,gBAAgB,CAC/B,uDAAuD,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,qBACpH,IAAI,MAAM,EAAC,aAAa,CACxB,GACC,cAAc,CAChB,CAAC,IACF,CACD;AAED;;CAGD,MAAM,kBAAkB,mBAAmB;EAAE,iBAAiB;EAAY;EAAc,CAAC;CACzF,MAAM,eAAyB,EAAE;AACjC,MAAK,MAAM,aAAa,gBACvB,cAAa,KACZ,GAAG,UAAU,KACb,GAAG,QAAQ,WACV,GAAG,eACF,IAAI,WAAW,gBAAgB,CAC/B,uDAAuD,UAAU,KAAK,IAAI,UAAU,aAAa,IAAI,UAAU,KAAK,qBACpH,IAAI,MAAM,EAAC,aAAa,CACxB,GACC,cAAc,CAChB,CAAC,IACF;AAGF,OAAM,SAAS,aAAa"}
|
package/sqlite-proxy/migrator.js
CHANGED
|
@@ -1,45 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readMigrationFiles } from "../migrator.js";
|
|
3
|
-
import { getMigrationsToRun } from "../migrator.utils.js";
|
|
4
|
-
import { upgradeAsyncIfNeeded } from "../up-migrations/sqlite.js";
|
|
1
|
+
import { migrateInternal } from "./migrator.internal.js";
|
|
5
2
|
|
|
6
3
|
//#region src/sqlite-proxy/migrator.ts
|
|
7
4
|
async function migrate(db, callback, config) {
|
|
8
|
-
|
|
9
|
-
const migrationsTable = typeof config === "string" ? "__drizzle_migrations" : config.migrationsTable ?? "__drizzle_migrations";
|
|
10
|
-
const { newDb } = await upgradeAsyncIfNeeded(migrationsTable, db.session, migrations);
|
|
11
|
-
if (newDb) {
|
|
12
|
-
const migrationTableCreate = sql`
|
|
13
|
-
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
|
|
14
|
-
id INTEGER PRIMARY KEY,
|
|
15
|
-
hash text NOT NULL,
|
|
16
|
-
created_at numeric,
|
|
17
|
-
name text,
|
|
18
|
-
applied_at TEXT
|
|
19
|
-
);`;
|
|
20
|
-
await db.run(migrationTableCreate);
|
|
21
|
-
}
|
|
22
|
-
const dbMigrations = (await db.values(sql`SELECT id, hash, created_at, name FROM ${sql.identifier(migrationsTable)}`)).map(([id, hash, created_at, name]) => ({
|
|
23
|
-
id,
|
|
24
|
-
hash,
|
|
25
|
-
created_at,
|
|
26
|
-
name
|
|
27
|
-
}));
|
|
28
|
-
if (typeof config === "object" && config.init) {
|
|
29
|
-
if (dbMigrations.length) return { exitCode: "databaseMigrations" };
|
|
30
|
-
if (migrations.length > 1) return { exitCode: "localMigrations" };
|
|
31
|
-
const [migration] = migrations;
|
|
32
|
-
if (!migration) return;
|
|
33
|
-
await callback([db.dialect.sqlToQuery(sql`insert into ${sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql]);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const migrationsToRun = getMigrationsToRun({
|
|
37
|
-
localMigrations: migrations,
|
|
38
|
-
dbMigrations
|
|
39
|
-
});
|
|
40
|
-
const queriesToRun = [];
|
|
41
|
-
for (const migration of migrationsToRun) queriesToRun.push(...migration.sql, db.dialect.sqlToQuery(sql`insert into ${sql.identifier(migrationsTable)} ("hash", "created_at", "name", "applied_at") values(${migration.hash}, ${migration.folderMillis}, ${migration.name}, ${(/* @__PURE__ */ new Date()).toISOString()})`.inlineParams()).sql);
|
|
42
|
-
await callback(queriesToRun);
|
|
5
|
+
return migrateInternal(db, callback, config, "transaction");
|
|
43
6
|
}
|
|
44
7
|
|
|
45
8
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrator.js","names":[],"sources":["../../src/sqlite-proxy/migrator.ts"],"sourcesContent":["import type { MigrationConfig } from '~/migrator.ts';\nimport
|
|
1
|
+
{"version":3,"file":"migrator.js","names":[],"sources":["../../src/sqlite-proxy/migrator.ts"],"sourcesContent":["import type { MigrationConfig } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport type { SqliteRemoteDatabase } from './driver.ts';\nimport { migrateInternal } from './migrator.internal.ts';\n\nexport type ProxyMigrator = (migrationQueries: string[]) => Promise<void>;\n\nexport async function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(\n\tdb: SqliteRemoteDatabase<TSchema, TRelations>,\n\tcallback: ProxyMigrator,\n\tconfig: MigrationConfig,\n) {\n\treturn migrateInternal(db, callback, config, 'transaction');\n}\n"],"mappings":";;;AAOA,eAAsB,QACrB,IACA,UACA,QACC;AACD,QAAO,gBAAgB,IAAI,UAAU,QAAQ,cAAc"}
|
package/up-migrations/sqlite.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let __utils_ts = require("../utils.cjs");
|
|
3
4
|
let __sql_sql_ts = require("../sql/sql.cjs");
|
|
4
5
|
|
|
5
6
|
//#region src/up-migrations/sqlite.ts
|
|
@@ -100,20 +101,20 @@ const upgradeSyncFunctions = { 0: (migrationsTable, session, localMigrations) =>
|
|
|
100
101
|
* Version 0: Original schema (id, hash, created_at)
|
|
101
102
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
102
103
|
*/
|
|
103
|
-
async function upgradeAsyncIfNeeded(migrationsTable, session, localMigrations) {
|
|
104
|
+
async function upgradeAsyncIfNeeded(migrationsTable, session, localMigrations, mode = "transaction") {
|
|
104
105
|
if ((await allAsync(session, __sql_sql_ts.sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`, (row) => ({ "1": row[0] }))).length === 0) return { newDb: true };
|
|
105
106
|
const version = getVersion((await allAsync(session, __sql_sql_ts.sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`, (row) => ({ column_name: row[0] }))).map((r) => r.column_name));
|
|
106
107
|
for (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {
|
|
107
108
|
const upgradeFn = upgradeAsyncFunctions[v];
|
|
108
109
|
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
109
|
-
await upgradeFn(migrationsTable, session, localMigrations);
|
|
110
|
+
await upgradeFn(migrationsTable, session, localMigrations, mode);
|
|
110
111
|
}
|
|
111
112
|
return {
|
|
112
113
|
prevVersion: version,
|
|
113
114
|
currentVersion: CURRENT_MIGRATION_TABLE_VERSION
|
|
114
115
|
};
|
|
115
116
|
}
|
|
116
|
-
const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrations) => {
|
|
117
|
+
const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrations, mode) => {
|
|
117
118
|
const table = __sql_sql_ts.sql`${__sql_sql_ts.sql.identifier(migrationsTable)}`;
|
|
118
119
|
const dbRows = await allAsync(session, __sql_sql_ts.sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`, (row) => ({
|
|
119
120
|
id: row[0],
|
|
@@ -156,17 +157,19 @@ const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrati
|
|
|
156
157
|
else unmatched.push(dbRow);
|
|
157
158
|
}
|
|
158
159
|
if (unmatched.length > 0) throw Error(`While upgrading your database migrations table we found ${unmatched.length} (${unmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(", ")}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
const statements = [__sql_sql_ts.sql`ALTER TABLE ${table} ADD COLUMN ${__sql_sql_ts.sql.identifier("name")} text`, __sql_sql_ts.sql`ALTER TABLE ${table} ADD COLUMN ${__sql_sql_ts.sql.identifier("applied_at")} TEXT`];
|
|
161
|
+
for (const backfillEntry of toApply) {
|
|
162
|
+
const updateQuery = __sql_sql_ts.sql`UPDATE ${table} SET ${__sql_sql_ts.sql.identifier("name")} = ${backfillEntry.name}, ${__sql_sql_ts.sql.identifier("applied_at")} = NULL WHERE`;
|
|
163
|
+
if (backfillEntry.id) updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("id")} = ${backfillEntry.id}`);
|
|
164
|
+
else if (backfillEntry.matchedBy === "millis") updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("created_at")} = ${backfillEntry.created_at}`);
|
|
165
|
+
else updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("hash")} = ${backfillEntry.hash}`);
|
|
166
|
+
statements.push(updateQuery);
|
|
167
|
+
}
|
|
168
|
+
if (mode === "transaction") await session.transaction(async (tx) => {
|
|
169
|
+
for (const statement of statements) await tx.run(statement);
|
|
169
170
|
});
|
|
171
|
+
else if (mode === "run") for (const statement of statements) await session.run(statement);
|
|
172
|
+
else (0, __utils_ts.assertUnreachable)(mode);
|
|
170
173
|
} };
|
|
171
174
|
|
|
172
175
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.cjs","names":["sql"],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { SQLiteD1Session } from '~/d1/session.ts';\nimport type { LibSQLSession } from '~/libsql/session.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { SQLiteCloudSession } from '~/sqlite-cloud/session.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport type { SQLiteRemoteSession } from '~/sqlite-proxy/session.ts';\n\nconst CURRENT_MIGRATION_TABLE_VERSION = 1;\n\ninterface UpgradeResult {\n\tnewDb?: boolean;\n\tprevVersion?: number;\n\tcurrentVersion?: number;\n}\n\nfunction getVersion(columns: string[]) {\n\tif (columns.includes('name')) return 1;\n\treturn 0;\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nfunction allSync<T>(\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): T[] {\n\tconst result = session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nasync function allAsync<T>(\n\tsession: SQLiteSession<\n\t\t'async',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): Promise<T[]> {\n\tconst result = await session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\t// Check if the table exists at all\n\t// sqlite-proxy returns [ [1] ]\n\t// sqlite returns [{ '1': 1 }]\n\tconst tableExists = allSync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = allSync<{ column_name: string }>(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = allSync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tsession:\n\t\t| SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>\n\t\t| SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await allAsync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\t// sqlite-proxy returns [ [string] ]\n\t// sqlite returns [{ column_name: string }]\n\tconst rows = await allAsync(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await allAsync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tawait session.transaction(async (tx) => {\n\t\t\tawait tx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\tawait tx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\tawait tx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite.cjs","names":["sql"],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { SQLiteD1Session } from '~/d1/session.ts';\nimport type { LibSQLSession } from '~/libsql/session.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { SQLiteCloudSession } from '~/sqlite-cloud/session.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport type { SQLiteRemoteSession } from '~/sqlite-proxy/session.ts';\nimport { assertUnreachable } from '~/utils.ts';\n\nconst CURRENT_MIGRATION_TABLE_VERSION = 1;\n\ninterface UpgradeResult {\n\tnewDb?: boolean;\n\tprevVersion?: number;\n\tcurrentVersion?: number;\n}\n\nfunction getVersion(columns: string[]) {\n\tif (columns.includes('name')) return 1;\n\treturn 0;\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nfunction allSync<T>(\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): T[] {\n\tconst result = session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nasync function allAsync<T>(\n\tsession: SQLiteSession<\n\t\t'async',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): Promise<T[]> {\n\tconst result = await session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\t// Check if the table exists at all\n\t// sqlite-proxy returns [ [1] ]\n\t// sqlite returns [{ '1': 1 }]\n\tconst tableExists = allSync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = allSync<{ column_name: string }>(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = allSync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tsession:\n\t\t| SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>\n\t\t| SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>,\n\tlocalMigrations: MigrationMeta[],\n\tmode: 'transaction' | 'run' = 'transaction',\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await allAsync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\t// sqlite-proxy returns [ [string] ]\n\t// sqlite returns [{ column_name: string }]\n\tconst rows = await allAsync(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations, mode);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t\tmode: 'transaction' | 'run',\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations, mode) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await allAsync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: SQL[] = [\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`,\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(updateQuery);\n\t\t}\n\n\t\tif (mode === 'transaction') {\n\t\t\t// for normal sqlite proxy migrate() call from code\n\t\t\tawait session.transaction(async (tx) => {\n\t\t\t\tfor (const statement of statements) {\n\t\t\t\t\tawait tx.run(statement);\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (mode === 'run') {\n\t\t\t// for drizzle-kit migrate call for d1 driver\n\t\t\t// see drizzle-kit/src/cli/connections.ts for sqlite d1\n\t\t\tfor (const statement of statements) {\n\t\t\t\tawait session.run(statement);\n\t\t\t}\n\t\t} else assertUnreachable(mode);\n\t},\n};\n"],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ declare function upgradeSyncIfNeeded(migrationsTable: string, session: SQLiteSes
|
|
|
26
26
|
* Version 0: Original schema (id, hash, created_at)
|
|
27
27
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
28
28
|
*/
|
|
29
|
-
declare function upgradeAsyncIfNeeded(migrationsTable: string, session: SQLiteSession<'async', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[]): Promise<UpgradeResult>;
|
|
29
|
+
declare function upgradeAsyncIfNeeded(migrationsTable: string, session: SQLiteSession<'async', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[], mode?: 'transaction' | 'run'): Promise<UpgradeResult>;
|
|
30
30
|
//#endregion
|
|
31
31
|
export { upgradeAsyncIfNeeded, upgradeSyncIfNeeded };
|
|
32
32
|
//# sourceMappingURL=sqlite.d.cts.map
|
|
@@ -26,7 +26,7 @@ declare function upgradeSyncIfNeeded(migrationsTable: string, session: SQLiteSes
|
|
|
26
26
|
* Version 0: Original schema (id, hash, created_at)
|
|
27
27
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
28
28
|
*/
|
|
29
|
-
declare function upgradeAsyncIfNeeded(migrationsTable: string, session: SQLiteSession<'async', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[]): Promise<UpgradeResult>;
|
|
29
|
+
declare function upgradeAsyncIfNeeded(migrationsTable: string, session: SQLiteSession<'async', unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig> | SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>, localMigrations: MigrationMeta[], mode?: 'transaction' | 'run'): Promise<UpgradeResult>;
|
|
30
30
|
//#endregion
|
|
31
31
|
export { upgradeAsyncIfNeeded, upgradeSyncIfNeeded };
|
|
32
32
|
//# sourceMappingURL=sqlite.d.ts.map
|
package/up-migrations/sqlite.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertUnreachable } from "../utils.js";
|
|
1
2
|
import { sql } from "../sql/sql.js";
|
|
2
3
|
|
|
3
4
|
//#region src/up-migrations/sqlite.ts
|
|
@@ -98,20 +99,20 @@ const upgradeSyncFunctions = { 0: (migrationsTable, session, localMigrations) =>
|
|
|
98
99
|
* Version 0: Original schema (id, hash, created_at)
|
|
99
100
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
100
101
|
*/
|
|
101
|
-
async function upgradeAsyncIfNeeded(migrationsTable, session, localMigrations) {
|
|
102
|
+
async function upgradeAsyncIfNeeded(migrationsTable, session, localMigrations, mode = "transaction") {
|
|
102
103
|
if ((await allAsync(session, sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`, (row) => ({ "1": row[0] }))).length === 0) return { newDb: true };
|
|
103
104
|
const version = getVersion((await allAsync(session, sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`, (row) => ({ column_name: row[0] }))).map((r) => r.column_name));
|
|
104
105
|
for (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {
|
|
105
106
|
const upgradeFn = upgradeAsyncFunctions[v];
|
|
106
107
|
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
107
|
-
await upgradeFn(migrationsTable, session, localMigrations);
|
|
108
|
+
await upgradeFn(migrationsTable, session, localMigrations, mode);
|
|
108
109
|
}
|
|
109
110
|
return {
|
|
110
111
|
prevVersion: version,
|
|
111
112
|
currentVersion: CURRENT_MIGRATION_TABLE_VERSION
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
|
-
const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrations) => {
|
|
115
|
+
const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrations, mode) => {
|
|
115
116
|
const table = sql`${sql.identifier(migrationsTable)}`;
|
|
116
117
|
const dbRows = await allAsync(session, sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`, (row) => ({
|
|
117
118
|
id: row[0],
|
|
@@ -154,17 +155,19 @@ const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrati
|
|
|
154
155
|
else unmatched.push(dbRow);
|
|
155
156
|
}
|
|
156
157
|
if (unmatched.length > 0) throw Error(`While upgrading your database migrations table we found ${unmatched.length} (${unmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(", ")}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
158
|
+
const statements = [sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier("name")} text`, sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier("applied_at")} TEXT`];
|
|
159
|
+
for (const backfillEntry of toApply) {
|
|
160
|
+
const updateQuery = sql`UPDATE ${table} SET ${sql.identifier("name")} = ${backfillEntry.name}, ${sql.identifier("applied_at")} = NULL WHERE`;
|
|
161
|
+
if (backfillEntry.id) updateQuery.append(sql` ${sql.identifier("id")} = ${backfillEntry.id}`);
|
|
162
|
+
else if (backfillEntry.matchedBy === "millis") updateQuery.append(sql` ${sql.identifier("created_at")} = ${backfillEntry.created_at}`);
|
|
163
|
+
else updateQuery.append(sql` ${sql.identifier("hash")} = ${backfillEntry.hash}`);
|
|
164
|
+
statements.push(updateQuery);
|
|
165
|
+
}
|
|
166
|
+
if (mode === "transaction") await session.transaction(async (tx) => {
|
|
167
|
+
for (const statement of statements) await tx.run(statement);
|
|
167
168
|
});
|
|
169
|
+
else if (mode === "run") for (const statement of statements) await session.run(statement);
|
|
170
|
+
else assertUnreachable(mode);
|
|
168
171
|
} };
|
|
169
172
|
|
|
170
173
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { SQLiteD1Session } from '~/d1/session.ts';\nimport type { LibSQLSession } from '~/libsql/session.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { SQLiteCloudSession } from '~/sqlite-cloud/session.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport type { SQLiteRemoteSession } from '~/sqlite-proxy/session.ts';\n\nconst CURRENT_MIGRATION_TABLE_VERSION = 1;\n\ninterface UpgradeResult {\n\tnewDb?: boolean;\n\tprevVersion?: number;\n\tcurrentVersion?: number;\n}\n\nfunction getVersion(columns: string[]) {\n\tif (columns.includes('name')) return 1;\n\treturn 0;\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nfunction allSync<T>(\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): T[] {\n\tconst result = session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nasync function allAsync<T>(\n\tsession: SQLiteSession<\n\t\t'async',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): Promise<T[]> {\n\tconst result = await session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\t// Check if the table exists at all\n\t// sqlite-proxy returns [ [1] ]\n\t// sqlite returns [{ '1': 1 }]\n\tconst tableExists = allSync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = allSync<{ column_name: string }>(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = allSync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tsession:\n\t\t| SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>\n\t\t| SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await allAsync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\t// sqlite-proxy returns [ [string] ]\n\t// sqlite returns [{ column_name: string }]\n\tconst rows = await allAsync(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await allAsync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tawait session.transaction(async (tx) => {\n\t\t\tawait tx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\tawait tx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\tawait tx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/up-migrations/sqlite.ts"],"sourcesContent":["import type { TablesRelationalConfig } from '~/_relations.ts';\nimport type { SQLiteD1Session } from '~/d1/session.ts';\nimport type { LibSQLSession } from '~/libsql/session.ts';\nimport type { MigrationMeta } from '~/migrator.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type SQL, sql } from '~/sql/sql.ts';\nimport type { SQLiteCloudSession } from '~/sqlite-cloud/session.ts';\nimport type { SQLiteSession } from '~/sqlite-core/session.ts';\nimport type { SQLiteRemoteSession } from '~/sqlite-proxy/session.ts';\nimport { assertUnreachable } from '~/utils.ts';\n\nconst CURRENT_MIGRATION_TABLE_VERSION = 1;\n\ninterface UpgradeResult {\n\tnewDb?: boolean;\n\tprevVersion?: number;\n\tcurrentVersion?: number;\n}\n\nfunction getVersion(columns: string[]) {\n\tif (columns.includes('name')) return 1;\n\treturn 0;\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nfunction allSync<T>(\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): T[] {\n\tconst result = session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n// sqlite-proxy returns [ [string] ]\n// sqlite returns [{ column_name: string }]\nasync function allAsync<T>(\n\tsession: SQLiteSession<\n\t\t'async',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tsqlQuery: SQL,\n\tresultMapper: (row: any[]) => T = () => [] as T,\n): Promise<T[]> {\n\tconst result = await session.all(sqlQuery) as any[] | any[][];\n\n\tif (result.length === 0) return [];\n\n\tif (Array.isArray(result[0])) {\n\t\treturn (result as any[][]).map((row) => resultMapper(row));\n\t}\n\n\treturn result as T[];\n}\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport function upgradeSyncIfNeeded(\n\tmigrationsTable: string,\n\tsession: SQLiteSession<\n\t\t'sync',\n\t\tunknown,\n\t\tRecord<string, unknown>,\n\t\tAnyRelations,\n\t\tTablesRelationalConfig\n\t>,\n\tlocalMigrations: MigrationMeta[],\n): UpgradeResult {\n\t// Check if the table exists at all\n\t// sqlite-proxy returns [ [1] ]\n\t// sqlite returns [{ '1': 1 }]\n\tconst tableExists = allSync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = allSync<{ column_name: string }>(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeSyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tupgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeSyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'sync',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => void\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = allSync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tsession.transaction((tx) => {\n\t\t\ttx.run(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`);\n\t\t\ttx.run(\n\t\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t\t);\n\n\t\t\tfor (const backfillEntry of toApply) {\n\t\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE`;\n\n\t\t\t\t// id\n\t\t\t\t// created_at\n\t\t\t\t// hash\n\t\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\t\ttx.run(updateQuery);\n\t\t\t}\n\t\t});\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tsession:\n\t\t| SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>\n\t\t| SQLiteRemoteSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteD1Session<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| LibSQLSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>\n\t\t| SQLiteCloudSession<Record<string, unknown>, AnyRelations, TablesRelationalConfig>,\n\tlocalMigrations: MigrationMeta[],\n\tmode: 'transaction' | 'run' = 'transaction',\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await allAsync(\n\t\tsession,\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t\t(row) => ({ '1': row[0] }),\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\t// sqlite-proxy returns [ [string] ]\n\t// sqlite returns [{ column_name: string }]\n\tconst rows = await allAsync(\n\t\tsession,\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t\t(row) => ({ column_name: row[0] }),\n\t);\n\n\tconst version = getVersion(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < CURRENT_MIGRATION_TABLE_VERSION; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations, mode);\n\t}\n\n\treturn { prevVersion: version, currentVersion: CURRENT_MIGRATION_TABLE_VERSION };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SQLiteSession<\n\t\t\t'async',\n\t\t\tunknown,\n\t\t\tRecord<string, unknown>,\n\t\t\tAnyRelations,\n\t\t\tTablesRelationalConfig\n\t\t>,\n\t\tlocalMigrations: MigrationMeta[],\n\t\tmode: 'transaction' | 'run',\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations, mode) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = await allAsync<{ id: number | null; hash: string; created_at: number }>(\n\t\t\tsession,\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t\t(row) => ({\n\t\t\t\tid: row[0],\n\t\t\t\thash: row[1],\n\t\t\t\tcreated_at: row[2],\n\t\t\t}),\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: SQL[] = [\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`,\n\t\t\tsql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(updateQuery);\n\t\t}\n\n\t\tif (mode === 'transaction') {\n\t\t\t// for normal sqlite proxy migrate() call from code\n\t\t\tawait session.transaction(async (tx) => {\n\t\t\t\tfor (const statement of statements) {\n\t\t\t\t\tawait tx.run(statement);\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (mode === 'run') {\n\t\t\t// for drizzle-kit migrate call for d1 driver\n\t\t\t// see drizzle-kit/src/cli/connections.ts for sqlite d1\n\t\t\tfor (const statement of statements) {\n\t\t\t\tawait session.run(statement);\n\t\t\t}\n\t\t} else assertUnreachable(mode);\n\t},\n};\n"],"mappings":"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"}
|