rads-db 3.1.16 → 3.2.1

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