@quantform/sqlite 0.6.7 → 0.7.0-beta.5
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/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/sqlite-language.d.ts +10 -0
- package/dist/sqlite-language.d.ts.map +1 -0
- package/dist/sqlite-language.js +80 -0
- package/dist/sqlite-language.spec.d.ts +2 -0
- package/dist/sqlite-language.spec.d.ts.map +1 -0
- package/dist/sqlite-language.spec.js +50 -0
- package/dist/sqlite-storage.d.ts +11 -8
- package/dist/sqlite-storage.d.ts.map +1 -1
- package/dist/sqlite-storage.js +54 -58
- package/dist/sqlite-storage.spec.d.ts +2 -0
- package/dist/sqlite-storage.spec.d.ts.map +1 -0
- package/dist/sqlite-storage.spec.js +204 -0
- package/package.json +3 -3
- package/src/index.ts +7 -0
- package/src/sqlite-language.spec.ts +53 -0
- package/src/sqlite-language.ts +114 -0
- package/src/sqlite-storage.spec.ts +232 -0
- package/src/sqlite-storage.ts +58 -83
- package/dist/error.d.ts +0 -4
- package/dist/error.d.ts.map +0 -1
- package/dist/error.js +0 -9
- package/dist/sqlite-feed.spec.d.ts +0 -2
- package/dist/sqlite-feed.spec.d.ts.map +0 -1
- package/dist/sqlite-feed.spec.js +0 -120
- package/dist/sqlite-measurement.spec.d.ts +0 -2
- package/dist/sqlite-measurement.spec.d.ts.map +0 -1
- package/dist/sqlite-measurement.spec.js +0 -122
- package/src/error.ts +0 -5
- package/src/sqlite-feed.spec.ts +0 -158
- package/src/sqlite-measurement.spec.ts +0 -151
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { d, eq, lt, Storage } from '@quantform/core';
|
|
2
|
+
|
|
3
|
+
import { SQLiteLanguage } from './sqlite-language';
|
|
4
|
+
|
|
5
|
+
describe(SQLiteLanguage.name, () => {
|
|
6
|
+
test('create table for object', async () => {
|
|
7
|
+
const object = Storage.createObject('orders', {
|
|
8
|
+
timestamp: 'number',
|
|
9
|
+
price: 'decimal',
|
|
10
|
+
id: 'string'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const sql = SQLiteLanguage.createTable(object);
|
|
14
|
+
|
|
15
|
+
expect(sql).toEqual(
|
|
16
|
+
`CREATE TABLE IF NOT EXISTS "orders" (timestamp INTEGER NOT NULL, price TEXT NOT NULL, id TEXT NOT NULL, PRIMARY KEY (timestamp))`
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('select to query object', async () => {
|
|
21
|
+
const object = Storage.createObject('orders', {
|
|
22
|
+
timestamp: 'number',
|
|
23
|
+
price: 'decimal',
|
|
24
|
+
id: 'string'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const sql = SQLiteLanguage.query(object, {
|
|
28
|
+
where: {
|
|
29
|
+
timestamp: lt(100),
|
|
30
|
+
id: eq('unique-id'),
|
|
31
|
+
price: eq(d('1.123456789123456789'))
|
|
32
|
+
},
|
|
33
|
+
limit: 3,
|
|
34
|
+
offset: 2
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(sql).toEqual(
|
|
38
|
+
`SELECT timestamp, price, id FROM "orders" WHERE timestamp < 100 AND id = 'unique-id' AND price = '1.123456789123456789' LIMIT 3 OFFSET 2`
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('replace statement', async () => {
|
|
43
|
+
const object = Storage.createObject('orders', {
|
|
44
|
+
timestamp: 'number',
|
|
45
|
+
price: 'decimal',
|
|
46
|
+
id: 'string'
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const sql = SQLiteLanguage.replace(object);
|
|
50
|
+
|
|
51
|
+
expect(sql).toEqual(`REPLACE INTO "orders" (timestamp, price, id) VALUES(?, ?, ?)`);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InferQueryObject,
|
|
3
|
+
Query,
|
|
4
|
+
QueryMappingType,
|
|
5
|
+
QueryObject,
|
|
6
|
+
QueryObjectType,
|
|
7
|
+
QueryWhere
|
|
8
|
+
} from '@quantform/core';
|
|
9
|
+
|
|
10
|
+
export class SQLiteLanguage {
|
|
11
|
+
static getType(type: QueryMappingType) {
|
|
12
|
+
switch (type) {
|
|
13
|
+
case 'number':
|
|
14
|
+
return 'INTEGER';
|
|
15
|
+
case 'decimal':
|
|
16
|
+
return 'TEXT';
|
|
17
|
+
case 'string':
|
|
18
|
+
return 'TEXT';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static getValue(type: QueryMappingType, value: any) {
|
|
23
|
+
switch (type) {
|
|
24
|
+
case 'number':
|
|
25
|
+
return value;
|
|
26
|
+
case 'decimal':
|
|
27
|
+
return `'${value.toString()}'`;
|
|
28
|
+
case 'string':
|
|
29
|
+
return `'${value}'`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static getConstraint(
|
|
34
|
+
columnName: string,
|
|
35
|
+
type: QueryMappingType,
|
|
36
|
+
constraint: QueryWhere
|
|
37
|
+
) {
|
|
38
|
+
switch (constraint?.type) {
|
|
39
|
+
case 'eq':
|
|
40
|
+
return `${columnName} = ${SQLiteLanguage.getValue(type, constraint.value)}`;
|
|
41
|
+
case 'gt':
|
|
42
|
+
return `${columnName} > ${SQLiteLanguage.getValue(type, constraint.value)}`;
|
|
43
|
+
case 'lt':
|
|
44
|
+
return `${columnName} < ${SQLiteLanguage.getValue(type, constraint.value)}`;
|
|
45
|
+
case 'between':
|
|
46
|
+
return `${columnName} > ${SQLiteLanguage.getValue(
|
|
47
|
+
type,
|
|
48
|
+
constraint.min
|
|
49
|
+
)} AND ${columnName} < ${SQLiteLanguage.getValue(type, constraint.max)}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static createTable<T extends QueryObjectType<K>, K extends QueryObject>(type: T) {
|
|
54
|
+
const columns = `${Object.entries(type.type)
|
|
55
|
+
.map(([name, type]) => `${name} ${SQLiteLanguage.getType(type)} NOT NULL`)
|
|
56
|
+
.join(', ')}`;
|
|
57
|
+
|
|
58
|
+
return `CREATE TABLE IF NOT EXISTS "${type.discriminator}" (${columns}, PRIMARY KEY (timestamp))`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static query<T extends QueryObjectType<K>, K extends QueryObject>(
|
|
62
|
+
type: T,
|
|
63
|
+
query: Query<InferQueryObject<T>>
|
|
64
|
+
) {
|
|
65
|
+
const columns = `${Object.entries(type.type)
|
|
66
|
+
.map(([name]) => `${name}`)
|
|
67
|
+
.join(', ')}`;
|
|
68
|
+
|
|
69
|
+
let sql = `SELECT ${columns} FROM "${type.discriminator}"`;
|
|
70
|
+
|
|
71
|
+
if (query.where) {
|
|
72
|
+
const where = Array.of<string>();
|
|
73
|
+
|
|
74
|
+
for (const columnName of Object.keys(query.where)) {
|
|
75
|
+
const constraint = query.where[columnName];
|
|
76
|
+
if (!constraint) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
where.push(
|
|
81
|
+
SQLiteLanguage.getConstraint(columnName, type.type[columnName], constraint)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
sql = `${sql} WHERE ${where.join(' AND ')}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (query.orderBy) {
|
|
89
|
+
sql = `${sql} ORDER BY timestamp ${query.orderBy ?? 'ASC'}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (query.limit) {
|
|
93
|
+
sql = `${sql} LIMIT ${query.limit}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (query.offset) {
|
|
97
|
+
sql = `${sql} OFFSET ${query.offset}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return sql;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static replace<T extends QueryObjectType<K>, K extends QueryObject>(type: T) {
|
|
104
|
+
const columns = `${Object.entries(type.type)
|
|
105
|
+
.map(([name]) => `${name}`)
|
|
106
|
+
.join(', ')}`;
|
|
107
|
+
|
|
108
|
+
return `REPLACE INTO "${type.discriminator}" (${columns}) VALUES(${Object.entries(
|
|
109
|
+
type.type
|
|
110
|
+
)
|
|
111
|
+
.map(() => '?')
|
|
112
|
+
.join(', ')})`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
2
|
+
|
|
3
|
+
import { d, eq, gt, lt, makeTestModule, Storage } from '@quantform/core';
|
|
4
|
+
|
|
5
|
+
import { SQLiteStorage } from './sqlite-storage';
|
|
6
|
+
|
|
7
|
+
describe(SQLiteStorage.name, () => {
|
|
8
|
+
let fixtures: Awaited<ReturnType<typeof getFixtures>>;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
fixtures = await getFixtures();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
fixtures.dispose();
|
|
16
|
+
});
|
|
17
|
+
/*
|
|
18
|
+
test('index return the names of discriminators', async () => {
|
|
19
|
+
const { sut } = fixtures;
|
|
20
|
+
|
|
21
|
+
await sut.save({ discriminator: 'pricing' }, [{ timestamp: 1, message: 'test-1' }]);
|
|
22
|
+
await sut.save({ discriminator: 'ordering' }, [{ timestamp: 1, message: 'test-1' }]);
|
|
23
|
+
|
|
24
|
+
const index = await sut.index();
|
|
25
|
+
|
|
26
|
+
expect(index).toEqual(['pricing', 'ordering']);
|
|
27
|
+
});
|
|
28
|
+
*/
|
|
29
|
+
test('write and read single object', async () => {
|
|
30
|
+
const { sut, object } = fixtures;
|
|
31
|
+
|
|
32
|
+
await sut.save(object, [
|
|
33
|
+
{ timestamp: 1, id: '123 123', price: d('1.123456789123456789'), quantity: 5 }
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const set = await sut.query(object, {
|
|
37
|
+
where: {
|
|
38
|
+
id: eq('123 123')
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(set).toEqual([
|
|
43
|
+
{ timestamp: 1, id: '123 123', price: d('1.123456789123456789'), quantity: 5 }
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('save and read full data', async () => {
|
|
48
|
+
const { sut } = fixtures;
|
|
49
|
+
|
|
50
|
+
const pricing = Storage.createObject('pricing', {
|
|
51
|
+
timestamp: 'number',
|
|
52
|
+
rate: 'decimal'
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await sut.save(pricing, [
|
|
56
|
+
{ timestamp: 1, rate: d(1) },
|
|
57
|
+
{ timestamp: 2, rate: d(2) },
|
|
58
|
+
{ timestamp: 3, rate: d(3) },
|
|
59
|
+
{ timestamp: 4, rate: d(4) },
|
|
60
|
+
{ timestamp: 5, rate: d(5) }
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const set = await sut.query(pricing, {});
|
|
64
|
+
|
|
65
|
+
expect(set).toEqual([
|
|
66
|
+
{ timestamp: 1, rate: d(1) },
|
|
67
|
+
{ timestamp: 2, rate: d(2) },
|
|
68
|
+
{ timestamp: 3, rate: d(3) },
|
|
69
|
+
{ timestamp: 4, rate: d(4) },
|
|
70
|
+
{ timestamp: 5, rate: d(5) }
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('save and read limited data', async () => {
|
|
75
|
+
const { sut } = fixtures;
|
|
76
|
+
|
|
77
|
+
const pricing = Storage.createObject('pricing', {
|
|
78
|
+
timestamp: 'number',
|
|
79
|
+
rate: 'decimal'
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await sut.save(pricing, [
|
|
83
|
+
{ timestamp: 1, rate: d(1) },
|
|
84
|
+
{ timestamp: 2, rate: d(2) },
|
|
85
|
+
{ timestamp: 3, rate: d(3) },
|
|
86
|
+
{ timestamp: 4, rate: d(4) },
|
|
87
|
+
{ timestamp: 5, rate: d(5) }
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
const set = await sut.query(pricing, { limit: 3 });
|
|
91
|
+
|
|
92
|
+
expect(set).toEqual([
|
|
93
|
+
{ timestamp: 1, rate: d(1) },
|
|
94
|
+
{ timestamp: 2, rate: d(2) },
|
|
95
|
+
{ timestamp: 3, rate: d(3) }
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('save and read desc ordered data', async () => {
|
|
100
|
+
const { sut } = fixtures;
|
|
101
|
+
|
|
102
|
+
const pricing = Storage.createObject('pricing', {
|
|
103
|
+
timestamp: 'number',
|
|
104
|
+
rate: 'decimal'
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await sut.save(pricing, [
|
|
108
|
+
{ timestamp: 1, rate: d(1) },
|
|
109
|
+
{ timestamp: 2, rate: d(2) },
|
|
110
|
+
{ timestamp: 3, rate: d(3) },
|
|
111
|
+
{ timestamp: 4, rate: d(4) },
|
|
112
|
+
{ timestamp: 5, rate: d(5) }
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
const set = await sut.query(pricing, { orderBy: 'DESC' });
|
|
116
|
+
|
|
117
|
+
expect(set).toEqual([
|
|
118
|
+
{ timestamp: 5, rate: d(5) },
|
|
119
|
+
{ timestamp: 4, rate: d(4) },
|
|
120
|
+
{ timestamp: 3, rate: d(3) },
|
|
121
|
+
{ timestamp: 2, rate: d(2) },
|
|
122
|
+
{ timestamp: 1, rate: d(1) }
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('save and read filtered eq data', async () => {
|
|
127
|
+
const { sut } = fixtures;
|
|
128
|
+
|
|
129
|
+
const pricing = Storage.createObject('pricing', {
|
|
130
|
+
timestamp: 'number',
|
|
131
|
+
rate: 'decimal'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await sut.save(pricing, [
|
|
135
|
+
{ timestamp: 1, rate: d(1) },
|
|
136
|
+
{ timestamp: 2, rate: d(2) },
|
|
137
|
+
{ timestamp: 3, rate: d(3) },
|
|
138
|
+
{ timestamp: 4, rate: d(4) },
|
|
139
|
+
{ timestamp: 5, rate: d(5) }
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const set = await sut.query(pricing, {
|
|
143
|
+
where: {
|
|
144
|
+
timestamp: eq(4)
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(set).toEqual([{ timestamp: 4, rate: d(4) }]);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('save and read filtered lt data', async () => {
|
|
152
|
+
const { sut } = fixtures;
|
|
153
|
+
|
|
154
|
+
const pricing = Storage.createObject('pricing', {
|
|
155
|
+
timestamp: 'number',
|
|
156
|
+
rate: 'decimal'
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await sut.save(pricing, [
|
|
160
|
+
{ timestamp: 1, rate: d(1) },
|
|
161
|
+
{ timestamp: 2, rate: d(2) },
|
|
162
|
+
{ timestamp: 3, rate: d(3) },
|
|
163
|
+
{ timestamp: 4, rate: d(4) },
|
|
164
|
+
{ timestamp: 5, rate: d(5) }
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const set = await sut.query(pricing, {
|
|
168
|
+
where: {
|
|
169
|
+
timestamp: lt(3)
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(set).toEqual([
|
|
174
|
+
{ timestamp: 1, rate: d(1) },
|
|
175
|
+
{ timestamp: 2, rate: d(2) }
|
|
176
|
+
]);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('save and read filtered gt data', async () => {
|
|
180
|
+
const { sut } = fixtures;
|
|
181
|
+
|
|
182
|
+
const pricing = Storage.createObject('pricing', {
|
|
183
|
+
timestamp: 'number',
|
|
184
|
+
rate: 'decimal'
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await sut.save(pricing, [
|
|
188
|
+
{ timestamp: 1, rate: d(1) },
|
|
189
|
+
{ timestamp: 2, rate: d(2) },
|
|
190
|
+
{ timestamp: 3, rate: d(3) },
|
|
191
|
+
{ timestamp: 4, rate: d(4) },
|
|
192
|
+
{ timestamp: 5, rate: d(5) }
|
|
193
|
+
]);
|
|
194
|
+
|
|
195
|
+
const set = await sut.query(pricing, {
|
|
196
|
+
where: {
|
|
197
|
+
timestamp: gt(3)
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(set).toEqual([
|
|
202
|
+
{ timestamp: 4, rate: d(4) },
|
|
203
|
+
{ timestamp: 5, rate: d(5) }
|
|
204
|
+
]);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
async function getFixtures() {
|
|
209
|
+
const { get } = await makeTestModule([
|
|
210
|
+
{
|
|
211
|
+
provide: 'storage',
|
|
212
|
+
useValue: new SQLiteStorage('test.db')
|
|
213
|
+
}
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
const sut = get<SQLiteStorage>('storage');
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
sut,
|
|
220
|
+
object: Storage.createObject('test', {
|
|
221
|
+
timestamp: 'number',
|
|
222
|
+
price: 'decimal',
|
|
223
|
+
quantity: 'number',
|
|
224
|
+
id: 'string'
|
|
225
|
+
}),
|
|
226
|
+
dispose() {
|
|
227
|
+
if (existsSync(sut.filename)) {
|
|
228
|
+
unlinkSync(sut.filename);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
package/src/sqlite-storage.ts
CHANGED
|
@@ -4,35 +4,35 @@ import { existsSync, mkdirSync } from 'fs';
|
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
d,
|
|
8
|
+
InferQueryObject,
|
|
9
|
+
provider,
|
|
10
|
+
Query,
|
|
11
|
+
QueryObject,
|
|
12
|
+
QueryObjectType,
|
|
9
13
|
Storage,
|
|
10
|
-
|
|
11
|
-
StorageQueryOptions,
|
|
14
|
+
StorageFactory,
|
|
12
15
|
workingDirectory
|
|
13
16
|
} from '@quantform/core';
|
|
14
17
|
|
|
15
|
-
import {
|
|
18
|
+
import { SQLiteLanguage } from './sqlite-language';
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
@provider()
|
|
21
|
+
export class SQLiteStorageFactory implements StorageFactory {
|
|
22
|
+
constructor(private readonly directory?: string) {}
|
|
23
|
+
|
|
24
|
+
for(key: string): Storage {
|
|
25
|
+
return new SQLiteStorage(
|
|
26
|
+
join(this.directory ?? workingDirectory(), `/${key}.sqlite`)
|
|
22
27
|
);
|
|
23
|
-
}
|
|
28
|
+
}
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
export class SQLiteStorage implements Storage {
|
|
27
|
-
protected connection
|
|
28
|
-
|
|
29
|
-
constructor(private readonly filename: string) {}
|
|
30
|
-
|
|
31
|
-
private tryConnect() {
|
|
32
|
-
if (this.connection) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
32
|
+
protected connection: Database;
|
|
33
|
+
private tables?: string[];
|
|
35
34
|
|
|
35
|
+
constructor(readonly filename: string) {
|
|
36
36
|
if (!existsSync(dirname(this.filename))) {
|
|
37
37
|
mkdirSync(dirname(this.filename), { recursive: true });
|
|
38
38
|
}
|
|
@@ -40,92 +40,67 @@ export class SQLiteStorage implements Storage {
|
|
|
40
40
|
this.connection = bettersqlite3(this.filename);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
private tryCreateTable(table: string) {
|
|
44
|
-
if (!this.connection) {
|
|
45
|
-
throw new NoConnectionError();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.connection.exec(
|
|
49
|
-
`CREATE TABLE IF NOT EXISTS "${table}" (
|
|
50
|
-
timestamp INTEGER NOT NULL,
|
|
51
|
-
kind TEXT NOT NULL,
|
|
52
|
-
json TEXT NOT NULL,
|
|
53
|
-
PRIMARY KEY (timestamp, kind)
|
|
54
|
-
)`
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
43
|
async index(): Promise<Array<string>> {
|
|
59
|
-
this.tryConnect();
|
|
60
|
-
|
|
61
|
-
if (!this.connection) {
|
|
62
|
-
throw new NoConnectionError();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
44
|
return this.connection
|
|
66
45
|
.prepare("SELECT name FROM sqlite_master WHERE type='table'")
|
|
67
46
|
.all()
|
|
68
47
|
.map(it => it.name);
|
|
69
48
|
}
|
|
70
49
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
// eslint-disable-next-line complexity
|
|
51
|
+
async query<T extends QueryObjectType<K>, K extends QueryObject>(
|
|
52
|
+
type: T,
|
|
53
|
+
query: Query<InferQueryObject<T>>
|
|
54
|
+
): Promise<InferQueryObject<T>[]> {
|
|
55
|
+
if (!this.tables) {
|
|
56
|
+
this.tables = await this.index();
|
|
76
57
|
}
|
|
77
58
|
|
|
78
|
-
if (
|
|
79
|
-
!this.connection
|
|
80
|
-
.prepare(
|
|
81
|
-
`SELECT name FROM sqlite_master WHERE type='table' AND name='${library}'`
|
|
82
|
-
)
|
|
83
|
-
.all().length
|
|
84
|
-
) {
|
|
59
|
+
if (!this.tables.includes(type.discriminator)) {
|
|
85
60
|
return [];
|
|
86
61
|
}
|
|
87
62
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.prepare(
|
|
92
|
-
`SELECT * FROM "${library}"
|
|
93
|
-
WHERE timestamp > ? AND timestamp < ? ${
|
|
94
|
-
options.kind ? `AND kind = '${options.kind}'` : ''
|
|
95
|
-
}
|
|
96
|
-
ORDER BY timestamp ${isBackward ? 'DESC' : ''}
|
|
97
|
-
LIMIT ?`
|
|
98
|
-
)
|
|
99
|
-
.all(
|
|
100
|
-
[options.from ?? 0, options.to ?? Number.MAX_VALUE],
|
|
101
|
-
Math.min(options.count, 50000)
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if (isBackward) {
|
|
105
|
-
rows = rows.reverse();
|
|
106
|
-
}
|
|
63
|
+
const objects = await this.connection
|
|
64
|
+
.prepare(SQLiteLanguage.query(type, query))
|
|
65
|
+
.all();
|
|
107
66
|
|
|
108
|
-
|
|
67
|
+
const types = Object.keys(type.type);
|
|
68
|
+
|
|
69
|
+
objects.forEach(it => {
|
|
70
|
+
for (const prop of types) {
|
|
71
|
+
if (type.type[prop] == 'decimal') {
|
|
72
|
+
it[prop] = d(it[prop]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return objects;
|
|
109
78
|
}
|
|
110
79
|
|
|
111
|
-
async save
|
|
112
|
-
|
|
80
|
+
async save<T extends QueryObjectType<K>, K extends QueryObject>(
|
|
81
|
+
type: T,
|
|
82
|
+
objects: InferQueryObject<T>[]
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
if (!this.tables) {
|
|
85
|
+
this.tables = await this.index();
|
|
86
|
+
}
|
|
113
87
|
|
|
114
|
-
if (!this.
|
|
115
|
-
|
|
88
|
+
if (!this.tables.includes(type.discriminator)) {
|
|
89
|
+
this.connection.exec(SQLiteLanguage.createTable(type));
|
|
90
|
+
|
|
91
|
+
this.tables = undefined;
|
|
116
92
|
}
|
|
117
93
|
|
|
118
|
-
this.
|
|
94
|
+
const statement = this.connection.prepare(SQLiteLanguage.replace(type));
|
|
95
|
+
|
|
96
|
+
const types = Object.keys(type.type);
|
|
119
97
|
|
|
120
|
-
const
|
|
121
|
-
REPLACE INTO "${library}" (timestamp, kind, json)
|
|
122
|
-
VALUES(?, ?, ?);
|
|
123
|
-
`);
|
|
98
|
+
const mapper = (it: InferQueryObject<T>) => types.map(type => it[type].toString());
|
|
124
99
|
|
|
125
100
|
const insertMany = this.connection.transaction(rows =>
|
|
126
|
-
rows.forEach((it:
|
|
101
|
+
rows.forEach((it: InferQueryObject<T>) => statement.run(mapper(it)))
|
|
127
102
|
);
|
|
128
103
|
|
|
129
|
-
insertMany(
|
|
104
|
+
insertMany(objects);
|
|
130
105
|
}
|
|
131
106
|
}
|
package/dist/error.d.ts
DELETED
package/dist/error.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,qBAAa,iBAAkB,SAAQ,KAAK;;CAI3C"}
|
package/dist/error.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NoConnectionError = void 0;
|
|
4
|
-
class NoConnectionError extends Error {
|
|
5
|
-
constructor() {
|
|
6
|
-
super('Missing database connection connection!');
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
exports.NoConnectionError = NoConnectionError;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-feed.spec.d.ts","sourceRoot":"","sources":["../src/sqlite-feed.spec.ts"],"names":[],"mappings":""}
|