Skip to content

Commit f89a3b5

Browse files
authored
Merge pull request #119 from powersync-ja/mongodb-types
[MongoDB] Support Decimal type
2 parents 5f31083 + 3bca30f commit f89a3b5

File tree

6 files changed

+105
-10
lines changed

6 files changed

+105
-10
lines changed

.github/workflows/test.yml

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ jobs:
152152
strategy:
153153
fail-fast: false
154154
matrix:
155-
mysql-version: [ 8.0, 8.4 ]
155+
mysql-version: [8.0, 8.4]
156156

157157
steps:
158158
- uses: actions/checkout@v4
@@ -208,3 +208,56 @@ jobs:
208208

209209
- name: Test
210210
run: pnpm test --filter='./modules/module-mysql'
211+
212+
run-mongodb-tests:
213+
name: MongoDB Test
214+
runs-on: ubuntu-latest
215+
needs: run-core-tests
216+
217+
strategy:
218+
fail-fast: false
219+
matrix:
220+
mongodb-version: ['6.0', '7.0', '8.0']
221+
222+
steps:
223+
- uses: actions/checkout@v4
224+
225+
- name: Start MongoDB
226+
uses: supercharge/[email protected]
227+
with:
228+
mongodb-version: ${{ matrix.mongodb-version }}
229+
mongodb-replica-set: test-rs
230+
231+
- name: Setup Node.js
232+
uses: actions/setup-node@v4
233+
with:
234+
node-version-file: '.nvmrc'
235+
236+
- uses: pnpm/action-setup@v4
237+
name: Install pnpm
238+
with:
239+
version: 9
240+
run_install: false
241+
242+
- name: Get pnpm store directory
243+
shell: bash
244+
run: |
245+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
246+
247+
- uses: actions/cache@v3
248+
name: Setup pnpm cache
249+
with:
250+
path: ${{ env.STORE_PATH }}
251+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
252+
restore-keys: |
253+
${{ runner.os }}-pnpm-store-
254+
255+
- name: Install dependencies
256+
run: pnpm install
257+
258+
- name: Build
259+
shell: bash
260+
run: pnpm build
261+
262+
- name: Test
263+
run: pnpm test --filter='./modules/module-mongodb'

