@foul11/awesome-db 1.1.0
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 +11 -0
- package/dist/BitFields.d.ts +29 -0
- package/dist/BitFields.d.ts.map +1 -0
- package/dist/Error.d.ts +21 -0
- package/dist/Error.d.ts.map +1 -0
- package/dist/ORM.d.ts +14 -0
- package/dist/ORM.d.ts.map +1 -0
- package/dist/SQLParser.d.ts +1394 -0
- package/dist/SQLParser.d.ts.map +1 -0
- package/dist/WebpackFileProvider.d.ts +12 -0
- package/dist/WebpackFileProvider.d.ts.map +1 -0
- package/dist/alter/column_add.d.ts +7 -0
- package/dist/alter/column_add.d.ts.map +1 -0
- package/dist/alter/column_drop.d.ts +6 -0
- package/dist/alter/column_drop.d.ts.map +1 -0
- package/dist/alter/column_rename.d.ts +6 -0
- package/dist/alter/column_rename.d.ts.map +1 -0
- package/dist/alter/column_update.d.ts +7 -0
- package/dist/alter/column_update.d.ts.map +1 -0
- package/dist/alter/columns_order.d.ts +6 -0
- package/dist/alter/columns_order.d.ts.map +1 -0
- package/dist/alter/index.d.ts +7 -0
- package/dist/alter/index.d.ts.map +1 -0
- package/dist/alter/pragma.d.ts +4 -0
- package/dist/alter/pragma.d.ts.map +1 -0
- package/dist/alter/utils.d.ts +6 -0
- package/dist/alter/utils.d.ts.map +1 -0
- package/dist/defaults.d.ts +2 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1540 -0
- package/dist/index.mjs.map +1 -0
- package/dist/indexer.d.ts +12 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/log/access_log.d.ts +7 -0
- package/dist/log/access_log.d.ts.map +1 -0
- package/dist/log/db.d.ts +6 -0
- package/dist/log/db.d.ts.map +1 -0
- package/dist/log/index.d.ts +3 -0
- package/dist/log/index.d.ts.map +1 -0
- package/dist/tables/AccessLog/index.d.ts +79 -0
- package/dist/tables/AccessLog/index.d.ts.map +1 -0
- package/dist/tables/AccessLog/schema.d.ts +17 -0
- package/dist/tables/AccessLog/schema.d.ts.map +1 -0
- package/dist/tables/Permission/index.d.ts +43 -0
- package/dist/tables/Permission/index.d.ts.map +1 -0
- package/dist/tables/Permission/schema.d.ts +12 -0
- package/dist/tables/Permission/schema.d.ts.map +1 -0
- package/dist/tables/SetString/index.d.ts +10 -0
- package/dist/tables/SetString/index.d.ts.map +1 -0
- package/dist/tables/SetString/schema.d.ts +7 -0
- package/dist/tables/SetString/schema.d.ts.map +1 -0
- package/dist/tables/Settings/index.d.ts +42 -0
- package/dist/tables/Settings/index.d.ts.map +1 -0
- package/dist/tables/Settings/schema.d.ts +8 -0
- package/dist/tables/Settings/schema.d.ts.map +1 -0
- package/dist/tables/Transaction/index.d.ts +90 -0
- package/dist/tables/Transaction/index.d.ts.map +1 -0
- package/dist/tables/Transaction/schema.d.ts +16 -0
- package/dist/tables/Transaction/schema.d.ts.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.d.ts.map +1 -0
- package/eslint.config.js +7 -0
- package/package.json +54 -0
- package/src/BitFields.ts +160 -0
- package/src/Error.ts +13 -0
- package/src/ORM.ts +49 -0
- package/src/SQLParser.js +1204 -0
- package/src/WebpackFileProvider.ts +63 -0
- package/src/alter/column_add.ts +79 -0
- package/src/alter/column_drop.ts +54 -0
- package/src/alter/column_rename.ts +55 -0
- package/src/alter/column_update.ts +92 -0
- package/src/alter/columns_order.ts +60 -0
- package/src/alter/index.ts +6 -0
- package/src/alter/pragma.ts +10 -0
- package/src/alter/utils.ts +70 -0
- package/src/defaults.ts +3 -0
- package/src/index.ts +227 -0
- package/src/indexer.ts +75 -0
- package/src/log/access_log.ts +29 -0
- package/src/log/db.ts +28 -0
- package/src/log/index.ts +2 -0
- package/src/tables/AccessLog/index.ts +252 -0
- package/src/tables/AccessLog/schema.ts +20 -0
- package/src/tables/Permission/index.ts +220 -0
- package/src/tables/Permission/schema.ts +13 -0
- package/src/tables/SetString/index.ts +45 -0
- package/src/tables/SetString/schema.ts +7 -0
- package/src/tables/Settings/index.ts +135 -0
- package/src/tables/Settings/schema.ts +8 -0
- package/src/tables/Transaction/index.ts +343 -0
- package/src/tables/Transaction/schema.ts +20 -0
- package/src/types/index.ts +33 -0
- package/src/utils.ts +48 -0
- package/test/sqliteExtExpert.test.ts +39 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
export * from './schema';
|
|
2
|
+
|
|
3
|
+
import { SetString } from '../SetString';
|
|
4
|
+
import { BitFields } from '../../BitFields';
|
|
5
|
+
import { master_table } from '../../utils';
|
|
6
|
+
import { ORM } from '../../ORM';
|
|
7
|
+
|
|
8
|
+
import type { Kysely } from 'kysely';
|
|
9
|
+
import type { DBTablesByType } from '../../types';
|
|
10
|
+
import type { TableSetString } from '../SetString';
|
|
11
|
+
import type { TableTransaction } from './schema';
|
|
12
|
+
|
|
13
|
+
type DefaultKeyTransaction = 'transaction';
|
|
14
|
+
interface DefaultKyselyTransaction {
|
|
15
|
+
transaction: TableTransaction
|
|
16
|
+
transaction_set_reason: TableSetString
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface TransactionData {
|
|
20
|
+
flag: number
|
|
21
|
+
reason: string
|
|
22
|
+
since_at?: Date
|
|
23
|
+
deadline_at?: Date
|
|
24
|
+
parent_id?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface TransactionHistory {
|
|
28
|
+
amount: number
|
|
29
|
+
reason: string | null
|
|
30
|
+
date: string | null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TransactionStore {
|
|
34
|
+
get(fk_id: number): Promise<number>
|
|
35
|
+
set(fk_id: number, value: number, options: TransactionData): Promise<number | undefined>
|
|
36
|
+
add(fk_id: number, amount: number, options: TransactionData): Promise<number | undefined>
|
|
37
|
+
|
|
38
|
+
accept(id: number): Promise<boolean>
|
|
39
|
+
reject(id: number): Promise<boolean>
|
|
40
|
+
|
|
41
|
+
history(fk_id: number): Promise<TransactionHistory[]>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class TransactionStoreDB implements TransactionStore {
|
|
45
|
+
protected readonly db: Kysely<DefaultKyselyTransaction>;
|
|
46
|
+
protected readonly domain: DefaultKeyTransaction;
|
|
47
|
+
|
|
48
|
+
protected constructor(db: any, domain: any) {
|
|
49
|
+
this.db = db;
|
|
50
|
+
this.domain = domain;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static async create<
|
|
54
|
+
DB,
|
|
55
|
+
Domain extends DBTablesByType<DB, TableTransaction>,
|
|
56
|
+
>(
|
|
57
|
+
db: Kysely<DB>,
|
|
58
|
+
domain: Domain,
|
|
59
|
+
) {
|
|
60
|
+
const master = await master_table(db);
|
|
61
|
+
const tables = [
|
|
62
|
+
`${domain}`,
|
|
63
|
+
`${domain}_set_reason`,
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
for (const table of tables) {
|
|
67
|
+
const info = master.find(r => r.name == table && r.type == 'table');
|
|
68
|
+
|
|
69
|
+
if (!info)
|
|
70
|
+
throw new Error(`Table ${table} not found, can't create transaction store`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return new TransactionStoreDB(db, domain);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
protected async db_get(fk_id: number, curr_date: Date, db_builder = this.db): Promise<number> {
|
|
77
|
+
const curr_iso = curr_date.toISOString();
|
|
78
|
+
|
|
79
|
+
return Number((
|
|
80
|
+
await db_builder
|
|
81
|
+
.selectFrom(this.domain)
|
|
82
|
+
.select(sb => sb
|
|
83
|
+
.fn.sum(sb.ref('amount')).as('sum'))
|
|
84
|
+
.where('fk_id', '=', fk_id)
|
|
85
|
+
.where(eb => eb
|
|
86
|
+
.or([
|
|
87
|
+
eb('deadline_at', '>=', curr_iso),
|
|
88
|
+
eb('deadline_at', 'is', null),
|
|
89
|
+
]))
|
|
90
|
+
.where(eb => eb
|
|
91
|
+
.or([
|
|
92
|
+
eb('since_at', '<=', curr_iso),
|
|
93
|
+
eb('since_at', 'is', null),
|
|
94
|
+
]))
|
|
95
|
+
.where('created_at', '<=', curr_iso)
|
|
96
|
+
.where('accept_at', 'is not', null)
|
|
97
|
+
.where('reject_at', 'is', null)
|
|
98
|
+
.groupBy('fk_id')
|
|
99
|
+
.executeTakeFirst()
|
|
100
|
+
)?.sum ?? 0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected async db_set(
|
|
104
|
+
fk_id: number,
|
|
105
|
+
value: number,
|
|
106
|
+
options: TransactionData,
|
|
107
|
+
db_builder = this.db,
|
|
108
|
+
): Promise<number | undefined> {
|
|
109
|
+
const curr_date = new Date();
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
await db_builder
|
|
113
|
+
.transaction()
|
|
114
|
+
.execute(async (trx) => {
|
|
115
|
+
const curr_amount = await this.db_get(fk_id, curr_date, trx);
|
|
116
|
+
return this.db_add(fk_id, value - curr_amount, options, trx);
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
protected async db_add(
|
|
122
|
+
fk_id: number,
|
|
123
|
+
value: number,
|
|
124
|
+
options: TransactionData,
|
|
125
|
+
db_builder = this.db,
|
|
126
|
+
): Promise<number | undefined> {
|
|
127
|
+
return (
|
|
128
|
+
await db_builder
|
|
129
|
+
.transaction()
|
|
130
|
+
.execute(async (trx) => {
|
|
131
|
+
const reason_id = await SetString.store_value_or_throw<DefaultKyselyTransaction>(options.reason, `${this.domain}_set_reason`, trx);
|
|
132
|
+
|
|
133
|
+
const trx_id = await trx
|
|
134
|
+
.insertInto(this.domain)
|
|
135
|
+
.returning('id')
|
|
136
|
+
.values({
|
|
137
|
+
fk_id,
|
|
138
|
+
flag: options.flag,
|
|
139
|
+
amount: value,
|
|
140
|
+
reason_id,
|
|
141
|
+
since_at: options?.since_at?.toISOString(),
|
|
142
|
+
deadline_at: options?.deadline_at?.toISOString(),
|
|
143
|
+
parent_id: options?.parent_id,
|
|
144
|
+
})
|
|
145
|
+
.executeTakeFirst();
|
|
146
|
+
|
|
147
|
+
return trx_id?.id;
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
protected async db_accept(id: number, db_builder = this.db): Promise<boolean> {
|
|
153
|
+
const curr_date = new Date();
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
await db_builder
|
|
157
|
+
.updateTable(this.domain)
|
|
158
|
+
.where('id', '=', id)
|
|
159
|
+
.where('reject_at', 'is', null)
|
|
160
|
+
.where('accept_at', 'is', null)
|
|
161
|
+
.set({
|
|
162
|
+
accept_at: curr_date.toISOString(),
|
|
163
|
+
})
|
|
164
|
+
.executeTakeFirst()
|
|
165
|
+
).numUpdatedRows != 0n;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected async db_reject(id: number, db_builder = this.db): Promise<boolean> {
|
|
169
|
+
const curr_date = new Date();
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
await db_builder
|
|
173
|
+
.updateTable(this.domain)
|
|
174
|
+
.where('id', '=', id)
|
|
175
|
+
.where('reject_at', 'is', null)
|
|
176
|
+
.where('accept_at', 'is', null)
|
|
177
|
+
.set({
|
|
178
|
+
reject_at: curr_date.toISOString(),
|
|
179
|
+
})
|
|
180
|
+
.executeTakeFirst()
|
|
181
|
+
).numUpdatedRows != 0n;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async get(
|
|
185
|
+
fk_id: number,
|
|
186
|
+
): Promise<number> {
|
|
187
|
+
const curr_date = new Date();
|
|
188
|
+
return this.db_get(fk_id, curr_date);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async set(
|
|
192
|
+
fk_id: number,
|
|
193
|
+
value: number,
|
|
194
|
+
options: TransactionData,
|
|
195
|
+
): Promise<number | undefined> {
|
|
196
|
+
return this.db_set(fk_id, value, options);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async add(
|
|
200
|
+
fk_id: number,
|
|
201
|
+
value: number,
|
|
202
|
+
options: TransactionData,
|
|
203
|
+
): Promise<number | undefined> {
|
|
204
|
+
return this.db_add(fk_id, value, options);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async accept(id: number): Promise<boolean> {
|
|
208
|
+
return this.db_accept(id);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async reject(id: number): Promise<boolean> {
|
|
212
|
+
return this.db_reject(id);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async history(fk_id: number, db_builder = this.db): Promise<TransactionHistory[]> { // FIXME: переписать, не достаточно точные данные, из-за того что добавились столбцы со сгоранием бонусов
|
|
216
|
+
const curr_iso = new Date().toISOString();
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
await db_builder
|
|
220
|
+
.selectFrom(this.domain)
|
|
221
|
+
.leftJoin(`${this.domain}_set_reason`, `${this.domain}_set_reason.id`, `${this.domain}.reason_id`)
|
|
222
|
+
.select([
|
|
223
|
+
`${this.domain}.amount as amount`,
|
|
224
|
+
`${this.domain}.accept_at as date`,
|
|
225
|
+
`${this.domain}_set_reason.value as reason`,
|
|
226
|
+
])
|
|
227
|
+
.where(`${this.domain}.fk_id`, '=', fk_id)
|
|
228
|
+
.where(`${this.domain}.created_at`, '<=', curr_iso)
|
|
229
|
+
.where(`${this.domain}.accept_at`, 'is not', null)
|
|
230
|
+
.where(`${this.domain}.reject_at`, 'is', null)
|
|
231
|
+
.execute()
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const TransactionDefaultFlags = {
|
|
237
|
+
hidden: 0,
|
|
238
|
+
type: {
|
|
239
|
+
receive: 1,
|
|
240
|
+
spend: 2,
|
|
241
|
+
transfer: 3,
|
|
242
|
+
internal: 4,
|
|
243
|
+
other: 5,
|
|
244
|
+
},
|
|
245
|
+
} as const;
|
|
246
|
+
|
|
247
|
+
export class CurrentTransaction<TransactionItemId extends WithId> extends ORM {
|
|
248
|
+
protected readonly transaction: Transaction<TransactionItemId>;
|
|
249
|
+
protected readonly id: number | undefined;
|
|
250
|
+
|
|
251
|
+
constructor(transaction: Transaction<TransactionItemId>, id: number | undefined) {
|
|
252
|
+
super();
|
|
253
|
+
|
|
254
|
+
this.transaction = transaction;
|
|
255
|
+
this.id = id;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async accept(): Promise<boolean> {
|
|
259
|
+
if (!this.id)
|
|
260
|
+
return false;
|
|
261
|
+
|
|
262
|
+
return this.transaction.store.accept(this.id);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async reject(): Promise<boolean> {
|
|
266
|
+
if (!this.id)
|
|
267
|
+
return false;
|
|
268
|
+
|
|
269
|
+
return this.transaction.store.reject(this.id);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
get_id(): number | undefined {
|
|
273
|
+
return this.id;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
interface WithId {
|
|
278
|
+
id: number
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface TransactionOptions {
|
|
282
|
+
type: keyof typeof TransactionDefaultFlags['type']
|
|
283
|
+
hidden?: boolean
|
|
284
|
+
reason: string
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export class Transaction<TransactionItemId extends WithId> extends ORM {
|
|
288
|
+
public readonly flags: BitFields<typeof TransactionDefaultFlags>;
|
|
289
|
+
public readonly store: TransactionStore;
|
|
290
|
+
|
|
291
|
+
protected constructor(store: TransactionStore) {
|
|
292
|
+
super();
|
|
293
|
+
|
|
294
|
+
this.store = store;
|
|
295
|
+
this.flags = new BitFields(TransactionDefaultFlags);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static new<
|
|
299
|
+
TransactionItemId extends WithId,
|
|
300
|
+
>(options: {
|
|
301
|
+
store: TransactionStore
|
|
302
|
+
}) {
|
|
303
|
+
return new Transaction<TransactionItemId>(options.store);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
transaction_from_id(id: number | undefined) {
|
|
307
|
+
return new CurrentTransaction<TransactionItemId>(this, id);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private transaction_parse_options(options: TransactionOptions): TransactionData {
|
|
311
|
+
return {
|
|
312
|
+
flag: this.flags.pack({
|
|
313
|
+
hidden: options.hidden,
|
|
314
|
+
type: { [options?.type] : true },
|
|
315
|
+
}),
|
|
316
|
+
reason: options?.reason,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async get_sum(fk_obj: TransactionItemId) {
|
|
321
|
+
return this.store.get(fk_obj.id);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async set_transaction(fk_obj: TransactionItemId, value: number, options: TransactionOptions) {
|
|
325
|
+
const trx_id = await this.store.set(fk_obj.id, value, (
|
|
326
|
+
this.transaction_parse_options(options)
|
|
327
|
+
));
|
|
328
|
+
|
|
329
|
+
return this.transaction_from_id(trx_id);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async add_transaction(fk_obj: TransactionItemId, value: number, options: TransactionOptions) {
|
|
333
|
+
const trx_id = await this.store.add(fk_obj.id, value, (
|
|
334
|
+
this.transaction_parse_options(options)
|
|
335
|
+
));
|
|
336
|
+
|
|
337
|
+
return this.transaction_from_id(trx_id);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async get_history(fk_obj: TransactionItemId) {
|
|
341
|
+
return this.store.history(fk_obj.id);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Generated, GeneratedAlways } from 'kysely';
|
|
2
|
+
|
|
3
|
+
export interface TableTransaction {
|
|
4
|
+
id: Generated<number>
|
|
5
|
+
fk_id: number // fk, на строку-владельца "счёта"
|
|
6
|
+
flag: number // BitField
|
|
7
|
+
amount: number // Величина изменения
|
|
8
|
+
remain: number | null // Остаток, носит чисто информационный характер, в большинстве случаев дедлайны сломают это значение
|
|
9
|
+
reason_id: number // fk, TableSetString
|
|
10
|
+
accept_at: string | null // ISO 8601, не может быть заполнен вместе с denied_at
|
|
11
|
+
reject_at: string | null // ISO 8601, не может быть заполнен вместе с success_at
|
|
12
|
+
// category_id: number // fk, Говорит категорию на которую можно потратить баланс, ее нужно указывать для всех нужных положительных amount,
|
|
13
|
+
// // если после операции останется баланс 0, значит транзакцию не создаем, если мы можем только списать только часть,
|
|
14
|
+
// // то создаем несколько транзакций, у операций которые списывают баланс нужно указывать category_id родительского пополнения
|
|
15
|
+
// TODO: Доработать deadline
|
|
16
|
+
parent_id: number | null // fk, self, Родительская транзакция должна так же указывать сама на себя, используется вместе с deadline_at
|
|
17
|
+
deadline_at: string | null // ISO 8601, по истечению которого транзакция не будет учитывается в подсчёте баланса
|
|
18
|
+
since_at: string | null // ISO 8601, дата после наступления которой, транзакция будет начислена на счет
|
|
19
|
+
created_at: GeneratedAlways<string>
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SelectType } from 'kysely';
|
|
2
|
+
import type { AllUnionFields } from 'type-fest';
|
|
3
|
+
|
|
4
|
+
export type DBTablesByType<DB, TB_Type> = {
|
|
5
|
+
[K in keyof DB]:
|
|
6
|
+
DB[K] extends TB_Type
|
|
7
|
+
? K extends string
|
|
8
|
+
? K
|
|
9
|
+
: never
|
|
10
|
+
: never
|
|
11
|
+
}[keyof DB];
|
|
12
|
+
|
|
13
|
+
export type DBSelectTypeFromTable<
|
|
14
|
+
DB,
|
|
15
|
+
TB extends keyof DB,
|
|
16
|
+
Fields extends string,
|
|
17
|
+
Default = never,
|
|
18
|
+
> = {
|
|
19
|
+
[K in Fields]?:
|
|
20
|
+
K extends keyof AllUnionFields<DB[TB]>
|
|
21
|
+
? SelectType<AllUnionFields<DB[TB]>[K]> | undefined
|
|
22
|
+
: Default
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type DBRequired<
|
|
26
|
+
ObjectFields,
|
|
27
|
+
Fields extends keyof ObjectFields = keyof ObjectFields,
|
|
28
|
+
> = {
|
|
29
|
+
[K in keyof ObjectFields]-?:
|
|
30
|
+
K extends Fields
|
|
31
|
+
? Exclude<ObjectFields[K], undefined>
|
|
32
|
+
: ObjectFields[K]
|
|
33
|
+
};
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
import { SQLParser } from './SQLParser';
|
|
3
|
+
|
|
4
|
+
import type { Kysely } from 'kysely';
|
|
5
|
+
|
|
6
|
+
export async function table_info<DB>(db: Kysely<DB>, table: Extract<keyof DB, string>) {
|
|
7
|
+
return (
|
|
8
|
+
(await sql`SELECT * FROM pragma_table_info('${sql.raw(table)}')`.execute(db))
|
|
9
|
+
.rows.map((r: any) => ({ ...r, notnull: !!r.notnull, pk: !!r.pk, type: r.type.toUpperCase() }))
|
|
10
|
+
) as {
|
|
11
|
+
cid: number
|
|
12
|
+
name: string
|
|
13
|
+
type: 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB' | 'NUMERIC'
|
|
14
|
+
notnull: boolean
|
|
15
|
+
dflt_value: string | null
|
|
16
|
+
pk: boolean
|
|
17
|
+
}[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function master_table(db: Kysely<any>) {
|
|
21
|
+
return (
|
|
22
|
+
(await db
|
|
23
|
+
.selectFrom('sqlite_master')
|
|
24
|
+
.selectAll()
|
|
25
|
+
.execute()
|
|
26
|
+
).filter((r: any) => r.name != 'sqlite_sequence')
|
|
27
|
+
) as {
|
|
28
|
+
type: 'index' | 'table'
|
|
29
|
+
name: string
|
|
30
|
+
tbl_name: string
|
|
31
|
+
rootpage: number
|
|
32
|
+
sql: string
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function get_table_schema<DB>(db: Kysely<DB>, table: Extract<keyof DB, string>) {
|
|
37
|
+
const master = (await master_table(db)).find(r => r.name == table);
|
|
38
|
+
|
|
39
|
+
if (!master)
|
|
40
|
+
return false;
|
|
41
|
+
|
|
42
|
+
const schema = SQLParser.parse(master.sql)[0];
|
|
43
|
+
|
|
44
|
+
if (schema?.type != 'CREATE_TABLE')
|
|
45
|
+
return false;
|
|
46
|
+
|
|
47
|
+
return schema;
|
|
48
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import sqliteExtExpert from 'better-sqlite3-expert';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
const sql = String.raw;
|
|
6
|
+
|
|
7
|
+
it('Check extension work', () => {
|
|
8
|
+
const db = new Database(':memory:');
|
|
9
|
+
|
|
10
|
+
db.loadExtension(sqliteExtExpert());
|
|
11
|
+
db.prepare(sql`
|
|
12
|
+
CREATE TABLE test (
|
|
13
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14
|
+
name TEXT NOT NULL
|
|
15
|
+
);
|
|
16
|
+
`).run();
|
|
17
|
+
|
|
18
|
+
let hash;
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < 10000; i++) {
|
|
21
|
+
const name = randomBytes(16).toString('hex');
|
|
22
|
+
|
|
23
|
+
if (i === 8000) {
|
|
24
|
+
hash = name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
db.prepare(sql`
|
|
28
|
+
INSERT INTO test (name) VALUES ('${name}');
|
|
29
|
+
`).run();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const result_1 = db.prepare(sql`
|
|
33
|
+
SELECT expert_suggest(?) as suggestion;
|
|
34
|
+
`).pluck().get(
|
|
35
|
+
"SELECT * FROM test WHERE name = '${hash}'",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(result_1).toBe(`CREATE INDEX test_idx_00015c29 ON test(name);\n\n`);
|
|
39
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"noEmit": false
|
|
7
|
+
},
|
|
8
|
+
"include": [
|
|
9
|
+
"src/**/*"
|
|
10
|
+
],
|
|
11
|
+
"exclude": [
|
|
12
|
+
"node_modules",
|
|
13
|
+
"dist",
|
|
14
|
+
"test/**/*",
|
|
15
|
+
"**/*.test.ts"
|
|
16
|
+
]
|
|
17
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": ".",
|
|
6
|
+
"noEmit": true
|
|
7
|
+
},
|
|
8
|
+
"include": [
|
|
9
|
+
"src/**/*",
|
|
10
|
+
"test/**/*"
|
|
11
|
+
, "../core/src/requireContext.ts" ],
|
|
12
|
+
"exclude": [
|
|
13
|
+
"node_modules",
|
|
14
|
+
"dist"
|
|
15
|
+
]
|
|
16
|
+
}
|