rads-db 3.1.16 → 3.2.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.
@@ -0,0 +1,848 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ var _lodash = _interopRequireDefault(require("lodash"));
8
+ var _mssql = _interopRequireDefault(require("mssql"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const cache = {};
11
+ const idType = "VARCHAR(128)";
12
+ const strType = "VARCHAR(MAX)";
13
+ const numberType = "DECIMAL(30,15)";
14
+ const booleanType = "BIT";
15
+ const simpleSubclauseRegex = /^[a-z0-9_@]+.[a-z0-9_@]+ = [a-z0-9_@.]+$/i;
16
+ const sanitizePathRegex = /^[a-z0-9_\[\]\.]+$/i;
17
+ const operatorHandlers = {
18
+ some: (ctx, parameters, whereArgs) => {
19
+ const {
20
+ name,
21
+ namePrefix,
22
+ paramNamePrefix,
23
+ paramName,
24
+ whereVal
25
+ } = whereArgs;
26
+ const subClause = getSqlWhere(ctx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
27
+ if (subClause) {
28
+ if (simpleSubclauseRegex.test(subClause)) {
29
+ const parts = subClause.split(" ");
30
+ const field = parts[0];
31
+ const variable = parts[2];
32
+ return `array_contains(${namePrefix}${name}, { ${field.split(".")[1]}: ${variable} }, true)`;
33
+ }
34
+ return `exists (select ${name} from ${name} in ${namePrefix}${name} where ${subClause})`;
35
+ }
36
+ return `array_length(${namePrefix}${name}) > 0`;
37
+ },
38
+ none: (ctx, parameters, whereArgs) => {
39
+ const {
40
+ name,
41
+ namePrefix,
42
+ paramNamePrefix,
43
+ paramName,
44
+ whereVal
45
+ } = whereArgs;
46
+ const subClause = getSqlWhere(ctx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
47
+ if (subClause) return `not exists (select ${name} from ${name} in ${namePrefix}${name} where ${subClause})`;
48
+ return `array_length(${namePrefix}${name}) = 0`;
49
+ },
50
+ and: (ctx, parameters, whereArgs) => {
51
+ const {
52
+ namePrefix,
53
+ paramNamePrefix,
54
+ whereVal
55
+ } = whereArgs;
56
+ if (!_lodash.default.isArray(whereVal)) throw new Error(`Value for where._and must be an array`);
57
+ const clauses = [];
58
+ for (let i = 0; i < whereVal.length; i++) {
59
+ const andQuery = getSqlWhere(ctx, parameters, whereVal[i], namePrefix, `${paramNamePrefix}and${i}_`);
60
+ clauses.push(andQuery);
61
+ }
62
+ if (!clauses.length) return null;
63
+ return `((${clauses.join(") and (")}))`;
64
+ },
65
+ not: (ctx, parameters, whereArgs) => {
66
+ const {
67
+ name,
68
+ namePrefix,
69
+ paramNamePrefix,
70
+ whereVal
71
+ } = whereArgs;
72
+ const subClause = getSqlWhere(ctx, parameters, whereVal, `${namePrefix}${name}`, `${paramNamePrefix}not_`);
73
+ if (!subClause) return null;
74
+ return `not(${subClause})`;
75
+ },
76
+ or: (ctx, parameters, whereArgs) => {
77
+ const {
78
+ namePrefix,
79
+ paramNamePrefix,
80
+ whereVal
81
+ } = whereArgs;
82
+ if (!_lodash.default.isArray(whereVal)) throw new Error(`Value for where._or must be an array`);
83
+ const clauses = [];
84
+ for (let i = 0; i < whereVal.length; i++) {
85
+ const orQuery = getSqlWhere(ctx, parameters, whereVal[i], namePrefix, `${paramNamePrefix}or${i}_`);
86
+ clauses.push(orQuery);
87
+ }
88
+ if (!clauses.length) return null;
89
+ return `((${clauses.join(") or (")}))`;
90
+ },
91
+ isNull: (ctx, parameters, whereArgs) => {
92
+ const {
93
+ name,
94
+ namePrefix,
95
+ whereVal
96
+ } = whereArgs;
97
+ const n = `${namePrefix}${name}`;
98
+ if (whereVal) return `${n} is null`;
99
+ return `${n} is not null`;
100
+ },
101
+ eq: (ctx, parameters, whereArgs) => {
102
+ const {
103
+ name,
104
+ namePrefix,
105
+ paramName,
106
+ paramNamePrefix,
107
+ whereVal
108
+ } = whereArgs;
109
+ const pn = `${paramNamePrefix}${paramName}`;
110
+ parameters[pn] = whereVal;
111
+ return `${namePrefix}${name} = @${pn}`;
112
+ },
113
+ in: (ctx, parameters, whereArgs) => {
114
+ const {
115
+ name,
116
+ namePrefix,
117
+ paramName,
118
+ paramNamePrefix,
119
+ whereVal
120
+ } = whereArgs;
121
+ const pn = `${paramNamePrefix}${paramName}`;
122
+ const inStrs = [];
123
+ for (const [i, v] of whereVal.entries()) {
124
+ parameters[`${pn}_${i}`] = v;
125
+ inStrs.push(`@${pn}_${i}`);
126
+ }
127
+ parameters[pn] = whereVal;
128
+ return `${namePrefix}${name} IN (${inStrs.join(", ")})`;
129
+ },
130
+ startsWith: (ctx, parameters, whereArgs) => {
131
+ const {
132
+ name,
133
+ namePrefix,
134
+ paramName,
135
+ paramNamePrefix,
136
+ whereVal
137
+ } = whereArgs;
138
+ const pn = `${paramNamePrefix}${paramName}`;
139
+ parameters[pn] = whereVal;
140
+ return `${namePrefix}${name} COLLATE Latin1_General_CS_AS LIKE @${pn} + '%'`;
141
+ },
142
+ istartsWith: (ctx, parameters, whereArgs) => {
143
+ const {
144
+ name,
145
+ namePrefix,
146
+ paramName,
147
+ paramNamePrefix,
148
+ whereVal
149
+ } = whereArgs;
150
+ const pn = `${paramNamePrefix}${paramName}`;
151
+ parameters[pn] = whereVal;
152
+ return `${namePrefix}${name} LIKE @${pn} + '%'`;
153
+ },
154
+ endsWith: (ctx, parameters, whereArgs) => {
155
+ const {
156
+ name,
157
+ namePrefix,
158
+ paramName,
159
+ paramNamePrefix,
160
+ whereVal
161
+ } = whereArgs;
162
+ const pn = `${paramNamePrefix}${paramName}`;
163
+ parameters[pn] = whereVal;
164
+ return `${namePrefix}${name} COLLATE Latin1_General_CS_AS LIKE '%' + @${pn}`;
165
+ },
166
+ iendsWith: (ctx, parameters, whereArgs) => {
167
+ const {
168
+ name,
169
+ namePrefix,
170
+ paramName,
171
+ paramNamePrefix,
172
+ whereVal
173
+ } = whereArgs;
174
+ const pn = `${paramNamePrefix}${paramName}`;
175
+ parameters[pn] = whereVal;
176
+ return `${namePrefix}${name} LIKE '%' + @${pn}`;
177
+ },
178
+ contains: (ctx, parameters, whereArgs) => {
179
+ const {
180
+ name,
181
+ namePrefix,
182
+ paramName,
183
+ paramNamePrefix,
184
+ whereVal
185
+ } = whereArgs;
186
+ const pn = `${paramNamePrefix}${paramName}`;
187
+ parameters[pn] = whereVal;
188
+ return `${namePrefix}${name} COLLATE Latin1_General_CS_AS LIKE '%' + @${pn} + '%'`;
189
+ },
190
+ icontains: (ctx, parameters, whereArgs) => {
191
+ const {
192
+ name,
193
+ namePrefix,
194
+ paramName,
195
+ paramNamePrefix,
196
+ whereVal
197
+ } = whereArgs;
198
+ const pn = `${paramNamePrefix}${paramName}`;
199
+ parameters[pn] = whereVal;
200
+ return `${namePrefix}${name} LIKE '%' + @${pn} + '%'`;
201
+ },
202
+ arrayContains: (ctx, parameters, whereArgs) => {
203
+ const {
204
+ name,
205
+ namePrefix,
206
+ paramName,
207
+ paramNamePrefix,
208
+ whereVal
209
+ } = whereArgs;
210
+ const pn = `${paramNamePrefix}${paramName}`;
211
+ parameters[pn] = whereVal;
212
+ return `@${pn} IN ${namePrefix}${name}`;
213
+ },
214
+ isEmpty: (ctx, parameters, whereArgs) => {
215
+ const {
216
+ name,
217
+ namePrefix,
218
+ whereVal
219
+ } = whereArgs;
220
+ const n = `${namePrefix}${name}`;
221
+ if (whereVal) return `${n} is null`;
222
+ return `${n} is not null`;
223
+ },
224
+ jsonContains: (ctx, parameters, whereArgs) => {
225
+ throw new Error("jsonContains operator is not supported in SQL driver yet");
226
+ },
227
+ ieq: (ctx, parameters, whereArgs) => {
228
+ const {
229
+ name,
230
+ namePrefix,
231
+ paramName,
232
+ paramNamePrefix,
233
+ whereVal
234
+ } = whereArgs;
235
+ const pn = `${paramNamePrefix}${paramName}`;
236
+ parameters[pn] = whereVal;
237
+ return `${namePrefix}${name} != @${pn}`;
238
+ },
239
+ gt: (ctx, parameters, whereArgs) => {
240
+ const {
241
+ name,
242
+ namePrefix,
243
+ paramName,
244
+ paramNamePrefix,
245
+ whereVal
246
+ } = whereArgs;
247
+ const pn = `${paramNamePrefix}${paramName}`;
248
+ parameters[pn] = whereVal;
249
+ return `${namePrefix}${name} > @${pn}`;
250
+ },
251
+ gte: (ctx, parameters, whereArgs) => {
252
+ const {
253
+ name,
254
+ namePrefix,
255
+ paramName,
256
+ paramNamePrefix,
257
+ whereVal
258
+ } = whereArgs;
259
+ const pn = `${paramNamePrefix}${paramName}`;
260
+ parameters[pn] = whereVal;
261
+ return `${namePrefix}${name} >= @${pn}`;
262
+ },
263
+ lt: (ctx, parameters, whereArgs) => {
264
+ const {
265
+ name,
266
+ namePrefix,
267
+ paramName,
268
+ paramNamePrefix,
269
+ whereVal
270
+ } = whereArgs;
271
+ const pn = `${paramNamePrefix}${paramName}`;
272
+ parameters[pn] = whereVal;
273
+ return `${namePrefix}${name} < @${pn}`;
274
+ },
275
+ lte: (ctx, parameters, whereArgs) => {
276
+ const {
277
+ name,
278
+ namePrefix,
279
+ paramName,
280
+ paramNamePrefix,
281
+ whereVal
282
+ } = whereArgs;
283
+ const pn = `${paramNamePrefix}${paramName}`;
284
+ parameters[pn] = whereVal;
285
+ return `${namePrefix}${name} <= @${pn}`;
286
+ }
287
+ };
288
+ var _default = options => {
289
+ let client;
290
+ let clientPromise;
291
+ let introspection;
292
+ let introspectionPromise;
293
+ options = {
294
+ tablePrefix: "dbo.",
295
+ stagingTablePrefix: "dbo_stg.",
296
+ ...options
297
+ };
298
+ if (options.tablePrefix === options.stagingTablePrefix) {
299
+ throw new Error("Table prefix and staging table prefix cannot be the same");
300
+ }
301
+ return (schema, entity) => {
302
+ const result = {
303
+ driverName: "sql",
304
+ get client() {
305
+ return client;
306
+ },
307
+ getMany,
308
+ async getAgg(args, ctx) {
309
+ args = args || {};
310
+ const {
311
+ query,
312
+ parameters
313
+ } = getSqlQuery(schema, entity, args, options);
314
+ const req = client.request();
315
+ for (const p in parameters) {
316
+ req.input(p, parameters[p]);
317
+ }
318
+ console.log(query);
319
+ const result2 = await req.query(query);
320
+ const nodes = result2.recordset || [];
321
+ console.log(result2);
322
+ return nodes[0];
323
+ },
324
+ async putMany(items, ctx) {
325
+ await ensureClientConnected();
326
+ const realTablePrefix = options.tablePrefix || "";
327
+ const stagingTablePrefix = options.stagingTablePrefix || "";
328
+ const sqlTables = getSqlTablesFor(entity);
329
+ for (const st of sqlTables) {
330
+ const request = client.request();
331
+ const columns = Object.keys(st.tableColumns);
332
+ const stagingTable = `${stagingTablePrefix || ""}${st.tableName}`;
333
+ const realTable = `${realTablePrefix || ""}${st.tableName}`;
334
+ const columnNames = columns.join(", ");
335
+ const valueStrs = [];
336
+ for (const row of items) {
337
+ const rowsToInsert = st.getValue ? st.getValue(row) || [] : [row];
338
+ for (let j = 0; j < rowsToInsert.length; j++) {
339
+ const rowToInsert = rowsToInsert[j];
340
+ const paramNames = [];
341
+ for (const columnName in st.tableColumns) {
342
+ paramNames.push(stringifyValueForInsert(st.tableColumns[columnName].getValue(rowToInsert, row, j)));
343
+ }
344
+ valueStrs.push(`(${paramNames.join(", ")})`);
345
+ }
346
+ }
347
+ for (const valuesChunk of _lodash.default.chunk(valueStrs, 1e3)) {
348
+ if (valuesChunk.length) {
349
+ const insertQuery = `TRUNCATE TABLE ${stagingTable}; INSERT INTO ${stagingTable} (${columnNames}) VALUES ${valuesChunk.join(",\n")}`;
350
+ try {
351
+ await request.query(insertQuery);
352
+ await client.request().query(`
353
+ UPDATE tgt
354
+ SET ${columns.map(c => `tgt.${c} = src.${c}`).join(",\n")}
355
+ FROM ${realTable} tgt
356
+ JOIN ${stagingTable} src ON tgt.id = src.id;
357
+
358
+ INSERT INTO ${realTable} (${columns.join(", ")})
359
+ SELECT ${columns.map(c => `src.${c}`).join(", ")}
360
+ FROM ${stagingTable} src
361
+ WHERE NOT EXISTS (
362
+ SELECT 1 FROM ${realTable} tgt WHERE tgt.id = src.id
363
+ );
364
+
365
+ `);
366
+ } catch (e) {
367
+ console.error(`\u274C Failed insert into ${realTable} [${valuesChunk.length} rows]`);
368
+ console.error(`First 300 chars of query:
369
+ ${insertQuery.slice(0, 300)}...`);
370
+ throw e;
371
+ }
372
+ }
373
+ }
374
+ }
375
+ },
376
+ async deleteAll() {
377
+ await ensureClientConnected();
378
+ await client.query(`delete from ${entity}`);
379
+ return [];
380
+ },
381
+ /** Generates migration script for this one entity */
382
+ async generateMigrationScript() {
383
+ await ensureIntrospectionIsAvailable();
384
+ return buildMigrationScript(schema, entity);
385
+ function buildMigrationScript(schema2, entityName) {
386
+ const entity2 = schema2[entityName];
387
+ if (!entity2) throw new Error(`Entity "${entityName}" not found in schema`);
388
+ const createTableRequest1 = getCreateTableRequestsFor(entityName, {
389
+ tableNamePrefix: options.tablePrefix
390
+ });
391
+ const createTableRequest2 = getCreateTableRequestsFor(entityName, {
392
+ tableNamePrefix: options.stagingTablePrefix
393
+ });
394
+ return [...createTableRequest1.createRequests, ...createTableRequest2.createRequests].join(";\n\n");
395
+ }
396
+ function getCreateTableRequestsFor(entityName, options2) {
397
+ const {
398
+ tableNameSuffix,
399
+ tableNamePrefix,
400
+ dropIfExists,
401
+ addPrimaryKey,
402
+ addForeignKeys,
403
+ foreignKeysTables
404
+ } = options2 || {};
405
+ const sqlTables = getSqlTablesFor(entityName);
406
+ const dropRequests = [];
407
+ const createRequests = [];
408
+ const pkRequests = [];
409
+ const fkRequests = [];
410
+ const schema2 = tableNamePrefix?.split(".")[0];
411
+ if (schema2) {
412
+ createRequests.push(`IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = N'${schema2}') BEGIN EXEC('CREATE SCHEMA ${schema2}') END`);
413
+ }
414
+ for (const st of sqlTables) {
415
+ const tableName = (tableNamePrefix || "") + st.tableName + (tableNameSuffix || "");
416
+ const columnStrs = buildColumnStrs(st.tableColumns);
417
+ const columnsStr = columnStrs.map(x => x.replace("ADD ", "")).join(", \n");
418
+ if (dropIfExists) {
419
+ dropRequests.push(`IF OBJECT_ID('${tableName}', 'U') IS NOT NULL BEGIN DROP TABLE ${tableName} END`);
420
+ }
421
+ createRequests.push(`IF OBJECT_ID('${tableName}', 'U') IS NULL BEGIN CREATE TABLE ${tableName} (${columnsStr}) END`);
422
+ if (addPrimaryKey) {
423
+ const primaryKeyColumns = Object.keys(st.tableColumns).filter(c => st.tableColumns[c].isPrimaryKey);
424
+ if (primaryKeyColumns.length) {
425
+ pkRequests.push(getAddPrimaryKeySql(tableName, primaryKeyColumns[0]));
426
+ }
427
+ }
428
+ if (addForeignKeys) {
429
+ for (const columnName in st.tableColumns) {
430
+ const column = st.tableColumns[columnName];
431
+ if (column.isForeignKeyFor) {
432
+ const foreignTableName = column.isForeignKeyFor;
433
+ if (foreignKeysTables && !foreignKeysTables.includes(foreignTableName)) {
434
+ console.warn(`Skipping foreign key for ${foreignTableName}`);
435
+ continue;
436
+ }
437
+ if ((tableNamePrefix || "") + foreignTableName === tableName) {
438
+ console.warn(`Skipping foreign key for ${foreignTableName} because it is same as source table`);
439
+ continue;
440
+ }
441
+ if (["tcUserCreatedBy_id", "tcUserUpdatedBy_id", "tcUserCreatedByApp_id", "tcUserUpdatedByApp_id", "tcAccountManufacturer_id"].includes(columnName)) {
442
+ continue;
443
+ }
444
+ const foreignColumnName = `id`;
445
+ fkRequests.push(getAddForeignKeySql(tableName, column.name, (tableNamePrefix || "") + foreignTableName, foreignColumnName));
446
+ }
447
+ }
448
+ }
449
+ }
450
+ return {
451
+ createRequests,
452
+ pkRequests,
453
+ fkRequests,
454
+ dropRequests
455
+ };
456
+ }
457
+ function getAddPrimaryKeySql(tableName, columnName) {
458
+ let schema2 = tableName.split(".")[0] || "";
459
+ if (schema2) schema2 += ".";
460
+ const constraintName = `PK_${tableName.replaceAll(".", "_")}_${columnName}`;
461
+ return `
462
+ IF OBJECT_ID('${schema2}${constraintName}', 'PK') IS NULL
463
+ BEGIN
464
+ ALTER TABLE ${tableName}
465
+ ADD CONSTRAINT ${constraintName}
466
+ PRIMARY KEY NONCLUSTERED(${columnName}) NOT ENFORCED;
467
+ END;
468
+ `.trim();
469
+ }
470
+ function getAddForeignKeySql(tableName, columnName, foreignTableName, foreignColumnName) {
471
+ let schema2 = tableName.split(".")[0] || "";
472
+ if (schema2) schema2 += ".";
473
+ const constraintName = `FK_${tableName.replaceAll(".", "_")}_${columnName}`;
474
+ return `
475
+ IF OBJECT_ID('${schema2}${constraintName}', 'F') IS NULL
476
+ BEGIN
477
+ ALTER TABLE ${tableName}
478
+ ADD CONSTRAINT ${constraintName}
479
+ FOREIGN KEY (${columnName})
480
+ REFERENCES ${foreignTableName} (${foreignColumnName}) NOT ENFORCED;
481
+ END;
482
+ `.trim();
483
+ }
484
+ function buildColumnStrs(tableColumns) {
485
+ const columns = [];
486
+ for (const fieldName in tableColumns) {
487
+ const field = tableColumns[fieldName];
488
+ columns.push(`ADD "${field.name}" ${field.type} ${field.isPrimaryKey ? "NOT NULL" : "NULL"}`);
489
+ }
490
+ return columns;
491
+ }
492
+ }
493
+ };
494
+ function getSqlTablesFor(entityName) {
495
+ return cache[entityName] || (cache[entityName] = getSqlTablesForEntityInner(entityName));
496
+ }
497
+ function getSqlTablesForEntityInner(entityName) {
498
+ const entitySchema = schema[entityName];
499
+ if (!entitySchema || !entitySchema.fields) {
500
+ throw new Error(`Entity ${entityName} not found in schema`);
501
+ }
502
+ const tableColumns = {};
503
+ const nestedArrayTables = [];
504
+ for (const fieldName in entitySchema.fields) {
505
+ const field = entitySchema.fields[fieldName];
506
+ if (!field || field.decorators?.deprecated || field.comment?.includes("@deprecated") || field.name === "beforeChange") {
507
+ continue;
508
+ }
509
+ if (field.isArray) {
510
+ const nts = getNestedArrayTables(field, entityName) || [];
511
+ nestedArrayTables.push(...nts);
512
+ continue;
513
+ }
514
+ if (field.isRelation) {
515
+ const adjustedFieldName = `${fieldName}_id`;
516
+ tableColumns[adjustedFieldName] = {
517
+ name: adjustedFieldName,
518
+ type: idType,
519
+ description: field.comment,
520
+ isRequired: field.isRequired,
521
+ isForeignKeyFor: field.type,
522
+ getValue: row => row[field.name]?.id
523
+ };
524
+ continue;
525
+ }
526
+ if (schema[field.type]?.fields) {
527
+ const [nestedObjectTable, ...nestedObjectArrayTables] = getSqlTablesForEntityInner(field.type);
528
+ for (const columnName in nestedObjectTable.tableColumns) {
529
+ if (field.isChange && columnName === "id") continue;
530
+ const column = nestedObjectTable.tableColumns[columnName];
531
+ const newColumnName = `${fieldName}_${column.name}`;
532
+ tableColumns[newColumnName] = {
533
+ ...column,
534
+ isPrimaryKey: void 0,
535
+ name: newColumnName,
536
+ getValue: (row, entity2, index) => row[field.name] && column.getValue(row[field.name], entity2, index)
537
+ };
538
+ }
539
+ for (const at of nestedObjectArrayTables) {
540
+ nestedArrayTables.push({
541
+ ...at,
542
+ tableName: `${entityName}_${fieldName}_${at.tableName.split("_").slice(1).join("_")}`,
543
+ getValue: row => row[field.name] && at.getValue?.(row[field.name]),
544
+ tableColumns: at.tableColumns
545
+ });
546
+ }
547
+ continue;
548
+ }
549
+ const sqlType = getSqlType(field);
550
+ if (!sqlType) continue;
551
+ tableColumns[fieldName] = {
552
+ name: field.name,
553
+ type: sqlType,
554
+ description: field.comment,
555
+ isRequired: field.isRequired,
556
+ isPrimaryKey: fieldName === "id",
557
+ getValue: row => row[field.name]
558
+ };
559
+ }
560
+ return [{
561
+ tableName: entityName,
562
+ description: entitySchema.comment,
563
+ tableColumns
564
+ }, ...nestedArrayTables];
565
+ }
566
+ function getNestedArrayTables(field, entityName) {
567
+ const result2 = [];
568
+ const entityReferenceColumnName = `${_lodash.default.lowerFirst(entityName)}_id`;
569
+ const commonTableColumns = {
570
+ id: {
571
+ name: "id",
572
+ type: idType,
573
+ isRequired: true,
574
+ isPrimaryKey: true,
575
+ getValue: (row, entity2, index) => `${entity2.id}_${index}`
576
+ },
577
+ [entityReferenceColumnName]: {
578
+ name: entityReferenceColumnName,
579
+ type: idType,
580
+ isForeignKeyFor: entityName,
581
+ isRequired: true,
582
+ getValue: (row, entity2) => entity2.id
583
+ }
584
+ };
585
+ if (schema[field.type]?.fields) {
586
+ if (!field.isRelation) {
587
+ const [objectTable, ...objectArrayTables] = getSqlTablesForEntityInner(field.type);
588
+ if (objectArrayTables.length) {
589
+ console.warn("Nested array tables are not supported yet", entityName, field.name);
590
+ }
591
+ result2.push({
592
+ tableName: `${entityName}_${field.name}`,
593
+ description: field.comment,
594
+ getValue: row => row[field.name] || [],
595
+ tableColumns: {
596
+ ...commonTableColumns,
597
+ ...objectTable.tableColumns
598
+ }
599
+ });
600
+ } else {
601
+ result2.push({
602
+ tableName: `${entityName}_${field.name}`,
603
+ description: field.comment,
604
+ getValue: row => row[field.name] || [],
605
+ tableColumns: {
606
+ ...commonTableColumns,
607
+ val_id: {
608
+ name: "val_id",
609
+ type: idType,
610
+ isRequired: true,
611
+ getValue: (row, entity2) => row?.id
612
+ }
613
+ }
614
+ });
615
+ }
616
+ } else {
617
+ const sqlType = getSqlType(field);
618
+ if (!sqlType) return [];
619
+ result2.push({
620
+ tableName: `${entityName}_${field.name}`,
621
+ description: field.comment,
622
+ getValue: row => row[field.name] || [],
623
+ tableColumns: {
624
+ ...commonTableColumns,
625
+ val: {
626
+ name: "val",
627
+ type: sqlType,
628
+ isRequired: true,
629
+ getValue: (row, entity2) => row
630
+ }
631
+ }
632
+ });
633
+ }
634
+ return result2;
635
+ }
636
+ function getSqlType(field) {
637
+ if (field.type === "string" && field.name === "id") return idType;
638
+ if (field.type === "string") return strType;
639
+ if (field.type === "number") return numberType;
640
+ if (field.type === "boolean") return booleanType;
641
+ if (schema[field.type]?.enumValues) {
642
+ return idType;
643
+ }
644
+ if (field.type === "Record<string, any>") {
645
+ return strType;
646
+ }
647
+ console.warn("Unknown type", field.type);
648
+ }
649
+ async function getMany(args, ctx) {
650
+ await ensureClientConnected();
651
+ args = args || {};
652
+ const maxItemCount = args.maxItemCount || 1e3;
653
+ const {
654
+ query,
655
+ parameters
656
+ } = getSqlQuery(schema, entity, args, options);
657
+ const req = client.request();
658
+ for (const p in parameters) {
659
+ req.input(p, parameters[p]);
660
+ }
661
+ console.log(query);
662
+ const result2 = await req.query(query);
663
+ const nodes = result2.recordset || [];
664
+ console.log(result2);
665
+ return {
666
+ nodes,
667
+ cursor: nodes.length === maxItemCount ? String((Number(args.cursor || "0") || 0) + nodes.length) : void 0
668
+ };
669
+ }
670
+ return result;
671
+ };
672
+ async function ensureIntrospectionIsAvailable() {
673
+ if (introspection) return;
674
+ if (!introspectionPromise) introspectionPromise = ensureIntrospectionIsAvailableInner();
675
+ await introspectionPromise;
676
+ }
677
+ async function ensureIntrospectionIsAvailableInner() {
678
+ await ensureClientConnected();
679
+ const response = await client.query(`
680
+ SELECT * FROM INFORMATION_SCHEMA.TABLES;
681
+ SELECT * FROM INFORMATION_SCHEMA.COLUMNS;
682
+ `);
683
+ if (!_lodash.default.isArray(response.recordsets)) {
684
+ throw new TypeError("Invalid result");
685
+ }
686
+ const tableRows = response.recordsets[0];
687
+ const columnRows = response.recordsets[1];
688
+ const result = {};
689
+ for (const tr of tableRows) {
690
+ if (tr.TABLE_SCHEMA !== "dbo") continue;
691
+ const tableName = tr.TABLE_NAME;
692
+ const columns = columnRows.filter(x => x.TABLE_NAME === tableName);
693
+ const type = {
694
+ tableName,
695
+ tableColumns: {},
696
+ nestedArrayTables: []
697
+ };
698
+ for (const cr of columns) {
699
+ const fieldName = cr.COLUMN_NAME;
700
+ const fieldType = cr.DATA_TYPE;
701
+ const isNullable = cr.IS_NULLABLE === "YES";
702
+ type.tableColumns[fieldName] = {
703
+ name: fieldName,
704
+ type: fieldType,
705
+ isRequired: !isNullable,
706
+ getValue: row => row[fieldName]
707
+ };
708
+ }
709
+ result[tableName] = type;
710
+ }
711
+ introspection = result;
712
+ }
713
+ async function ensureClientConnected() {
714
+ if (client) return;
715
+ if (!clientPromise) clientPromise = ensureClientConnectedInner();
716
+ await clientPromise;
717
+ }
718
+ async function ensureClientConnectedInner() {
719
+ client = await _mssql.default.connect({
720
+ authentication: options.authentication,
721
+ server: options.server,
722
+ database: options.database,
723
+ ...options.options
724
+ });
725
+ }
726
+ };
727
+ module.exports = _default;
728
+ function stringifyValueForInsert(value) {
729
+ if (value === null || value === void 0) return "NULL";
730
+ if (typeof value === "string") return `'${value.replace(/'/g, "''")}'`;
731
+ if (typeof value === "number") return value.toString();
732
+ if (typeof value === "boolean") return value ? "1" : "0";
733
+ throw new Error(`Unsupported value type: ${typeof value} for value: ${value}`);
734
+ }
735
+ function getSqlQuery(schema, entity, args, options) {
736
+ args = args || {};
737
+ const where = args.where || {};
738
+ const parameters = {};
739
+ const whereClauses = [getSqlWhere({
740
+ schema,
741
+ entity
742
+ }, parameters, where)].filter(x => x);
743
+ const orderByClause = getSqlOrderBy(args);
744
+ let columns = "*";
745
+ if (args.agg) {
746
+ const columnsArray = [];
747
+ for (const key of args.agg) {
748
+ const [field, aggOp] = key.split("_");
749
+ if (aggOp === "count") {
750
+ columnsArray.push("COUNT(1) AS _count");
751
+ }
752
+ if (aggOp === "max" && field) {
753
+ columnsArray.push(`MAX(r.[${field}]) AS ${field}_max`);
754
+ }
755
+ if (aggOp === "min" && field) {
756
+ columnsArray.push(`MIN(r.[${field}]) AS ${field}_min`);
757
+ }
758
+ if (aggOp === "sum" && field) {
759
+ columnsArray.push(`SUM(r.[${field}]) AS ${field}_sum`);
760
+ }
761
+ }
762
+ if (columnsArray.length) {
763
+ columns = columnsArray.join(", ");
764
+ }
765
+ }
766
+ const maxItemCount = args.maxItemCount || 1e3;
767
+ const cursor = args.cursor || "0";
768
+ const paginationClause = args.agg ? "" : `OFFSET ${cursor} ROWS FETCH NEXT ${maxItemCount} ROWS ONLY`;
769
+ const query = `select ${columns} from ${options.tablePrefix || ""}${entity} as r ${whereClauses.length ? `where ${whereClauses.join(" AND ")}` : ""} ${orderByClause} ${paginationClause}`;
770
+ return {
771
+ query,
772
+ parameters
773
+ };
774
+ }
775
+ function getSqlOrderBy(args) {
776
+ let orderByClause = args.agg ? "" : "order by r.id asc";
777
+ if (args.orderByArray) {
778
+ orderByClause = `order by `;
779
+ const orderByColumns = [];
780
+ for (const order of args.orderByArray) {
781
+ orderByColumns.push(parseOrderBy(order));
782
+ }
783
+ orderByClause = orderByClause + orderByColumns.join(",");
784
+ } else if (args.orderBy) {
785
+ orderByClause = `order by ${parseOrderBy(args.orderBy)}`;
786
+ }
787
+ return orderByClause;
788
+ }
789
+ function parseOrderBy(order) {
790
+ const orderByParts = order.split("_");
791
+ const orderPropFromOrderBy = orderByParts.slice(0, -1).join(".");
792
+ const orderDirection = orderByParts.at(-1);
793
+ const orderProp = orderPropFromOrderBy === "value" ? `r.[value]` : `r.${orderPropFromOrderBy}`;
794
+ const ordr = `${orderProp} ${orderDirection}`;
795
+ return ordr;
796
+ }
797
+ function getSqlWhere(ctx, parameters, where, namePrefix = "r.", paramNamePrefix = "") {
798
+ const whereClauses = [];
799
+ for (const key in where) {
800
+ const [nameFromWhere, operator] = getWhereNameOperatorPair(key);
801
+ let name = nameFromWhere;
802
+ if (name === "value") {
803
+ name = `[${name}]`;
804
+ }
805
+ const paramName = key;
806
+ const whereVal = where[key];
807
+ if (whereVal == null) continue;
808
+ const f = getSqlWhereInner({
809
+ ...ctx,
810
+ field: nameFromWhere
811
+ }, parameters, {
812
+ operator,
813
+ whereVal,
814
+ name,
815
+ namePrefix,
816
+ paramName,
817
+ paramNamePrefix
818
+ });
819
+ if (f) whereClauses.push(f);
820
+ }
821
+ return whereClauses.join(" AND ");
822
+ }
823
+ function getWhereNameOperatorPair(whereKey) {
824
+ if (whereKey.startsWith("_type")) {
825
+ const operator = whereKey.split("_")[2];
826
+ return ["_type", operator];
827
+ }
828
+ return whereKey.split("_");
829
+ }
830
+ function getSqlWhereInner(ctx, parameters, whereArgs) {
831
+ const {
832
+ operator,
833
+ whereVal,
834
+ name,
835
+ namePrefix,
836
+ paramName,
837
+ paramNamePrefix
838
+ } = whereArgs;
839
+ if (!operator && _lodash.default.isObject(whereVal)) {
840
+ return getSqlWhere(ctx, parameters, whereVal, `${namePrefix}${name}.`, `${paramNamePrefix}${paramName}_`);
841
+ }
842
+ const fn = operatorHandlers[operator || "eq"];
843
+ if (!fn) {
844
+ console.warn(`Unsupported operator: "${operator}"`);
845
+ return null;
846
+ }
847
+ return fn(ctx, parameters, whereArgs);
848
+ }