@powersync/service-module-mssql 0.1.1 → 0.1.2
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @powersync/service-module-mssql
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bdfd287: Add the `timestamp_max_precision` option for sync rules. It can be set to `seconds`, `milliseconds` or `microseconds` to restrict the precision of synced datetime values.
|
|
8
|
+
- Updated dependencies [8fdbf8d]
|
|
9
|
+
- Updated dependencies [bdfd287]
|
|
10
|
+
- @powersync/service-core@1.18.2
|
|
11
|
+
- @powersync/service-sync-rules@0.29.10
|
|
12
|
+
- @powersync/lib-services-framework@0.7.14
|
|
13
|
+
|
|
3
14
|
## 0.1.1
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import sql from 'mssql';
|
|
2
|
-
import { ExpressionType, SQLITE_FALSE, SQLITE_TRUE, toSyncRulesRow } from '@powersync/service-sync-rules';
|
|
2
|
+
import { DateTimeValue, ExpressionType, SQLITE_FALSE, SQLITE_TRUE, TimeValue, TimeValuePrecision, toSyncRulesRow } from '@powersync/service-sync-rules';
|
|
3
3
|
import { MSSQLUserDefinedType } from '../types/mssql-data-types.js';
|
|
4
4
|
export function toSqliteInputRow(row, columns) {
|
|
5
5
|
let result = {};
|
|
@@ -23,14 +23,22 @@ export function toSqliteInputRow(row, columns) {
|
|
|
23
23
|
result[key] = toISODateString(row[key]);
|
|
24
24
|
break;
|
|
25
25
|
case sql.TYPES.Time:
|
|
26
|
-
result[key] =
|
|
26
|
+
result[key] = toISOTimeValue(row[key]);
|
|
27
27
|
break;
|
|
28
28
|
case sql.TYPES.DateTime:
|
|
29
29
|
case sql.TYPES.DateTime2:
|
|
30
30
|
case sql.TYPES.SmallDateTime:
|
|
31
31
|
case sql.TYPES.DateTimeOffset: // The offset is lost when the driver converts to Date. This needs to be handled in the sql query.
|
|
32
32
|
const date = row[key];
|
|
33
|
-
|
|
33
|
+
if (isNaN(date.getTime())) {
|
|
34
|
+
result[key] = null;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
const originalFormat = date.toISOString();
|
|
38
|
+
const withNanos = originalFormat.substring(0, originalFormat.length - 1) + // Drop the trailing Z
|
|
39
|
+
subMillisecondSuffix(date) + // Expand the .xyz millisecond part to include nanoseconds
|
|
40
|
+
'Z';
|
|
41
|
+
result[key] = new DateTimeValue(withNanos, withNanos, msSqlTimeSourceOptions);
|
|
34
42
|
break;
|
|
35
43
|
case sql.TYPES.Binary:
|
|
36
44
|
case sql.TYPES.VarBinary:
|
|
@@ -67,11 +75,24 @@ function toISODateString(date) {
|
|
|
67
75
|
}
|
|
68
76
|
/**
|
|
69
77
|
* MSSQL time format is HH:mm:ss[.nnnnnnn]
|
|
70
|
-
* @param date
|
|
71
|
-
* @returns
|
|
72
78
|
*/
|
|
73
|
-
function
|
|
74
|
-
|
|
79
|
+
function toISOTimeValue(date) {
|
|
80
|
+
if (isNaN(date.getTime())) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const withMillis = date.toISOString().split('T')[1].replace('Z', '') + subMillisecondSuffix(date);
|
|
84
|
+
return TimeValue.parse(withMillis, msSqlTimeSourceOptions);
|
|
85
|
+
}
|
|
86
|
+
function subMillisecondSuffix(value) {
|
|
87
|
+
if (value.nanosecondsDelta != null) {
|
|
88
|
+
// nanosecondsDelta is actually measured in seconds. It will always be smaller than 1ms though, because that delta
|
|
89
|
+
// is stored on the datetime itself. We truncate here as a precaution.
|
|
90
|
+
const actualNanos = Math.round(value.nanosecondsDelta * Math.pow(10, 9)) % Math.pow(10, 6);
|
|
91
|
+
return actualNanos.toString().padStart(6, '0');
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return '000000';
|
|
95
|
+
}
|
|
75
96
|
}
|
|
76
97
|
/**
|
|
77
98
|
* Converts MSSQL type names to SQLite ExpressionType
|
|
@@ -140,4 +161,9 @@ export function CDCToSqliteRow(options) {
|
|
|
140
161
|
}
|
|
141
162
|
return toSqliteInputRow(filteredRow, columns);
|
|
142
163
|
}
|
|
164
|
+
const msSqlTimeSourceOptions = {
|
|
165
|
+
// We actually only have 100ns precision (7 digits), but all the options are SI prefixes.
|
|
166
|
+
subSecondPrecision: TimeValuePrecision.nanoseconds,
|
|
167
|
+
defaultSubSecondPrecision: TimeValuePrecision.nanoseconds
|
|
168
|
+
};
|
|
143
169
|
//# sourceMappingURL=mssqls-to-sqlite.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mssqls-to-sqlite.js","sourceRoot":"","sources":["../../src/common/mssqls-to-sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,OAAO,CAAC;AACxB,OAAO,
|
|
1
|
+
{"version":3,"file":"mssqls-to-sqlite.js","sourceRoot":"","sources":["../../src/common/mssqls-to-sqlite.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mssql",
|
|
3
3
|
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
4
|
"types": "dist/index.d.ts",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.2",
|
|
6
6
|
"license": "FSL-1.1-ALv2",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"type": "module",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"ts-codec": "^1.3.0",
|
|
28
28
|
"uri-js": "^4.4.1",
|
|
29
29
|
"uuid": "^11.1.0",
|
|
30
|
-
"@powersync/lib-services-framework": "0.7.
|
|
31
|
-
"@powersync/service-core": "1.18.
|
|
30
|
+
"@powersync/lib-services-framework": "0.7.14",
|
|
31
|
+
"@powersync/service-core": "1.18.2",
|
|
32
32
|
"@powersync/service-errors": "0.3.6",
|
|
33
|
-
"@powersync/service-sync-rules": "0.29.
|
|
33
|
+
"@powersync/service-sync-rules": "0.29.10",
|
|
34
34
|
"@powersync/service-types": "0.13.3",
|
|
35
35
|
"@powersync/service-jsonbig": "0.17.12"
|
|
36
36
|
},
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"@types/mssql": "^9.1.8",
|
|
39
39
|
"@types/semver": "^7.7.1",
|
|
40
40
|
"@types/uuid": "^10.0.0",
|
|
41
|
-
"@powersync/service-core-tests": "0.12.
|
|
42
|
-
"@powersync/service-module-mongodb-storage": "0.12.
|
|
43
|
-
"@powersync/service-module-postgres-storage": "0.10.
|
|
41
|
+
"@powersync/service-core-tests": "0.12.16",
|
|
42
|
+
"@powersync/service-module-mongodb-storage": "0.12.16",
|
|
43
|
+
"@powersync/service-module-postgres-storage": "0.10.16"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsc -b",
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import sql from 'mssql';
|
|
2
2
|
import {
|
|
3
3
|
DatabaseInputRow,
|
|
4
|
+
DateTimeSourceOptions,
|
|
5
|
+
DateTimeValue,
|
|
4
6
|
ExpressionType,
|
|
5
7
|
SQLITE_FALSE,
|
|
6
8
|
SQLITE_TRUE,
|
|
7
9
|
SqliteInputRow,
|
|
10
|
+
TimeValue,
|
|
11
|
+
TimeValuePrecision,
|
|
8
12
|
toSyncRulesRow
|
|
9
13
|
} from '@powersync/service-sync-rules';
|
|
10
14
|
import { MSSQLUserDefinedType } from '../types/mssql-data-types.js';
|
|
@@ -32,14 +36,25 @@ export function toSqliteInputRow(row: any, columns: sql.IColumnMetadata): Sqlite
|
|
|
32
36
|
result[key] = toISODateString(row[key] as Date);
|
|
33
37
|
break;
|
|
34
38
|
case sql.TYPES.Time:
|
|
35
|
-
result[key] =
|
|
39
|
+
result[key] = toISOTimeValue(row[key] as DateWithNanosecondsDelta);
|
|
36
40
|
break;
|
|
37
41
|
case sql.TYPES.DateTime:
|
|
38
42
|
case sql.TYPES.DateTime2:
|
|
39
43
|
case sql.TYPES.SmallDateTime:
|
|
40
44
|
case sql.TYPES.DateTimeOffset: // The offset is lost when the driver converts to Date. This needs to be handled in the sql query.
|
|
41
|
-
const date = row[key] as
|
|
42
|
-
|
|
45
|
+
const date = row[key] as DateWithNanosecondsDelta;
|
|
46
|
+
if (isNaN(date.getTime())) {
|
|
47
|
+
result[key] = null;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const originalFormat = date.toISOString();
|
|
52
|
+
const withNanos =
|
|
53
|
+
originalFormat.substring(0, originalFormat.length - 1) + // Drop the trailing Z
|
|
54
|
+
subMillisecondSuffix(date) + // Expand the .xyz millisecond part to include nanoseconds
|
|
55
|
+
'Z';
|
|
56
|
+
|
|
57
|
+
result[key] = new DateTimeValue(withNanos, withNanos, msSqlTimeSourceOptions);
|
|
43
58
|
break;
|
|
44
59
|
case sql.TYPES.Binary:
|
|
45
60
|
case sql.TYPES.VarBinary:
|
|
@@ -74,13 +89,33 @@ function toISODateString(date: Date): string | null {
|
|
|
74
89
|
return isNaN(date.getTime()) ? null : date.toISOString().split('T')[0];
|
|
75
90
|
}
|
|
76
91
|
|
|
92
|
+
// https://github.com/tediousjs/tedious/blob/0c256f186600d7230aec05553ebad209bed81acc/src/value-parser.ts#L668-L670
|
|
93
|
+
interface DateWithNanosecondsDelta extends Date {
|
|
94
|
+
nanosecondsDelta?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
/**
|
|
78
98
|
* MSSQL time format is HH:mm:ss[.nnnnnnn]
|
|
79
|
-
* @param date
|
|
80
|
-
* @returns
|
|
81
99
|
*/
|
|
82
|
-
function
|
|
83
|
-
|
|
100
|
+
function toISOTimeValue(date: DateWithNanosecondsDelta): TimeValue | null {
|
|
101
|
+
if (isNaN(date.getTime())) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const withMillis = date.toISOString().split('T')[1].replace('Z', '') + subMillisecondSuffix(date);
|
|
106
|
+
return TimeValue.parse(withMillis, msSqlTimeSourceOptions);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function subMillisecondSuffix(value: DateWithNanosecondsDelta): string {
|
|
110
|
+
if (value.nanosecondsDelta != null) {
|
|
111
|
+
// nanosecondsDelta is actually measured in seconds. It will always be smaller than 1ms though, because that delta
|
|
112
|
+
// is stored on the datetime itself. We truncate here as a precaution.
|
|
113
|
+
const actualNanos = Math.round(value.nanosecondsDelta * Math.pow(10, 9)) % Math.pow(10, 6);
|
|
114
|
+
|
|
115
|
+
return actualNanos.toString().padStart(6, '0');
|
|
116
|
+
} else {
|
|
117
|
+
return '000000';
|
|
118
|
+
}
|
|
84
119
|
}
|
|
85
120
|
|
|
86
121
|
/**
|
|
@@ -156,3 +191,9 @@ export function CDCToSqliteRow(options: CDCRowToSqliteRowOptions): SqliteInputRo
|
|
|
156
191
|
}
|
|
157
192
|
return toSqliteInputRow(filteredRow, columns);
|
|
158
193
|
}
|
|
194
|
+
|
|
195
|
+
const msSqlTimeSourceOptions: DateTimeSourceOptions = {
|
|
196
|
+
// We actually only have 100ns precision (7 digits), but all the options are SI prefixes.
|
|
197
|
+
subSecondPrecision: TimeValuePrecision.nanoseconds,
|
|
198
|
+
defaultSubSecondPrecision: TimeValuePrecision.nanoseconds
|
|
199
|
+
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
applyRowContext,
|
|
3
|
+
CompatibilityContext,
|
|
4
|
+
SQLITE_TRUE,
|
|
5
|
+
SqliteInputRow,
|
|
6
|
+
TimeValuePrecision
|
|
7
|
+
} from '@powersync/service-sync-rules';
|
|
2
8
|
import { afterAll, beforeEach, describe, expect, test } from 'vitest';
|
|
3
9
|
import { clearTestDb, createUpperCaseUUID, TEST_CONNECTION_OPTIONS, waitForPendingCDCChanges } from './util.js';
|
|
4
10
|
import { CDCToSqliteRow, toSqliteInputRow } from '@module/common/mssqls-to-sqlite.js';
|
|
@@ -43,10 +49,10 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
43
49
|
|
|
44
50
|
date_col DATE,
|
|
45
51
|
datetime_col DATETIME,
|
|
46
|
-
datetime2_col DATETIME2(
|
|
52
|
+
datetime2_col DATETIME2(7),
|
|
47
53
|
smalldatetime_col SMALLDATETIME,
|
|
48
54
|
datetimeoffset_col DATETIMEOFFSET(3),
|
|
49
|
-
time_col TIME(
|
|
55
|
+
time_col TIME(7),
|
|
50
56
|
|
|
51
57
|
char_col CHAR(10),
|
|
52
58
|
varchar_col VARCHAR(255),
|
|
@@ -214,7 +220,14 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
214
220
|
|
|
215
221
|
test('Date types mappings', async () => {
|
|
216
222
|
const beforeLSN = await getLatestLSN(connectionManager);
|
|
217
|
-
const testDate = new Date('2023-03-06T15:47:00.
|
|
223
|
+
const testDate = new Date('2023-03-06T15:47:00.123Z');
|
|
224
|
+
// This adds 0.4567 milliseconds to the JS date, see https://github.com/tediousjs/tedious/blob/0c256f186600d7230aec05553ebad209bed81acc/src/data-types/datetime2.ts#L74.
|
|
225
|
+
// Note that there's a typo in tedious there. When reading dates, the property is actually called nanosecondsDelta.
|
|
226
|
+
// This is only relevant when binding datetime values, so only in this test.
|
|
227
|
+
Object.defineProperty(testDate, 'nanosecondDelta', {
|
|
228
|
+
enumerable: false,
|
|
229
|
+
value: 0.0004567
|
|
230
|
+
});
|
|
218
231
|
await connectionManager.query(
|
|
219
232
|
`
|
|
220
233
|
INSERT INTO ${escapeIdentifier(connectionManager.schema)}.test_data(
|
|
@@ -235,9 +248,9 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
235
248
|
[
|
|
236
249
|
{ name: 'date_col', type: sql.Date, value: testDate },
|
|
237
250
|
{ name: 'datetime_col', type: sql.DateTime, value: testDate },
|
|
238
|
-
{ name: 'datetime2_col', type: sql.DateTime2(
|
|
251
|
+
{ name: 'datetime2_col', type: sql.DateTime2(7), value: testDate },
|
|
239
252
|
{ name: 'smalldatetime_col', type: sql.SmallDateTime, value: testDate },
|
|
240
|
-
{ name: 'time_col', type: sql.Time(
|
|
253
|
+
{ name: 'time_col', type: sql.Time(7), value: testDate }
|
|
241
254
|
]
|
|
242
255
|
);
|
|
243
256
|
await waitForPendingCDCChanges(beforeLSN, connectionManager);
|
|
@@ -246,14 +259,32 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
246
259
|
const replicatedRows = await getReplicatedRows(connectionManager, 'test_data');
|
|
247
260
|
const expectedResult = {
|
|
248
261
|
date_col: '2023-03-06',
|
|
249
|
-
datetime_col: '2023-03-06T15:47:00.
|
|
250
|
-
datetime2_col: '2023-03-06T15:47:00.
|
|
251
|
-
smalldatetime_col: '2023-03-06T15:47:00.
|
|
252
|
-
time_col: '15:47:00.
|
|
262
|
+
datetime_col: '2023-03-06T15:47:00.123000000Z',
|
|
263
|
+
datetime2_col: '2023-03-06T15:47:00.123456700Z',
|
|
264
|
+
smalldatetime_col: '2023-03-06T15:47:00.000000000Z',
|
|
265
|
+
time_col: '15:47:00.123456700'
|
|
253
266
|
};
|
|
254
267
|
|
|
255
|
-
expect(databaseRows[0]).toMatchObject(
|
|
256
|
-
|
|
268
|
+
expect(applyRowContext(databaseRows[0], CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY)).toMatchObject(
|
|
269
|
+
expectedResult
|
|
270
|
+
);
|
|
271
|
+
expect(applyRowContext(replicatedRows[0], CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY)).toMatchObject(
|
|
272
|
+
expectedResult
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const restrictedPrecisionResult = {
|
|
276
|
+
date_col: '2023-03-06',
|
|
277
|
+
datetime_col: '2023-03-06T15:47:00.123000Z',
|
|
278
|
+
datetime2_col: '2023-03-06T15:47:00.123456Z',
|
|
279
|
+
smalldatetime_col: '2023-03-06T15:47:00.000000Z',
|
|
280
|
+
time_col: '15:47:00.123456'
|
|
281
|
+
};
|
|
282
|
+
const restrictedPrecision = new CompatibilityContext({
|
|
283
|
+
edition: 2,
|
|
284
|
+
maxTimeValuePrecision: TimeValuePrecision.microseconds
|
|
285
|
+
});
|
|
286
|
+
expect(applyRowContext(databaseRows[0], restrictedPrecision)).toMatchObject(restrictedPrecisionResult);
|
|
287
|
+
expect(applyRowContext(replicatedRows[0], restrictedPrecision)).toMatchObject(restrictedPrecisionResult);
|
|
257
288
|
});
|
|
258
289
|
|
|
259
290
|
test('Date types edge cases mappings', async () => {
|
|
@@ -277,18 +308,22 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
277
308
|
await waitForPendingCDCChanges(beforeLSN, connectionManager);
|
|
278
309
|
|
|
279
310
|
const expectedResults = [
|
|
280
|
-
{ datetime2_col: '0001-01-01T00:00:00.
|
|
281
|
-
{ datetime2_col: '9999-12-31T23:59:59.
|
|
282
|
-
{ datetime_col: '1753-01-01T00:00:00.
|
|
283
|
-
{ datetime_col: '9999-12-31T23:59:59.
|
|
311
|
+
{ datetime2_col: '0001-01-01T00:00:00.000000000Z' },
|
|
312
|
+
{ datetime2_col: '9999-12-31T23:59:59.999000000Z' },
|
|
313
|
+
{ datetime_col: '1753-01-01T00:00:00.000000000Z' },
|
|
314
|
+
{ datetime_col: '9999-12-31T23:59:59.997000000Z' }
|
|
284
315
|
];
|
|
285
316
|
|
|
286
317
|
const databaseRows = await getDatabaseRows(connectionManager, 'test_data');
|
|
287
318
|
const replicatedRows = await getReplicatedRows(connectionManager, 'test_data');
|
|
288
319
|
|
|
289
320
|
for (let i = 0; i < expectedResults.length; i++) {
|
|
290
|
-
expect(databaseRows[i]).toMatchObject(
|
|
291
|
-
|
|
321
|
+
expect(applyRowContext(databaseRows[i], CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY)).toMatchObject(
|
|
322
|
+
expectedResults[i]
|
|
323
|
+
);
|
|
324
|
+
expect(applyRowContext(replicatedRows[i], CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY)).toMatchObject(
|
|
325
|
+
expectedResults[i]
|
|
326
|
+
);
|
|
292
327
|
}
|
|
293
328
|
});
|
|
294
329
|
|
|
@@ -302,15 +337,19 @@ describe('MSSQL Data Types Tests', () => {
|
|
|
302
337
|
await waitForPendingCDCChanges(beforeLSN, connectionManager);
|
|
303
338
|
|
|
304
339
|
const expectedResult = {
|
|
305
|
-
datetimeoffset_col: '2023-03-06T10:47:00.
|
|
340
|
+
datetimeoffset_col: '2023-03-06T10:47:00.000000000Z' // Converted to UTC
|
|
306
341
|
};
|
|
307
342
|
|
|
308
343
|
const databaseRows = await getDatabaseRows(connectionManager, 'test_data');
|
|
309
344
|
const replicatedRows = await getReplicatedRows(connectionManager, 'test_data');
|
|
310
345
|
|
|
311
346
|
// Note: The driver converts DateTimeOffset to Date, which incorporates the timezone offset which is then represented in UTC.
|
|
312
|
-
expect(databaseRows[0]).toMatchObject(
|
|
313
|
-
|
|
347
|
+
expect(applyRowContext(databaseRows[0], CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY)).toMatchObject(
|
|
348
|
+
expectedResult
|
|
349
|
+
);
|
|
350
|
+
expect(applyRowContext(replicatedRows[0], CompatibilityContext.FULL_BACKWARDS_COMPATIBILITY)).toMatchObject(
|
|
351
|
+
expectedResult
|
|
352
|
+
);
|
|
314
353
|
});
|
|
315
354
|
|
|
316
355
|
test('UniqueIdentifier type mapping', async () => {
|