| import type { Kysely } from '../../kysely.js'; | ||
| import { DialectAdapterBase } from '../dialect-adapter-base.js'; | ||
| import type { MigrationLockOptions } from '../dialect-adapter.js'; | ||
| export declare class MssqlAdapter extends DialectAdapterBase { | ||
@@ -61,3 +62,3 @@ get supportsCreateIfNotExists(): boolean; | ||
| */ | ||
| acquireMigrationLock(db: Kysely<any>): Promise<void>; | ||
| acquireMigrationLock(db: Kysely<any>, _opt: MigrationLockOptions): Promise<void>; | ||
| /** | ||
@@ -71,3 +72,3 @@ * Releases the migration lock. See {@link acquireMigrationLock}. | ||
| */ | ||
| releaseMigrationLock(): Promise<void>; | ||
| releaseMigrationLock(db: Kysely<any>, _opt: MigrationLockOptions): Promise<void>; | ||
| } |
@@ -5,2 +5,3 @@ /// <reference types="./mssql-adapter.d.ts" /> | ||
| import { DialectAdapterBase } from '../dialect-adapter-base.js'; | ||
| const LOCK_PRINCIPAL = 'dbo'; | ||
| export class MssqlAdapter extends DialectAdapterBase { | ||
@@ -16,12 +17,17 @@ get supportsCreateIfNotExists() { | ||
| } | ||
| async acquireMigrationLock(db) { | ||
| // Acquire a transaction-level exclusive lock on the migrations table. | ||
| async acquireMigrationLock(db, _opt) { | ||
| // Acquire a session-level exclusive lock on the migrations table. A | ||
| // session-level lock (as opposed to the default transaction-level lock) | ||
| // stays held after the migration transaction commits and until we release | ||
| // it explicitly in `releaseMigrationLock` OR the session ends. This way we | ||
| // know the lock is either released by us after successful or failed | ||
| // migrations OR it's released by SQL Server if the connection dies. | ||
| // https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver16 | ||
| await sql `exec sp_getapplock @DbPrincipal = ${sql.lit('dbo')}, @Resource = ${sql.lit(DEFAULT_MIGRATION_TABLE)}, @LockMode = ${sql.lit('Exclusive')}`.execute(db); | ||
| await sql `exec sp_getapplock @DbPrincipal = ${sql.lit(LOCK_PRINCIPAL)}, @Resource = ${sql.lit(DEFAULT_MIGRATION_TABLE)}, @LockMode = ${sql.lit('Exclusive')}, @LockOwner = ${sql.lit('Session')}`.execute(db); | ||
| } | ||
| async releaseMigrationLock() { | ||
| // Nothing to do here. `sp_getapplock` is automatically released at the | ||
| // end of the transaction and since `supportsTransactionalDdl` true, we know | ||
| // the `db` instance passed to acquireMigrationLock is actually a transaction. | ||
| async releaseMigrationLock(db, _opt) { | ||
| // Release the session-level lock acquired in `acquireMigrationLock`. | ||
| // https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-releaseapplock-transact-sql?view=sql-server-ver16 | ||
| await sql `exec sp_releaseapplock @DbPrincipal = ${sql.lit(LOCK_PRINCIPAL)}, @Resource = ${sql.lit(DEFAULT_MIGRATION_TABLE)}, @LockOwner = ${sql.lit('Session')}`.execute(db); | ||
| } | ||
| } |
@@ -13,3 +13,6 @@ import type { DatabaseConnection } from '../../driver/database-connection.js'; | ||
| */ | ||
| controlConnection?: (config: object) => MysqlConnection; | ||
| controlConnection?: { | ||
| (connectionUri: string): MysqlConnection; | ||
| (config: object): MysqlConnection; | ||
| }; | ||
| /** | ||
@@ -48,6 +51,5 @@ * A mysql2 Pool instance or a function that returns one. | ||
| destroy(): void; | ||
| query(sql: string, parameters: Array<unknown>): { | ||
| stream: <T>(options: MysqlStreamOptions) => MysqlStream<T>; | ||
| query(sql: string, parameters: any, callback?: (error: unknown, result: MysqlQueryResult) => void): { | ||
| stream: (options: MysqlStreamOptions) => MysqlStream; | ||
| }; | ||
| query(sql: string, parameters: Array<unknown>, callback: (error: unknown, result: MysqlQueryResult) => void): void; | ||
| threadId: number; | ||
@@ -63,4 +65,4 @@ } | ||
| } | ||
| export interface MysqlStream<T> { | ||
| [Symbol.asyncIterator](): AsyncIterableIterator<T>; | ||
| export interface MysqlStream { | ||
| [Symbol.asyncIterator](): any; | ||
| } | ||
@@ -67,0 +69,0 @@ export interface MysqlOkPacket { |
@@ -70,3 +70,3 @@ import type { Kysely } from '../../kysely.js'; | ||
| */ | ||
| releaseMigrationLock(_db: Kysely<any>, _opt: MigrationLockOptions): Promise<void>; | ||
| releaseMigrationLock(db: Kysely<any>, _opt: MigrationLockOptions): Promise<void>; | ||
| } |
| /// <reference types="./postgres-adapter.d.ts" /> | ||
| import { sql } from '../../raw-builder/sql.js'; | ||
| import { DialectAdapterBase } from '../dialect-adapter-base.js'; | ||
| // Random id for our transaction lock. | ||
| const LOCK_ID = BigInt('3853314791062309107'); | ||
| const LOCK_TIMEOUT_MILLISECONDS = 60 * 60 * 1_000; | ||
| export class PostgresAdapter extends DialectAdapterBase { | ||
@@ -14,10 +14,16 @@ get supportsTransactionalDdl() { | ||
| async acquireMigrationLock(db, _opt) { | ||
| // Acquire a transaction level advisory lock. | ||
| await sql `select pg_advisory_xact_lock(${sql.lit(LOCK_ID)})`.execute(db); | ||
| // in 1 RTT, acquire a session-level advisory lock with a timeout. | ||
| // | ||
| // if ever this runs inside a transaction, niche edge case we support - the | ||
| // user is responsible to set a different timeout value or disable (`0` value). | ||
| await sql ` | ||
| with set_timeout as ( | ||
| select set_config('lock_timeout', '${sql.lit(LOCK_TIMEOUT_MILLISECONDS)}', true) as config_val | ||
| ) | ||
| select pg_advisory_lock(${sql.lit(LOCK_ID)}) | ||
| from set_timeout`.execute(db); | ||
| } | ||
| async releaseMigrationLock(_db, _opt) { | ||
| // Nothing to do here. `pg_advisory_xact_lock` is automatically released at the | ||
| // end of the transaction and since `supportsTransactionalDdl` true, we know | ||
| // the `db` instance passed to acquireMigrationLock is actually a transaction. | ||
| async releaseMigrationLock(db, _opt) { | ||
| await sql `select pg_advisory_unlock(${sql.lit(LOCK_ID)})`.execute(db); | ||
| } | ||
| } |
@@ -407,19 +407,22 @@ /// <reference types="./migrator.d.ts" /> | ||
| const run = async (db) => { | ||
| const state = await this.#getState(db); | ||
| if (state.migrations.length === 0) { | ||
| return { results: [] }; | ||
| } | ||
| const { direction, step } = getMigrationDirectionAndStep(state); | ||
| if (step <= 0) { | ||
| return { results: [] }; | ||
| } | ||
| if (direction === 'Down') { | ||
| return await this.#migrateDown(db, state, step); | ||
| } | ||
| else if (direction === 'Up') { | ||
| return await this.#migrateUp(db, state, step); | ||
| } | ||
| return { results: [] }; | ||
| }; | ||
| const runWithLock = async (db, cb) => { | ||
| try { | ||
| await adapter.acquireMigrationLock(db, lockOptions); | ||
| const state = await this.#getState(db); | ||
| if (state.migrations.length === 0) { | ||
| return { results: [] }; | ||
| } | ||
| const { direction, step } = getMigrationDirectionAndStep(state); | ||
| if (step <= 0) { | ||
| return { results: [] }; | ||
| } | ||
| if (direction === 'Down') { | ||
| return await this.#migrateDown(db, state, step); | ||
| } | ||
| else if (direction === 'Up') { | ||
| return await this.#migrateUp(db, state, step); | ||
| } | ||
| return { results: [] }; | ||
| return await cb(db); | ||
| } | ||
@@ -438,8 +441,10 @@ finally { | ||
| } | ||
| return run(this.#props.db); | ||
| return runWithLock(this.#props.db, run); | ||
| } | ||
| if (adapter.supportsTransactionalDdl && !disableTransactions) { | ||
| return this.#props.db.transaction().execute(run); | ||
| return this.#props.db | ||
| .connection() | ||
| .execute((db) => runWithLock(db, (db) => db.transaction().execute((trx) => run(trx)))); | ||
| } | ||
| return this.#props.db.connection().execute(run); | ||
| return this.#props.db.connection().execute((db) => runWithLock(db, run)); | ||
| } | ||
@@ -446,0 +451,0 @@ async #getState(db) { |
@@ -76,3 +76,3 @@ import type { AliasableExpression, AliasedExpression, Expression } from '../expression/expression.js'; | ||
| */ | ||
| at<I extends any[] extends O ? number | 'last' | `#-${number}` : never, O2 = null | NonNullable<NonNullable<O>[keyof NonNullable<O> & number]>>(index: `${I}` extends `${any}.${any}` | `#--${any}` ? never : I): TraversedJSONPathBuilder<S, O2>; | ||
| at<I extends (any[] extends O ? number | 'last' | `#-${number}` : never), O2 = null | NonNullable<NonNullable<O>[keyof NonNullable<O> & number]>>(index: `${I}` extends `${any}.${any}` | `#--${any}` ? never : I): TraversedJSONPathBuilder<S, O2>; | ||
| /** | ||
@@ -127,3 +127,3 @@ * Access a property of a JSON object. | ||
| */ | ||
| key<K extends any[] extends O ? never : O extends object ? keyof NonNullable<O> & string : never, O2 = undefined extends O ? null | NonNullable<NonNullable<O>[K]> : null extends O ? null | NonNullable<NonNullable<O>[K]> : string extends keyof NonNullable<O> ? null | NonNullable<NonNullable<O>[K]> : NonNullable<O>[K]>(key: K): TraversedJSONPathBuilder<S, O2>; | ||
| key<K extends (any[] extends O ? never : O extends object ? keyof NonNullable<O> & string : never), O2 = undefined extends O ? null | NonNullable<NonNullable<O>[K]> : null extends O ? null | NonNullable<NonNullable<O>[K]> : string extends keyof NonNullable<O> ? null | NonNullable<NonNullable<O>[K]> : NonNullable<O>[K]>(key: K): TraversedJSONPathBuilder<S, O2>; | ||
| } | ||
@@ -130,0 +130,0 @@ export declare class TraversedJSONPathBuilder<S, O> extends JSONPathBuilder<S, O> implements AliasableExpression<O> { |
+19
-19
| { | ||
| "name": "kysely", | ||
| "version": "0.29.2", | ||
| "version": "0.29.3", | ||
| "description": "Type safe SQL query builder", | ||
@@ -64,5 +64,5 @@ "repository": { | ||
| "devDependencies": { | ||
| "@arethetypeswrong/cli": "0.18.2", | ||
| "@ark/attest": "0.56.0", | ||
| "@electric-sql/pglite": "0.4.5", | ||
| "@arethetypeswrong/cli": "0.18.4", | ||
| "@ark/attest": "0.56.2", | ||
| "@electric-sql/pglite": "0.5.3", | ||
| "@types/better-sqlite3": "7.6.13", | ||
@@ -72,3 +72,3 @@ "@types/chai": "5.2.3", | ||
| "@types/mocha": "10.0.10", | ||
| "@types/node": "25.8.0", | ||
| "@types/node": "26.1.0", | ||
| "@types/pg": "8.20.0", | ||
@@ -78,24 +78,24 @@ "@types/pg-cursor": "2.7.2", | ||
| "@types/semver": "7.7.1", | ||
| "@types/sinon": "21.0.1", | ||
| "better-sqlite3": "12.10.0", | ||
| "@types/sinon": "22.0.0", | ||
| "better-sqlite3": "12.11.1", | ||
| "chai": "6.2.2", | ||
| "chai-as-promised": "8.0.2", | ||
| "esbuild": "0.28.0", | ||
| "esbuild": "0.28.1", | ||
| "jsr": "0.14.3", | ||
| "mocha": "11.7.5", | ||
| "mysql2": "3.22.3", | ||
| "mocha": "11.7.6", | ||
| "mysql2": "3.22.5", | ||
| "pathe": "2.0.3", | ||
| "pg": "8.20.0", | ||
| "pg-cursor": "2.19.0", | ||
| "playwright": "1.60.0", | ||
| "prettier": "3.8.3", | ||
| "pg": "8.22.0", | ||
| "pg-cursor": "2.21.0", | ||
| "playwright": "1.61.1", | ||
| "prettier": "3.9.4", | ||
| "prototype-pollution-vulnerable-lodash.merge-dont-upgrade": "npm:lodash.merge@4.6.1", | ||
| "remeda": "2.34.1", | ||
| "semver": "7.8.0", | ||
| "remeda": "2.39.0", | ||
| "semver": "7.8.5", | ||
| "sinon": "22.0.0", | ||
| "std-env": "4.1.0", | ||
| "tarn": "3.0.2", | ||
| "tedious": "19.2.1", | ||
| "tarn": "3.1.0", | ||
| "tedious": "20.0.0", | ||
| "tsd": "0.33.0", | ||
| "tsx": "4.22.0", | ||
| "tsx": "4.22.4", | ||
| "typescript": "6.0.3" | ||
@@ -102,0 +102,0 @@ }, |
+1
-1
| [](https://stand-with-ukraine.pp.ua) | ||
| [](https://github.com/kysely-org/kysely/releases/latest) | ||
| [](https://socket.dev/npm/package/kysely/overview/0.29.2) | ||
| [](https://socket.dev/npm/package/kysely/overview/0.29.3) | ||
| [](https://github.com/kysely-org/kysely) | ||
@@ -6,0 +6,0 @@ [](https://github.com/kysely-org/kysely/blob/master/LICENSE) |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1724883
0.07%47714
0.04%