xmysql-timzoned 0.6.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/lib/xsql.js ADDED
@@ -0,0 +1,1321 @@
1
+ "use strict";
2
+
3
+ const mysql = require("mysql");
4
+ const dataHelp = require("./util/data.helper.js");
5
+ const whereHelp = require("./util/whereClause.helper.js");
6
+ const assert = require("assert");
7
+
8
+ //define class§
9
+ class Xsql {
10
+ constructor(sqlConfig, pool) {
11
+ //define this variables
12
+ this.sqlConfig = {};
13
+ this.pool = {};
14
+ this.metaDb = {};
15
+ this.metaDb.tables = {};
16
+ this.metaDb.routines = [];
17
+
18
+ this.sqlConfig = sqlConfig;
19
+ this.pool = pool;
20
+ }
21
+
22
+ /**************** START : Cache functions ****************/
23
+ init(cbk) {
24
+ this.dbCacheInitAsync((err, results) => {
25
+ cbk(err, results);
26
+ });
27
+ }
28
+
29
+ dbCacheInitAsync(cbk) {
30
+ let self = this;
31
+
32
+ self.pool.query(
33
+ dataHelp.getSchemaQuery(),
34
+ [this.sqlConfig.database],
35
+ (err, results) => {
36
+ if (err) {
37
+ console.log("Cache init failed during database reading");
38
+ console.log(err, results);
39
+ cbk(err, results);
40
+ } else {
41
+ for (var i = 0; i < results.length; ++i) {
42
+ let keys = Object.keys(results[i]);
43
+
44
+ for (var j = 0; j < keys.length; ++j) {
45
+ let value = results[i][keys[j]];
46
+
47
+ results[i][keys[j].toLowerCase()] = value;
48
+
49
+ //console.log(value);
50
+ }
51
+ }
52
+
53
+ self.iterateToCacheTables(results);
54
+ self.iterateToCacheTablePks(results);
55
+ self.iterateToCacheTableColumns(results);
56
+ self.iterateToCacheTableFks(results);
57
+
58
+ // osx mysql server has limitations related to open_tables
59
+ self.pool.query("FLUSH TABLES", [], (err, results) => {
60
+ self.pool.query(dataHelp.getRoutines(), [this.sqlConfig.database], (err, results) => {
61
+ if (err) {
62
+ cbk(err, results)
63
+ } else {
64
+ self.iterateToCacheRoutines(results)
65
+ cbk(null, null)
66
+ }
67
+ })
68
+ });
69
+ }
70
+ }
71
+ );
72
+ }
73
+
74
+ iterateToCacheTables(schemaResults) {
75
+ for (let i = 0; i < schemaResults.length; ++i) {
76
+ let schemaRow = schemaResults[i];
77
+
78
+ let tableName = schemaRow["table_name"];
79
+
80
+ if (!(tableName in this.metaDb.tables)) {
81
+ this.metaDb.tables[tableName] = {};
82
+ this.metaDb.tables[tableName]["primaryKeys"] = [];
83
+ this.metaDb.tables[tableName]["foreignKeys"] = [];
84
+ this.metaDb.tables[tableName]["columns"] = [];
85
+ this.metaDb.tables[tableName]["indicies"] = [];
86
+ this.metaDb.tables[tableName]["isView"] = schemaRow["isView"];
87
+ }
88
+ }
89
+ }
90
+
91
+ iterateToCacheRoutines(routineResults) {
92
+ for (let i = 0; i < routineResults.length; i++) {
93
+ const routine = routineResults[i]
94
+ const routineName = routine.routine_name
95
+ this.metaDb.routines.push(routineName)
96
+ }
97
+ }
98
+
99
+ iterateToCacheTableColumns(schemaResults) {
100
+ for (let i = 0; i < schemaResults.length; ++i) {
101
+ let schemaRow = schemaResults[i];
102
+ let tableName = schemaRow["table_name"];
103
+ let col = {};
104
+ col["column_name"] = schemaRow["column_name"];
105
+ col["ordinal_position"] = schemaRow["ordinal_position"];
106
+ col["column_key"] = schemaRow["column_key"];
107
+ col["data_type"] = schemaRow["data_type"];
108
+ col["column_type"] = schemaRow["column_type"];
109
+
110
+ dataHelp.findOrInsertObjectArrayByKey(
111
+ col,
112
+ "column_name",
113
+ this.metaDb.tables[tableName]["columns"]
114
+ );
115
+ }
116
+ }
117
+
118
+ iterateToCacheTablePks(schemaResults) {
119
+ for (let i = 0; i < schemaResults.length; ++i) {
120
+ let schemaRow = schemaResults[i];
121
+ let tableName = schemaRow["table_name"];
122
+
123
+ if (schemaRow["column_key"] === "PRI") {
124
+ let pk = {};
125
+ pk["column_name"] = schemaRow["column_name"];
126
+ pk["ordinal_position"] = schemaRow["ordinal_position"];
127
+ pk["column_key"] = schemaRow["column_key"];
128
+ pk["data_type"] = schemaRow["data_type"];
129
+ pk["column_type"] = schemaRow["column_type"];
130
+
131
+ dataHelp.findOrInsertObjectArrayByKey(
132
+ pk,
133
+ "column_name",
134
+ this.metaDb.tables[tableName]["primaryKeys"]
135
+ );
136
+ }
137
+ }
138
+ }
139
+
140
+ iterateToCacheTableFks(schemaResults) {
141
+ for (let i = 0; i < schemaResults.length; ++i) {
142
+ let schemaRow = schemaResults[i];
143
+ let tableName = schemaRow["table_name"];
144
+
145
+ if (schemaRow["referenced_table_name"]) {
146
+ let fk = {};
147
+
148
+ fk["column_name"] = schemaRow["column_name"];
149
+ fk["table_name"] = schemaRow["table_name"];
150
+ fk["referenced_table_name"] = schemaRow["referenced_table_name"];
151
+ fk["referenced_column_name"] = schemaRow["referenced_column_name"];
152
+ fk["data_type"] = schemaRow["data_type"];
153
+ fk["column_type"] = schemaRow["column_type"];
154
+
155
+ dataHelp.findOrInsertObjectArrayByKey(
156
+ fk,
157
+ "column_name",
158
+ this.metaDb.tables[tableName]["foreignKeys"]
159
+ );
160
+
161
+ //console.log(fk['referenced_table_name'],fk['referenced_column_name'],tableName, schemaRow['column_name'], this.metaDb.tables[tableName]['foreignKeys'].length)
162
+ }
163
+ }
164
+ }
165
+
166
+ /**************** END : Cache functions ****************/
167
+
168
+ exec(query, params) {
169
+ let _this = this;
170
+ return new Promise(function(resolve, reject) {
171
+ //console.log('mysql>', query, params);
172
+ _this.pool.query(query, params, function(error, rows, _fields) {
173
+ if (error) {
174
+ console.log("mysql> ", error);
175
+ return reject(error);
176
+ }
177
+ return resolve(rows);
178
+ });
179
+ });
180
+ }
181
+
182
+ typeOfColumn(Type) {
183
+ //TODO: Im sure there are more types to handle here
184
+ const strTypes = [
185
+ "varchar",
186
+ "text",
187
+ "char",
188
+ "tinytext",
189
+ "mediumtext",
190
+ "longtext",
191
+ "blob",
192
+ "mediumblob",
193
+ "longblob"
194
+ ];
195
+ const intTypes = [
196
+ "int",
197
+ "long",
198
+ "smallint",
199
+ "mediumint",
200
+ "bigint",
201
+ "tinyint"
202
+ ];
203
+ const flatTypes = ["float", "double", "decimal"];
204
+ const dateTypes = ["date", "datetime", "timestamp", "time", "year"];
205
+
206
+ if (dataHelp.getType(Type, strTypes)) {
207
+ return "string";
208
+ } else if (dataHelp.getType(Type, intTypes)) {
209
+ return "int";
210
+ } else if (dataHelp.getType(Type, flatTypes)) {
211
+ return "float";
212
+ } else if (dataHelp.getType(Type, dateTypes)) {
213
+ return "date";
214
+ } else {
215
+ return "unknown";
216
+ }
217
+ }
218
+
219
+ isTypeOfColumnNumber(Type) {
220
+ //console.log(Type, this.typeOfColumn(Type));
221
+ return (
222
+ "int" === this.typeOfColumn(Type) || "float" === this.typeOfColumn(Type)
223
+ );
224
+ }
225
+
226
+ getLimitClause(reqParams) {
227
+ //defaults
228
+ reqParams._index = 0;
229
+ reqParams._len = 20;
230
+
231
+ if ("_size" in reqParams) {
232
+ if (parseInt(reqParams._size) > 0 && parseInt(reqParams._size) <= 100) {
233
+ reqParams._len = parseInt(reqParams._size);
234
+ } else if (parseInt(reqParams._size) > 100) {
235
+ reqParams._len = 100;
236
+ }
237
+ }
238
+
239
+ if ("_p" in reqParams && parseInt(reqParams._p) > 0) {
240
+ reqParams._index = parseInt(reqParams._p) * reqParams._len;
241
+ }
242
+
243
+ //console.log(reqParams._index, reqParams._len);
244
+
245
+ return [reqParams._index, reqParams._len];
246
+ }
247
+
248
+ prepareBulkInsert(tableName, objectArray, queryParamsObj) {
249
+ if (tableName in this.metaDb.tables && objectArray) {
250
+ let insObj = objectArray[0];
251
+
252
+ // goal => insert into ?? (?,?..?) values ? [tablName, col1,col2...coln,[[ObjValues_1],[ObjValues_2],...[ObjValues_N]]
253
+ queryParamsObj.query = " INSERT INTO ?? ( ";
254
+ queryParamsObj.params.push(tableName);
255
+
256
+ let cols = [];
257
+ let colPresent = false;
258
+
259
+ /**************** START : prepare column names to be inserted ****************/
260
+ // iterate over all column in table and have only ones existing in objects to be inserted
261
+ for (
262
+ var i = 0;
263
+ i < this.metaDb.tables[tableName]["columns"].length;
264
+ ++i
265
+ ) {
266
+ let colName = this.metaDb.tables[tableName]["columns"][i][
267
+ "column_name"
268
+ ];
269
+
270
+ if (colName in insObj) {
271
+ if (colPresent) {
272
+ queryParamsObj.query += ",";
273
+ }
274
+
275
+ queryParamsObj.query += colName;
276
+
277
+ colPresent = true;
278
+ }
279
+
280
+ cols.push(colName);
281
+
282
+ //console.log('> > ', queryParamsObj.query);
283
+ }
284
+
285
+ queryParamsObj.query += " ) values ?";
286
+ /**************** END : prepare column names to be inserted ****************/
287
+
288
+ /**************** START : prepare value object in prepared statement ****************/
289
+ // iterate over sent object array
290
+ let arrOfArr = [];
291
+ for (var i = 0; i < objectArray.length; ++i) {
292
+ let arrValues = [];
293
+ for (var j = 0; j < cols.length; ++j) {
294
+ if (cols[j] in objectArray[i])
295
+ arrValues.push(objectArray[i][cols[j]]);
296
+ }
297
+ arrOfArr.push(arrValues);
298
+ }
299
+ queryParamsObj.params.push(arrOfArr);
300
+ /**************** END : prepare value object in prepared statement ****************/
301
+ }
302
+ }
303
+
304
+ getGroupByClause(_groupby, tableName, queryParamsObj) {
305
+ if (_groupby) {
306
+ queryParamsObj.query += " group by " + _groupby + " ";
307
+ return _groupby;
308
+ }
309
+ }
310
+
311
+ getHavingClause(_having, tableName, queryParamsObj) {
312
+ if (_having) {
313
+ let whereClauseObj = whereHelp.getConditionClause(_having, "having");
314
+
315
+ if (whereClauseObj.err === 0) {
316
+ queryParamsObj.query =
317
+ queryParamsObj.query + " having " + whereClauseObj.query;
318
+ queryParamsObj.params = queryParamsObj.params.concat(
319
+ whereClauseObj.params
320
+ );
321
+ }
322
+
323
+ //console.log('> > > after where clause filling up:', queryParamsObj.query, queryParamsObj.params);
324
+ }
325
+ }
326
+
327
+ getWhereClause(queryparams, tableName, queryParamsObj, appendToWhere) {
328
+ if (queryparams) {
329
+ let whereClauseObj = whereHelp.getConditionClause(queryparams);
330
+
331
+ if (whereClauseObj.err === 0) {
332
+ queryParamsObj.query =
333
+ queryParamsObj.query + appendToWhere + whereClauseObj.query;
334
+ queryParamsObj.params = queryParamsObj.params.concat(
335
+ whereClauseObj.params
336
+ );
337
+ }
338
+ }
339
+ }
340
+
341
+ getOrderByClause(queryparams, tableName, queryParamsObj) {
342
+ if (queryparams._sort) {
343
+ queryParamsObj.query += " ORDER BY ";
344
+
345
+ let orderByCols = queryparams._sort.split(",");
346
+
347
+ for (let i = 0; i < orderByCols.length; ++i) {
348
+ if (i) {
349
+ queryParamsObj.query += ", ";
350
+ }
351
+ const aggregationFunction = this.getAggregationFunction(orderByCols[i]);
352
+ const columnName = this.getColumnNameWithoutAggregationFunctions(orderByCols[i]);
353
+ const orderByDirection = orderByCols[i][0] === "-" ? 'DESC' : 'ASC';
354
+
355
+ if (aggregationFunction) {
356
+ queryParamsObj.query += `${aggregationFunction}(??) ${orderByDirection}`;
357
+ queryParamsObj.params.push(columnName);
358
+ } else {
359
+ queryParamsObj.query += `?? ${orderByDirection}`;
360
+ queryParamsObj.params.push(columnName);
361
+ }
362
+ }
363
+ }
364
+ }
365
+
366
+ getColumnsForSelectStmtWithGrpBy(reqQueryParams, tableName, queryParamsObj) {
367
+ let grpByCols = reqQueryParams._groupby.split(",");
368
+
369
+ for (var i = 0; i < grpByCols.length; ++i) {
370
+ if (i) {
371
+ queryParamsObj.query += ",";
372
+ }
373
+ queryParamsObj.query += " ??";
374
+ queryParamsObj.params.push(grpByCols[i]);
375
+ }
376
+
377
+ queryParamsObj.query += ",count(1) as _count ";
378
+ }
379
+
380
+ getColumnsForGroupBy(tableName, reqQueryParams, queryParamsObj) {
381
+ const updatedQueryParams = Object.assign({}, reqQueryParams);
382
+ if ('_groupbyfields' in updatedQueryParams) {
383
+ // allows you to group by different fields than you have in the select
384
+ updatedQueryParams['_fields'] = updatedQueryParams['_groupbyfields'];
385
+ }
386
+
387
+ return this.getColumnsForSelectStmt(tableName, updatedQueryParams, queryParamsObj)
388
+ }
389
+
390
+ getColumnsForSelectStmt(tableName, reqQueryParams, queryParamsObj) {
391
+ let table = this.metaDb.tables[tableName];
392
+ let cols = [];
393
+ let _fieldsInQuery = [];
394
+ let removeFieldsObj = {};
395
+
396
+ // populate _fields array from query params
397
+ if ("_fields" in reqQueryParams) {
398
+ _fieldsInQuery = reqQueryParams["_fields"].split(",");
399
+ } else {
400
+ queryParamsObj.query += " * ";
401
+ return " * ";
402
+ }
403
+
404
+ // get column name in _fields and mark column name which start with '-'
405
+ for (let i = 0; i < _fieldsInQuery.length; ++i) {
406
+ if (_fieldsInQuery[i][0] === "-") {
407
+ removeFieldsObj[
408
+ _fieldsInQuery[i].substring(1, _fieldsInQuery[i].length)
409
+ ] = 1;
410
+ } else {
411
+ cols.push(_fieldsInQuery[i]);
412
+ }
413
+ }
414
+
415
+ if (!cols.length) {
416
+ // for each column in table - add only which are not in removeFieldsObj
417
+ for (let i = 0; i < table["columns"].length; ++i) {
418
+ if (!(table["columns"][i]["column_name"] in removeFieldsObj)) {
419
+ cols.push(table["columns"][i]["column_name"]);
420
+ }
421
+ }
422
+ } else {
423
+ cols = this.removeUnknownColumns(cols, tableName);
424
+ }
425
+
426
+ for (var i = 0; i < cols.length; ++i) {
427
+ if (i) {
428
+ queryParamsObj.query += ",";
429
+ }
430
+ const aggregationFunction = this.getAggregationFunction(cols[i]);
431
+
432
+ if (aggregationFunction) {
433
+ queryParamsObj.query += `${aggregationFunction}(??)`;
434
+ const columnName = this.getColumnNameWithoutAggregationFunctions(cols[i]);
435
+ queryParamsObj.params.push(columnName);
436
+ } else {
437
+ queryParamsObj.query += "??";
438
+ queryParamsObj.params.push(cols[i]);
439
+ }
440
+ }
441
+
442
+ return cols.join(",");
443
+ }
444
+
445
+ getAggregationFunction(rawColumnName) {
446
+ const AGGREGATION_FUNCTION_REGEX = /^[-]?(AVG|BIT_AND|BIT_OR|BIT_XOR|COUNT|COUNTDISTINCT|GROUP_CONCAT|JSON_ARRAYAGG|JSON_OBJECTAGG|MAX|MIN|STD|STDDEV|STDDEV_POP|STDDEV_SAMP|SUM|VAR_POP|VAR_SAMP|VARIANCE)\((.*)\)$/i;
447
+ const aggFuncMatch = rawColumnName.match(AGGREGATION_FUNCTION_REGEX);
448
+ if (aggFuncMatch && aggFuncMatch.length === 3) {
449
+ // match will look like (3) ["AVG(timestamp)", "AVG", "timestamp", index: 0, input: "AVG(timestamp)", groups: undefined]
450
+ return aggFuncMatch[1];
451
+ }
452
+ return null;
453
+ }
454
+
455
+ getColumnNameWithoutAggregationFunctions(rawColumnName) {
456
+ const AGGREGATION_FUNCTION_REGEX = /^[-]?(AVG|BIT_AND|BIT_OR|BIT_XOR|COUNT|COUNTDISTINCT|GROUP_CONCAT|JSON_ARRAYAGG|JSON_OBJECTAGG|MAX|MIN|STD|STDDEV|STDDEV_POP|STDDEV_SAMP|SUM|VAR_POP|VAR_SAMP|VARIANCE)\((.*)\)$/i;
457
+ const aggFuncMatch = rawColumnName.match(AGGREGATION_FUNCTION_REGEX);
458
+ if (aggFuncMatch && aggFuncMatch.length === 3) {
459
+ // match will look like (3) ["AVG(timestamp)", "AVG", "timestamp", index: 0, input: "AVG(timestamp)", groups: undefined]
460
+ return aggFuncMatch[2];
461
+ }
462
+ return rawColumnName.replace(/-/, '');
463
+ }
464
+
465
+ removeUnknownColumns(inputColumns, tableName) {
466
+ let cols = inputColumns;
467
+ let unknown_cols_in_input = [];
468
+ let shadowCols = [];
469
+ let tableColumns = this.metaDb.tables[tableName]["columns"];
470
+
471
+ // find unknown fields if any
472
+ for (var j = 0; j < cols.length; ++j) {
473
+ let found = 0;
474
+ // Used to allow aggregation functions like AVG(timestamp)
475
+ let columnNameWithoutAggregationClauses = this.getColumnNameWithoutAggregationFunctions(cols[j]);
476
+
477
+ for (var i = 0; i < tableColumns.length; ++i) {
478
+ if (tableColumns[i]["column_name"] === columnNameWithoutAggregationClauses) {
479
+ found = 1;
480
+ break;
481
+ }
482
+ }
483
+
484
+ if (!found) {
485
+ unknown_cols_in_input.push(j);
486
+ }
487
+ }
488
+
489
+ // if there are unknown fields - remove and ignore 'em
490
+ if (unknown_cols_in_input.length) {
491
+ for (var i = 0; i < cols.length; ++i) {
492
+ if (unknown_cols_in_input.indexOf(i) === -1) {
493
+ shadowCols.push(cols[i]);
494
+ }
495
+ }
496
+
497
+ cols = [];
498
+ cols = shadowCols;
499
+ }
500
+
501
+ return cols;
502
+ }
503
+
504
+ getPrimaryKeyName(tableName) {
505
+ let pk = null;
506
+ if (tableName in this.metaDb.tables) {
507
+ pk = this.metaDb.tables[tableName].primaryKeys[0]["column_name"];
508
+ }
509
+ return pk;
510
+ }
511
+
512
+ getPrimaryKeyWhereClause(tableName, pksValues) {
513
+ let whereClause = "";
514
+ let whereCol = "";
515
+ let whereValue = "";
516
+ let pks = [];
517
+
518
+ if (tableName in this.metaDb.tables) {
519
+ pks = this.metaDb.tables[tableName].primaryKeys;
520
+ } else {
521
+ return null;
522
+ }
523
+
524
+ // number of primary keys in table and one sent should be same
525
+ if (pksValues.length !== pks.length) {
526
+ return null;
527
+ }
528
+
529
+ // get a where clause out of the above columnNames and their values
530
+ for (let i = 0; i < pks.length; ++i) {
531
+ let type = dataHelp.getColumnType(pks[i]);
532
+
533
+ whereCol = pks[i]["column_name"];
534
+
535
+ if (type === "string") {
536
+ whereValue = mysql.escape(pksValues[i]);
537
+ } else if (type === "int") {
538
+ whereValue = parseInt(pksValues[i]);
539
+ } else if (type === "float") {
540
+ whereValue = parseFloat(pksValues[i]);
541
+ } else if (type === "date") {
542
+ whereValue = Date(pksValues[i]);
543
+ } else {
544
+ console.error(pks[i]);
545
+ assert(false, "Unhandled type of primary key");
546
+ }
547
+
548
+ if (i) {
549
+ whereClause += " and ";
550
+ }
551
+
552
+ whereClause += whereCol + " = " + whereValue;
553
+ }
554
+
555
+ return whereClause;
556
+ }
557
+
558
+ getForeignKeyWhereClause(parentTable, parentId, childTable) {
559
+ let whereValue = "";
560
+
561
+ //get all foreign keys of child table
562
+ let fks = this.metaDb.tables[childTable].foreignKeys;
563
+ let fk = dataHelp.findObjectInArrayByKey(
564
+ "referenced_table_name",
565
+ parentTable,
566
+ fks
567
+ );
568
+ let whereCol = fk["column_name"];
569
+ let colType = dataHelp.getColumnType(fk);
570
+
571
+ if (colType === "string") {
572
+ whereValue = mysql.escape(parentId);
573
+ } else if (colType === "int") {
574
+ whereValue = mysql.escape(parseInt(parentId));
575
+ } else if (colType === "float") {
576
+ whereValue = mysql.escape(parseFloat(parentId));
577
+ } else if (colType === "date") {
578
+ whereValue = mysql.escape(Date(parentId));
579
+ } else {
580
+ console.error(pks[i]);
581
+ assert(false, "Unhandled column type in foreign key handling");
582
+ }
583
+
584
+ return whereCol + " = " + whereValue;
585
+ }
586
+
587
+ prepareRoute(internal, httpType, apiPrefix, urlRoute, routeType) {
588
+ let route = {};
589
+ route["httpType"] = httpType;
590
+ route["routeUrl"] = apiPrefix + urlRoute;
591
+ if (internal) {
592
+ route["routeType"] = routeType;
593
+ }
594
+ return route;
595
+ }
596
+
597
+ getSchemaRoutes(internal, apiPrefix) {
598
+ let schemaRoutes = [];
599
+
600
+ for (var tableName in this.metaDb.tables) {
601
+ if (tableName in this.sqlConfig.ignoreTables) {
602
+ //console.log('ignore table', tableName);
603
+ } else {
604
+ let routes = [];
605
+ let tableObj = {};
606
+ let table = this.metaDb.tables[tableName];
607
+ let isView = this.metaDb.tables[tableName]["isView"];
608
+
609
+ tableObj["resource"] = tableName;
610
+
611
+ // order of routes is important for express routing - DO NOT CHANGE ORDER
612
+ routes.push(
613
+ this.prepareRoute(
614
+ internal,
615
+ "get",
616
+ apiPrefix,
617
+ tableName + "/describe",
618
+ "describe"
619
+ )
620
+ );
621
+ routes.push(
622
+ this.prepareRoute(
623
+ internal,
624
+ "get",
625
+ apiPrefix,
626
+ tableName + "/count",
627
+ "count"
628
+ )
629
+ );
630
+ routes.push(
631
+ this.prepareRoute(
632
+ internal,
633
+ "get",
634
+ apiPrefix,
635
+ tableName + "/groupby",
636
+ "groupby"
637
+ )
638
+ );
639
+ routes.push(
640
+ this.prepareRoute(
641
+ internal,
642
+ "get",
643
+ apiPrefix,
644
+ tableName + "/distinct",
645
+ "distinct"
646
+ )
647
+ );
648
+ routes.push(
649
+ this.prepareRoute(
650
+ internal,
651
+ "get",
652
+ apiPrefix,
653
+ tableName + "/ugroupby",
654
+ "ugroupby"
655
+ )
656
+ );
657
+ routes.push(
658
+ this.prepareRoute(
659
+ internal,
660
+ "get",
661
+ apiPrefix,
662
+ tableName + "/chart",
663
+ "chart"
664
+ )
665
+ );
666
+ routes.push(
667
+ this.prepareRoute(
668
+ internal,
669
+ "get",
670
+ apiPrefix,
671
+ tableName + "/aggregate",
672
+ "aggregate"
673
+ )
674
+ );
675
+ routes.push(
676
+ this.prepareRoute(
677
+ internal,
678
+ "get",
679
+ apiPrefix,
680
+ tableName + "/findOne",
681
+ "findOne"
682
+ )
683
+ );
684
+ routes.push(
685
+ this.prepareRoute(
686
+ internal,
687
+ "get",
688
+ apiPrefix,
689
+ tableName + "/autoChart",
690
+ "autoChart"
691
+ )
692
+ );
693
+
694
+ if (!isView && !this.sqlConfig.readOnly) {
695
+ routes.push(
696
+ this.prepareRoute(internal, "post", apiPrefix, tableName, "create")
697
+ );
698
+ }
699
+ routes.push(
700
+ this.prepareRoute(internal, "get", apiPrefix, tableName, "list")
701
+ );
702
+
703
+ if (!isView && !this.sqlConfig.readOnly) {
704
+ routes.push(
705
+ this.prepareRoute(
706
+ internal,
707
+ "post",
708
+ apiPrefix,
709
+ tableName + "/bulk",
710
+ "bulkInsert"
711
+ )
712
+ );
713
+ routes.push(
714
+ this.prepareRoute(
715
+ internal,
716
+ "delete",
717
+ apiPrefix,
718
+ tableName + "/bulk",
719
+ "bulkDelete"
720
+ )
721
+ );
722
+ }
723
+ routes.push(
724
+ this.prepareRoute(
725
+ internal,
726
+ "get",
727
+ apiPrefix,
728
+ tableName + "/bulk",
729
+ "bulkRead"
730
+ )
731
+ );
732
+
733
+ if (!isView && !this.sqlConfig.readOnly) {
734
+ routes.push(
735
+ this.prepareRoute(internal, "put", apiPrefix, tableName, "update")
736
+ );
737
+ routes.push(
738
+ this.prepareRoute(
739
+ internal,
740
+ "patch",
741
+ apiPrefix,
742
+ tableName + "/:id",
743
+ "patch"
744
+ )
745
+ );
746
+ routes.push(
747
+ this.prepareRoute(
748
+ internal,
749
+ "delete",
750
+ apiPrefix,
751
+ tableName + "/:id",
752
+ "delete"
753
+ )
754
+ );
755
+ }
756
+
757
+ routes.push(
758
+ this.prepareRoute(
759
+ internal,
760
+ "get",
761
+ apiPrefix,
762
+ tableName + "/:id",
763
+ "read"
764
+ )
765
+ );
766
+ routes.push(
767
+ this.prepareRoute(
768
+ internal,
769
+ "get",
770
+ apiPrefix,
771
+ tableName + "/:id/exists",
772
+ "exists"
773
+ )
774
+ );
775
+
776
+ for (var j = 0; j < table["foreignKeys"].length; ++j) {
777
+ let fk = table["foreignKeys"][j];
778
+
779
+ if (fk["referenced_table_name"] in this.sqlConfig.ignoreTables) {
780
+ //console.log('ignore table',fk['referenced_table_name']);
781
+ } else {
782
+ routes.push(
783
+ this.prepareRoute(
784
+ internal,
785
+ "get",
786
+ apiPrefix,
787
+ fk["referenced_table_name"] + "/:id/" + fk["table_name"],
788
+ "relational"
789
+ )
790
+ );
791
+ }
792
+ }
793
+
794
+ var procList = this.getProcList()
795
+ for (var j = 0; j < procList.length; j++) {
796
+ routes.push(this.prepareRoute(internal, 'post', apiPrefix, '_proc/' + procList[j]))
797
+ }
798
+
799
+ tableObj['routes'] = routes;
800
+
801
+ schemaRoutes.push(tableObj);
802
+ }
803
+ }
804
+
805
+ return schemaRoutes;
806
+ }
807
+
808
+ getProcList() {
809
+ let procRoutes = []
810
+ for (let procName in this.metaDb.routines) {
811
+ procRoutes.push(this.metaDb.routines[procName])
812
+ }
813
+ return procRoutes
814
+ }
815
+
816
+ getJoinType(joinInQueryParams) {
817
+ //console.log('joinInQueryParams',joinInQueryParams);
818
+
819
+ switch (joinInQueryParams) {
820
+ case "_lj":
821
+ return " left join ";
822
+ break;
823
+
824
+ case "_rj":
825
+ return " right join ";
826
+ break;
827
+
828
+ // case '_fj':
829
+ // return ' full join '
830
+ // break;
831
+
832
+ case "_ij":
833
+ return " inner join ";
834
+ break;
835
+
836
+ case "_j":
837
+ return " join ";
838
+ break;
839
+ }
840
+
841
+ return " join ";
842
+ }
843
+
844
+ globalRoutesPrint(apiPrefix) {
845
+ let r = [];
846
+
847
+ r.push(apiPrefix + "tables");
848
+ r.push(apiPrefix + "xjoin");
849
+
850
+ if (this.sqlConfig.dynamic) {
851
+ r.push(apiPrefix + "dynamic");
852
+ r.push("/upload");
853
+ r.push("/uploads");
854
+ r.push("/download");
855
+ }
856
+
857
+ return r;
858
+ }
859
+
860
+ getChartQueryAndParamsFromStepPair(
861
+ tableName,
862
+ columnName,
863
+ stepArray,
864
+ isRange = false
865
+ ) {
866
+ let obj = {};
867
+
868
+ obj.query = "";
869
+ obj.params = [];
870
+
871
+ //console.log('getChartQueryAndParamsFromStepArray',isRange);
872
+
873
+ //select ? as ??, count(*) as _count from ?? where ?? between ? and ?
874
+
875
+ if (
876
+ stepArray.length &&
877
+ stepArray.length >= 2 &&
878
+ stepArray.length % 2 === 0
879
+ ) {
880
+ for (
881
+ let i = 0;
882
+ i < stepArray.length && stepArray.length >= 2;
883
+ i = i + 2
884
+ ) {
885
+ obj.query = obj.query + dataHelp.getChartQuery();
886
+
887
+ if (i + 2 < stepArray.length) {
888
+ obj.query = obj.query + " union ";
889
+ }
890
+
891
+ obj.params.push(stepArray[i] + " to " + stepArray[i + 1]);
892
+ obj.params.push(columnName);
893
+ obj.params.push(tableName);
894
+ obj.params.push(columnName);
895
+ obj.params.push(stepArray[i]);
896
+ obj.params.push(stepArray[i + 1]);
897
+ }
898
+ }
899
+
900
+ //console.log('step spread query', obj);
901
+
902
+ return obj;
903
+ }
904
+
905
+ getChartQueryAndParamsFromStepArray(
906
+ tableName,
907
+ columnName,
908
+ stepArray,
909
+ isRange = false
910
+ ) {
911
+ let obj = {};
912
+
913
+ obj.query = "";
914
+ obj.params = [];
915
+
916
+ //console.log('getChartQueryAndParamsFromStepArray',isRange);
917
+
918
+ if (stepArray.length && stepArray.length >= 2) {
919
+ for (let i = 0; i < stepArray.length - 1; i = i + 1) {
920
+ obj.query = obj.query + dataHelp.getChartQuery();
921
+ if (i + 2 < stepArray.length) {
922
+ obj.query = obj.query + " union ";
923
+ }
924
+
925
+ if (i && isRange === false) {
926
+ stepArray[i] = stepArray[i] + 1;
927
+ }
928
+
929
+ if (isRange === false) {
930
+ obj.params.push(stepArray[i] + " to " + stepArray[i + 1]);
931
+ } else {
932
+ obj.params.push(stepArray[0] + " to " + stepArray[i + 1]);
933
+ }
934
+
935
+ obj.params.push(columnName);
936
+ obj.params.push(tableName);
937
+ obj.params.push(columnName);
938
+
939
+ if (isRange === false) {
940
+ obj.params.push(stepArray[i]);
941
+ obj.params.push(stepArray[i + 1]);
942
+ } else {
943
+ obj.params.push(stepArray[0]);
944
+ obj.params.push(stepArray[i + 1]);
945
+ }
946
+ }
947
+ }
948
+
949
+ //console.log('step spread query', obj);
950
+
951
+ return obj;
952
+ }
953
+
954
+ getChartQueryAndParamsFromMinMaxStddev(
955
+ tableName,
956
+ columnName,
957
+ min,
958
+ max,
959
+ stddev,
960
+ isRange = false
961
+ ) {
962
+ let stepArray = dataHelp.getStepArray(min, max, stddev);
963
+
964
+ //console.log('steparray', stepArray);
965
+
966
+ let obj = this.getChartQueryAndParamsFromStepArray(
967
+ tableName,
968
+ columnName,
969
+ stepArray,
970
+ isRange
971
+ );
972
+
973
+ //console.log('steparray', obj);
974
+
975
+ return obj;
976
+ }
977
+
978
+ getChartQueryAndParamsFromMinMaxStep(
979
+ tableName,
980
+ columnName,
981
+ min,
982
+ max,
983
+ step,
984
+ isRange = false
985
+ ) {
986
+ let stepArray = dataHelp.getStepArraySimple(min, max, step);
987
+
988
+ //console.log('steparray', stepArray);
989
+
990
+ let obj = this.getChartQueryAndParamsFromStepArray(
991
+ tableName,
992
+ columnName,
993
+ stepArray,
994
+ isRange
995
+ );
996
+
997
+ //console.log('steparray', obj);
998
+
999
+ return obj;
1000
+ }
1001
+
1002
+ _getGrpByHavingOrderBy(req, tableName, queryParamsObj, listType) {
1003
+ /**************** add group by ****************/
1004
+ this.getGroupByClause(
1005
+ req.query._groupby,
1006
+ req.app.locals._tableName,
1007
+ queryParamsObj
1008
+ );
1009
+
1010
+ /**************** add having ****************/
1011
+ this.getHavingClause(
1012
+ req.query._having,
1013
+ req.app.locals._tableName,
1014
+ queryParamsObj
1015
+ );
1016
+
1017
+ /**************** add order clause ****************/
1018
+ this.getOrderByClause(req.query, req.app.locals._tableName, queryParamsObj);
1019
+
1020
+ /**************** add limit clause ****************/
1021
+ if (listType === 2) {
1022
+ //nested
1023
+ queryParamsObj.query += " limit 1 ";
1024
+ } else {
1025
+ queryParamsObj.query += " limit ?,? ";
1026
+ queryParamsObj.params = queryParamsObj.params.concat(
1027
+ this.getLimitClause(req.query)
1028
+ );
1029
+ }
1030
+ }
1031
+
1032
+ /**
1033
+ *
1034
+ * @param req
1035
+ * @param res
1036
+ * @param queryParamsObj : {query, params}
1037
+ * @param listType : 0:list, 1:nested, 2:findOne, 3:bulkRead, 4:distinct, 5:xjoin
1038
+ *
1039
+ * Updates query, params for query of type listType
1040
+ */
1041
+ prepareListQuery(req, res, queryParamsObj, listType = 0) {
1042
+ queryParamsObj.query = "select ";
1043
+ queryParamsObj.params = [];
1044
+
1045
+ if (listType === 4) {
1046
+ //list type distinct
1047
+ queryParamsObj.query += " distinct ";
1048
+ }
1049
+
1050
+ /**************** select columns ****************/
1051
+ if (req.query._groupby) {
1052
+ this.getColumnsForSelectStmtWithGrpBy(
1053
+ req.query,
1054
+ req.app.locals._tableName,
1055
+ queryParamsObj
1056
+ );
1057
+ } else {
1058
+ this.getColumnsForSelectStmt(
1059
+ req.app.locals._tableName,
1060
+ req.query,
1061
+ queryParamsObj
1062
+ );
1063
+ }
1064
+
1065
+ /**************** add tableName ****************/
1066
+ queryParamsObj.query += " from ?? ";
1067
+
1068
+ if (listType === 1) {
1069
+ //nested list
1070
+
1071
+ req.app.locals._tableName = req.app.locals._childTable;
1072
+
1073
+ queryParamsObj.params.push(req.app.locals._childTable);
1074
+
1075
+ queryParamsObj.query += " where ";
1076
+
1077
+ /**************** add where foreign key ****************/
1078
+ let whereClause = this.getForeignKeyWhereClause(
1079
+ req.app.locals._parentTable,
1080
+ req.params.id,
1081
+ req.app.locals._childTable
1082
+ );
1083
+
1084
+ if (!whereClause) {
1085
+ return res.status(400).send({
1086
+ error:
1087
+ "Table is made of composite primary keys - all keys were not in input"
1088
+ });
1089
+ }
1090
+ queryParamsObj.query += whereClause;
1091
+
1092
+ this.getWhereClause(
1093
+ req.query._where,
1094
+ req.app.locals._tableName,
1095
+ queryParamsObj,
1096
+ " and "
1097
+ );
1098
+ } else if (listType === 3) {
1099
+ //bulkRead
1100
+
1101
+ // select * from table where pk in (ids) and whereConditions
1102
+ queryParamsObj.params.push(req.app.locals._tableName);
1103
+ queryParamsObj.query += " where ?? in ";
1104
+ queryParamsObj.params.push(
1105
+ this.getPrimaryKeyName(req.app.locals._tableName)
1106
+ );
1107
+
1108
+ queryParamsObj.query += "(";
1109
+
1110
+ if (req.query && req.query._ids) {
1111
+ let ids = req.query._ids.split(",");
1112
+ for (var i = 0; i < ids.length; ++i) {
1113
+ if (i) {
1114
+ queryParamsObj.query += ",";
1115
+ }
1116
+ queryParamsObj.query += "?";
1117
+ queryParamsObj.params.push(ids[i]);
1118
+ }
1119
+ }
1120
+ queryParamsObj.query += ") ";
1121
+ this.getWhereClause(
1122
+ req.query._where,
1123
+ req.app.locals._tableName,
1124
+ queryParamsObj,
1125
+ " and "
1126
+ );
1127
+ } else {
1128
+ queryParamsObj.params.push(req.app.locals._tableName);
1129
+
1130
+ /**************** add where clause ****************/
1131
+ this.getWhereClause(
1132
+ req.query._where,
1133
+ req.app.locals._tableName,
1134
+ queryParamsObj,
1135
+ " where "
1136
+ );
1137
+ }
1138
+
1139
+ this._getGrpByHavingOrderBy(req, req.app.locals._tableName, queryParamsObj);
1140
+
1141
+ //console.log(queryParamsObj.query, queryParamsObj.params);
1142
+ }
1143
+
1144
+ _joinTableNames(isSecondJoin, joinTables, index, queryParamsObj) {
1145
+ if (isSecondJoin) {
1146
+ /**
1147
+ * in second join - there will be ONE table and an ON condition
1148
+ * if clause deals with this
1149
+ *
1150
+ */
1151
+
1152
+ // add : join / left join / right join / full join / inner join
1153
+ queryParamsObj.query += this.getJoinType(joinTables[index]);
1154
+ queryParamsObj.query += " ?? as ?? ";
1155
+
1156
+ // eg: tbl.tableName
1157
+ let tableNameAndAs = joinTables[index + 1].split(".");
1158
+
1159
+ if (
1160
+ tableNameAndAs.length === 2 &&
1161
+ !(tableNameAndAs[1] in this.sqlConfig.ignoreTables)
1162
+ ) {
1163
+ queryParamsObj.params.push(tableNameAndAs[1]);
1164
+ queryParamsObj.params.push(tableNameAndAs[0]);
1165
+ } else {
1166
+ queryParamsObj.grammarErr = 1;
1167
+ console.log("there was no dot for tableName ", joinTables[index + 1]);
1168
+ }
1169
+ } else {
1170
+ /**
1171
+ * in first join - there will be TWO tables and an ON condition
1172
+ * else clause deals with this
1173
+ */
1174
+
1175
+ // first table
1176
+ queryParamsObj.query += " ?? as ?? ";
1177
+ // add : join / left join / right join / full join / inner join
1178
+ queryParamsObj.query += this.getJoinType(joinTables[index + 1]);
1179
+ // second table
1180
+ queryParamsObj.query += " ?? as ?? ";
1181
+
1182
+ let tableNameAndAs = joinTables[index].split(".");
1183
+ if (
1184
+ tableNameAndAs.length === 2 &&
1185
+ !(tableNameAndAs[1] in this.sqlConfig.ignoreTables)
1186
+ ) {
1187
+ queryParamsObj.params.push(tableNameAndAs[1]);
1188
+ queryParamsObj.params.push(tableNameAndAs[0]);
1189
+ } else {
1190
+ queryParamsObj.grammarErr = 1;
1191
+ console.log("there was no dot for tableName ", joinTables[index]);
1192
+ }
1193
+
1194
+ tableNameAndAs = [];
1195
+ tableNameAndAs = joinTables[index + 2].split(".");
1196
+ if (
1197
+ tableNameAndAs.length === 2 &&
1198
+ !(tableNameAndAs[1] in this.sqlConfig.ignoreTables)
1199
+ ) {
1200
+ queryParamsObj.params.push(tableNameAndAs[1]);
1201
+ queryParamsObj.params.push(tableNameAndAs[0]);
1202
+ } else {
1203
+ queryParamsObj.grammarErr = 1;
1204
+ console.log("there was no dot for tableName ", joinTables[index]);
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ prepareJoinQuery(req, res, queryParamsObj) {
1210
+ queryParamsObj.query = "SELECT ";
1211
+ queryParamsObj.grammarErr = 0;
1212
+
1213
+ while (1) {
1214
+ /**************** START : get fields ****************/
1215
+ if (req.query._fields) {
1216
+ let fields = req.query._fields.split(",");
1217
+
1218
+ // from _fields to - ??, ??, ?? [col1,col2,col3]
1219
+ for (var i = 0; i < fields.length && !queryParamsObj.grammarErr; ++i) {
1220
+ if (i) {
1221
+ queryParamsObj.query += ",";
1222
+ }
1223
+ queryParamsObj.query += " ?? ";
1224
+ queryParamsObj.params.push(fields[i]);
1225
+ let aliases = fields[i].split(".");
1226
+ if (aliases.length === 2) {
1227
+ queryParamsObj.query += "as " + aliases[0] + "_" + aliases[1];
1228
+ //console.log(queryParamsObj.query);
1229
+ } else {
1230
+ queryParamsObj.grammarErr = 1;
1231
+ }
1232
+ }
1233
+ } else {
1234
+ queryParamsObj.grammarErr = 1;
1235
+ }
1236
+
1237
+ queryParamsObj.query += " from ";
1238
+
1239
+ if (queryParamsObj.grammarErr) {
1240
+ break;
1241
+ }
1242
+
1243
+ /**************** END : get fields ****************/
1244
+
1245
+ /**************** START : get join + on ****************/
1246
+ let joinTables = req.query._join.split(",");
1247
+ if (joinTables.length < 3) {
1248
+ //console.log('grammar error ', joinTables.length);
1249
+ queryParamsObj.grammarErr = 1;
1250
+ break;
1251
+ }
1252
+
1253
+ //console.log('jointables.length', joinTables);
1254
+
1255
+ let onCondnCount = 0;
1256
+
1257
+ for (
1258
+ let i = 0;
1259
+ i < joinTables.length - 1 && queryParamsObj.grammarErr === 0;
1260
+ i = i + 2
1261
+ ) {
1262
+ onCondnCount++;
1263
+
1264
+ this._joinTableNames(i, joinTables, i, queryParamsObj);
1265
+
1266
+ if (queryParamsObj.grammarErr) {
1267
+ console.log("failed at _joinTableNames", queryParamsObj);
1268
+ break;
1269
+ }
1270
+
1271
+ //console.log('after join tables', queryParamsObj);
1272
+
1273
+ let onCondn = "_on" + onCondnCount;
1274
+ let onCondnObj = {};
1275
+ if (onCondn in req.query) {
1276
+ //console.log(onCondn, req.query[onCondn]);
1277
+ onCondnObj = whereHelp.getConditionClause(req.query[onCondn], " on ");
1278
+ //console.log('onCondnObj', onCondnObj);
1279
+ queryParamsObj.query += " on " + onCondnObj.query;
1280
+ queryParamsObj.params = queryParamsObj.params.concat(
1281
+ onCondnObj.params
1282
+ );
1283
+ } else {
1284
+ queryParamsObj.grammarErr = 1;
1285
+ //console.log('No on condition: ', onCondn);
1286
+ break;
1287
+ }
1288
+
1289
+ if (i === 0) {
1290
+ i = i + 1;
1291
+ }
1292
+ }
1293
+ /**************** END : get join + on ****************/
1294
+
1295
+ if (queryParamsObj.grammarErr) {
1296
+ break;
1297
+ } else {
1298
+ this.getWhereClause(
1299
+ req.query._where,
1300
+ " ignore ",
1301
+ queryParamsObj,
1302
+ " where "
1303
+ );
1304
+ this._getGrpByHavingOrderBy(req, "ignore", queryParamsObj, 5);
1305
+ //console.log('after where',queryParamsObj);
1306
+ }
1307
+
1308
+ break;
1309
+ }
1310
+
1311
+ if (queryParamsObj.grammarErr) {
1312
+ queryParamsObj.query = "";
1313
+ queryParamsObj.params = [];
1314
+ }
1315
+
1316
+ return queryParamsObj;
1317
+ }
1318
+ }
1319
+
1320
+ //expose class
1321
+ module.exports = Xsql;