Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/persisters/persister-mssql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {Store} from '../../@types/store/index.d.ts';
import {arrayForEach} from '../../common/array.ts';
import {IdObj} from '../../common/obj.ts';
import {noop} from '../../common/other.ts';
import {getWrappedCommand} from '../common/database/common.ts';
import {createCustomMsSqlPersister} from '../common/database/mssql.ts';

// The shared code emits positional parameters as @p1, @p2, and so on, which
Expand Down Expand Up @@ -49,8 +50,13 @@ export const createMsSqlPersister = (async (
const transaction = mssql.transaction();
await transaction.begin();
try {
// The command is wrapped here too, since the statements inside the
// transaction would otherwise never reach onSqlCommand.
const result = await actions(
getExecuteCommand(() => transaction.request()),
getWrappedCommand(
getExecuteCommand(() => transaction.request()),
onSqlCommand,
),
);
await transaction.commit();
return result;
Expand Down
53 changes: 53 additions & 0 deletions test/unit/persisters/database/mssql.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {createMergeableStore, createStore} from 'tinybase';
import {createCustomMsSqlPersister, Persists} from 'tinybase/persisters';
import {createMsSqlPersister} from 'tinybase/persisters/persister-mssql';
import {expect, test, vi} from 'vitest';
import {pause} from '../../common/other.ts';

Expand Down Expand Up @@ -323,3 +324,55 @@ test('returns the thing it was given', async () => {
expect((persister as any).getMsSql()).toBe(fake);
await persister.destroy();
});

// A stand-in for the driver's ConnectionPool, built on the fake above, so that
// the real entry point rather than the shared code is under test.
const getFakePool = (fake: FakeMsSql) => {
const getRequest = () => {
const params: any[] = [];
const request: any = {
input: (name: string, param: any) => {
params[Number(name.slice(1)) - 1] = param;
return request;
},
query: async (sql: string) => ({
recordset: await fake.executeCommand(sql, params),
}),
};
return request;
};
return {
request: getRequest,
transaction: () => ({
begin: async () => {},
commit: async () => {},
rollback: async () => {},
request: getRequest,
}),
};
};

test('reports the SQL it runs in a transaction to onSqlCommand', async () => {
const fake = getFakeMsSql();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const sql: string[] = [];
const persister = await createMsSqlPersister(
store,
getFakePool(fake) as any,
{mode: 'json', storeTableName: STORE_TABLE_NAME},
(oneSql) => sql.push(oneSql),
);

await persister.save();

// Saving runs in a Transaction, which takes its own connection, so its
// statements would otherwise never reach the callback.
expect(
sql.filter((oneSql) => oneSql.startsWith('CREATE TABLE')),
).toHaveLength(1);
expect(sql.filter((oneSql) => oneSql.startsWith('MERGE INTO'))).toHaveLength(
1,
);

await persister.destroy();
});
Loading