modules/module-mongodb/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"build": "tsc -b",
1414
"build:tests": "tsc -b test/tsconfig.json",
1515
"clean": "rm -rf ./dist && tsc -b --clean",
16-
"test": "vitest --no-threads"
16+
"test": "vitest"
1717
},
1818
"exports": {
1919
".": {

modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,14 @@ export class MongoRouteAPIAdapter implements api.RouteAPI {
313313
return 'Binary';
314314
} else if (data instanceof mongo.Long) {
315315
return 'Long';
316+
} else if (data instanceof RegExp) {
317+
return 'RegExp';
318+
} else if (data instanceof mongo.MinKey) {
319+
return 'MinKey';
320+
} else if (data instanceof mongo.MaxKey) {
321+
return 'MaxKey';
322+
} else if (data instanceof mongo.Decimal128) {
323+
return 'Decimal';
316324
} else if (Array.isArray(data)) {
317325
return 'Array';
318326
} else if (data instanceof Uint8Array) {

modules/module-mongodb/src/replication/MongoRelation.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ export function toMongoSyncRulesValue(data: any): SqliteValue {
6262
return new Uint8Array(data.buffer);
6363
} else if (data instanceof mongo.Long) {
6464
return data.toBigInt();
65+
} else if (data instanceof mongo.Decimal128) {
66+
return data.toString();
67+
} else if (data instanceof mongo.MinKey || data instanceof mongo.MaxKey) {
68+
return null;
69+
} else if (data instanceof RegExp) {
70+
return JSON.stringify({ pattern: data.source, options: data.flags });
6571
} else if (Array.isArray(data)) {
6672
// We may be able to avoid some parse + stringify cycles here for JsonSqliteContainer.
6773
return JSONBig.stringify(data.map((element) => filterJsonData(element)));
@@ -112,6 +118,12 @@ function filterJsonData(data: any, depth = 0): any {
112118
return undefined;
113119
} else if (data instanceof mongo.Long) {
114120
return data.toBigInt();
121+
} else if (data instanceof mongo.Decimal128) {
122+
return data.toString();
123+
} else if (data instanceof mongo.MinKey || data instanceof mongo.MaxKey) {
124+
return data._bsontype;
125+
} else if (data instanceof mongo.BSONRegExp) {
126+
return JSON.stringify({ pattern: data.pattern, options: data.options });
115127
} else if (Array.isArray(data)) {
116128
return data.map((element) => filterJsonData(element, depth + 1));
117129
} else if (ArrayBuffer.isView(data)) {

modules/module-mongodb/test/src/mongo_test.test.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ describe('mongo data types', () => {
2323
int2: 1000,
2424
int4: 1000000,
2525
int8: 9007199254740993n,
26-
float: 3.14
26+
float: 3.14,
27+
decimal: new mongo.Decimal128('3.14')
2728
},
2829
{ _id: 2 as any, nested: { test: 'thing' } },
2930
{ _id: 3 as any, date: new Date('2023-03-06 15:47+02') },
3031
{
3132
_id: 4 as any,
3233
timestamp: mongo.Timestamp.fromBits(123, 456),
33-
objectId: mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')
34+
objectId: mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb'),
35+
regexp: new mongo.BSONRegExp('test', 'i'),
36+
minKey: new mongo.MinKey(),
37+
maxKey: new mongo.MaxKey()
3438
}
3539
]);
3640
}
@@ -47,14 +51,18 @@ describe('mongo data types', () => {
4751
int2: [1000],
4852
int4: [1000000],
4953
int8: [9007199254740993n],
50-
float: [3.14]
54+
float: [3.14],
55+
decimal: [new mongo.Decimal128('3.14')]
5156
},
5257
{ _id: 2 as any, nested: [{ test: 'thing' }] },
5358
{ _id: 3 as any, date: [new Date('2023-03-06 15:47+02')] },
5459
{
5560
_id: 10 as any,
5661
timestamp: [mongo.Timestamp.fromBits(123, 456)],
57-
objectId: [mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')]
62+
objectId: [mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')],
63+
regexp: [new mongo.BSONRegExp('test', 'i')],
64+
minKey: [new mongo.MinKey()],
65+
maxKey: [new mongo.MaxKey()]
5866
}
5967
]);
6068
}
@@ -70,7 +78,8 @@ describe('mongo data types', () => {
7078
int4: 1000000n,
7179
int8: 9007199254740993n,
7280
float: 3.14,
73-
null: null
81+
null: null,
82+
decimal: '3.14'
7483
});
7584
expect(transformed[1]).toMatchObject({
7685
_id: 2n,
@@ -85,7 +94,10 @@ describe('mongo data types', () => {
8594
expect(transformed[3]).toMatchObject({
8695
_id: 4n,
8796
objectId: '66e834cc91d805df11fa0ecb',
88-
timestamp: 1958505087099n
97+
timestamp: 1958505087099n,
98+
regexp: '{"pattern":"test","options":"i"}',
99+
minKey: null,
100+
maxKey: null
89101
});
90102
}
91103

@@ -220,21 +232,25 @@ describe('mongo data types', () => {
220232
const schema = await adapter.getConnectionSchema();
221233
const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0];
222234
expect(dbSchema).not.toBeNull();
223-
expect(dbSchema.tables).toEqual([
235+
expect(dbSchema.tables).toMatchObject([
224236
{
225237
name: 'test_data',
226238
columns: [
227239
{ name: '_id', sqlite_type: 4, internal_type: 'Integer' },
228240
{ name: 'bool', sqlite_type: 4, internal_type: 'Boolean' },
229241
{ name: 'bytea', sqlite_type: 1, internal_type: 'Binary' },
230242
{ name: 'date', sqlite_type: 2, internal_type: 'Date' },
243+
{ name: 'decimal', sqlite_type: 2, internal_type: 'Decimal' },
231244
{ name: 'float', sqlite_type: 8, internal_type: 'Double' },
232245
{ name: 'int2', sqlite_type: 4, internal_type: 'Integer' },
233246
{ name: 'int4', sqlite_type: 4, internal_type: 'Integer' },
234247
{ name: 'int8', sqlite_type: 4, internal_type: 'Long' },
248+
{ name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' },
249+
{ name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' },
235250
{ name: 'nested', sqlite_type: 2, internal_type: 'Object' },
236251
{ name: 'null', sqlite_type: 0, internal_type: 'Null' },
237252
{ name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' },
253+
{ name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' },
238254
{ name: 'text', sqlite_type: 2, internal_type: 'String' },
239255
{ name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' },
240256
{ name: 'uuid', sqlite_type: 2, internal_type: 'UUID' }

modules/module-mongodb/vitest.config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import tsconfigPaths from 'vite-tsconfig-paths';
44
export default defineConfig({
55
plugins: [tsconfigPaths()],
66
test: {
7-
setupFiles: './test/src/setup.ts'
7+
setupFiles: './test/src/setup.ts',
8+
poolOptions: {
9+
threads: {
10+
singleThread: true
11+
}
12+
},
13+
pool: 'threads'
814
}
915
});

0 commit comments

Comments
 (0)