@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] = toISOTimeString(row[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
- result[key] = isNaN(date.getTime()) ? null : date.toISOString();
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 toISOTimeString(date) {
74
- return isNaN(date.getTime()) ? null : date.toISOString().split('T')[1].replace('Z', '');
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":"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"}
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.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.13",
31
- "@powersync/service-core": "1.18.1",
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.9",
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.15",
42
- "@powersync/service-module-mongodb-storage": "0.12.15",
43
- "@powersync/service-module-postgres-storage": "0.10.15"
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] = toISOTimeString(row[key] as Date);
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 Date;
42
- result[key] = isNaN(date.getTime()) ? null : date.toISOString();
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 toISOTimeString(date: Date): string | null {
83
- return isNaN(date.getTime()) ? null : date.toISOString().split('T')[1].replace('Z', '');
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 { SQLITE_TRUE, SqliteInputRow } from '@powersync/service-sync-rules';
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(6),
52
+ datetime2_col DATETIME2(7),
47
53
  smalldatetime_col SMALLDATETIME,
48
54
  datetimeoffset_col DATETIMEOFFSET(3),
49
- time_col TIME(6),
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.000Z');
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(6), value: testDate },
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(6), value: testDate }
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.000Z',
250
- datetime2_col: '2023-03-06T15:47:00.000Z',
251
- smalldatetime_col: '2023-03-06T15:47:00.000Z',
252
- time_col: '15:47:00.000'
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(expectedResult);
256
- expect(replicatedRows[0]).toMatchObject(expectedResult);
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.000Z' },
281
- { datetime2_col: '9999-12-31T23:59:59.999Z' },
282
- { datetime_col: '1753-01-01T00:00:00.000Z' },
283
- { datetime_col: '9999-12-31T23:59:59.997Z' }
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(expectedResults[i]);
291
- expect(replicatedRows[i]).toMatchObject(expectedResults[i]);
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.000Z' // Converted to UTC
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(expectedResult);
313
- expect(replicatedRows[0]).toMatchObject(expectedResult);
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 () => {