databaseHelper.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. const sqlite3 = require('sqlite3');
  2. const CONFIG = require('./config.js');
  3. function DatabaseHelper() {
  4. this.db = null;
  5. this.models = [];
  6. }
  7. DatabaseHelper.prototype.init = function(allModels) {
  8. this.models = allModels;
  9. return new Promise((ok, ko) => {
  10. this.db = new sqlite3.Database(CONFIG.database, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE | sqlite3.OPEN_FULLMUTEX, async (err) => {
  11. if (err) {
  12. ko(err);
  13. return;
  14. }
  15. let types = this.models;
  16. for (let i =0; i < types.length; ++i) {
  17. let instance = new types[i]();
  18. await instance.createOrUpdateBase(this);
  19. }
  20. console.log("Database is ready");
  21. ok();
  22. });
  23. });
  24. }
  25. DatabaseHelper.prototype.runSql = function(sqlStatement, args, fetch) {
  26. console.info("DatabaseHelper::runSql ", sqlStatement, args);
  27. return new Promise((ok, ko) => {
  28. if (sqlStatement.indexOf('--') >= 0) {
  29. console.error("Rejecting SQL request containing comments");
  30. ko("SQL Comment Error");
  31. return;
  32. }
  33. try {
  34. let fnc = (fetch === false ? this.db.run : this.db.all);
  35. fnc.call(this.db, sqlStatement, args, function(err, data) {
  36. if (err) {
  37. console.error(err);
  38. ko(err);
  39. } else {
  40. ok(fetch !== false ? data : this.lastID);
  41. }
  42. });
  43. } catch (err) {
  44. console.error(err);
  45. ko(err);
  46. }
  47. });
  48. }
  49. DatabaseHelper.prototype.insertOne = async function(object) {
  50. let columns = [];
  51. let args = [];
  52. for (let [key, val] of Object.entries(object.describe())) {
  53. columns.push("`"+key+"`");
  54. args.push(val);
  55. }
  56. let request = "insert into `" +object.getTableName()
  57. +"` (" +columns.join(", ")
  58. +") VALUES (" +columns.map(i => "?").join(",")+")";
  59. let id = await this.runSql(request, args, false);
  60. if (object.id !== undefined)
  61. object.id = id;
  62. return id;
  63. }
  64. DatabaseHelper.prototype.insertMultipleSameTable = async function(arr) {
  65. let columns = [];
  66. let args = [];
  67. if (!arr || !arr.length)
  68. return;
  69. for (let [key, val] of Object.entries(arr[0].describe()))
  70. columns.push("`"+key+"`");
  71. let request = "insert into `" +arr[0].getTableName()
  72. +"` (" +columns.join(", ")
  73. +") VALUES ";
  74. let valuesArr = [];
  75. request += arr.map(i => ("(" +columns.map(i => "?").join(",")+")")).join(',');
  76. for (let i of arr)
  77. {
  78. for (let [key, val] of Object.entries(i.describe()))
  79. args.push(val);
  80. }
  81. await this.runSql(request, args, false);
  82. }
  83. DatabaseHelper.prototype.upsertOne = async function(obj) {
  84. let columns = [];
  85. let columnsVal = [];
  86. let args = [];
  87. let i =0;
  88. for (let [key, val] of Object.entries(obj.describe())) {
  89. columns.push("`"+key+"`");
  90. columnsVal.push("`"+key+"`=:"+(i++));
  91. args.push(val);
  92. }
  93. let request = "insert into `" +obj.getTableName()
  94. +"` (" +columns.join(", ")
  95. +") VALUES (" +columns.map((_, i) => `:${i}`).join(",")+")"
  96. +" ON CONFLICT DO UPDATE SET " +columnsVal.join(", ");
  97. await this.runSql(request, args, false);
  98. }
  99. DatabaseHelper.prototype.buildWhere = function(whereObj, columns, args) {
  100. columns = columns || [];
  101. args = args || [];
  102. if (!whereObj) {
  103. columns.push("1=1");
  104. } else {
  105. for (let [key, val] of Object.entries(whereObj)) {
  106. if (val === null) {
  107. columns.push("`"+key+"` is null");
  108. } else if (Array.isArray(val)) {
  109. columns.push("`"+key+"` in (" +val.map(i => "?").join(",") + ")");
  110. for (let i of val) args.push(i);
  111. } else {
  112. columns.push("`"+key+"`=?");
  113. args.push(val);
  114. }
  115. }
  116. }
  117. return {
  118. columns: columns,
  119. args: args
  120. };
  121. }
  122. DatabaseHelper.prototype.maxVersion = async function(instanceName) {
  123. let queryParts = [];
  124. for (let i of this.models)
  125. queryParts.push("select max(" +i.prototype.versionColumn.call(null) +") as v from " +i.prototype.getTableName.call(null) +" where instance=:0");
  126. let version = await this.runSql("select max(v) as v from (" +queryParts.join(" union ") +")", [ instanceName ], true);
  127. return (version?.[0]?.v) || 0;
  128. }
  129. DatabaseHelper.prototype.versionFetch = async function(objectPrototype, minVersion) {
  130. let query = "select * from `" +objectPrototype.prototype.getTableName.call(null) +"` where `" +objectPrototype.prototype.versionColumn.call(null)+"` > ? and instance=?";
  131. let result = [];
  132. for (let i of await this.runSql(query, [minVersion, CONFIG.instanceHostname], true))
  133. {
  134. let resultObj = new objectPrototype();
  135. resultObj.fromDb(i);
  136. result.push(resultObj);
  137. }
  138. return result;
  139. }
  140. DatabaseHelper.prototype.fetch = async function(objectPrototype, where, orderBy) {
  141. let result = await this.fetchRaw("*", objectPrototype.prototype.getTableName.call(null), where, orderBy);
  142. let resultArr = [];
  143. for (let i of result)
  144. {
  145. let resultObj = new objectPrototype();
  146. resultObj.fromDb(i);
  147. resultArr.push(resultObj);
  148. }
  149. return resultArr;
  150. }
  151. DatabaseHelper.prototype.fetchRaw = async function(columns, tableName, where, orderBy) {
  152. let whereArgs = this.buildWhere(where);
  153. let query = "select " + (Array.isArray(columns) ? columns.join(","): columns) +" from `" +tableName +"` where " +whereArgs.columns.join(" and ");
  154. if (orderBy)
  155. query += " ORDER BY " +Object.keys(orderBy || {}).map(i => "`"+i+"` " +(orderBy[i] === 'DESC' ? "DESC":"ASC")).join(",");
  156. return await this.runSql(query, whereArgs.args, true);
  157. }
  158. DatabaseHelper.prototype.findOne = async function(objectPrototype, where) {
  159. let whereArgs = this.buildWhere(where);
  160. let result = await this.runSql("select * from `" +objectPrototype.prototype.getTableName.call(null) +"` where " +whereArgs.columns.join(" and ") +" LIMIT 1", whereArgs.args, true);
  161. if (result && result.length)
  162. {
  163. let resultObj = new objectPrototype();
  164. resultObj.fromDb(result[0]);
  165. return resultObj;
  166. }
  167. return null;
  168. }
  169. DatabaseHelper.prototype.update = async function(where, object) {
  170. if (object.lastUpdated)
  171. object.lastUpdated = new Date();
  172. await this.rawUpdate(object, where, object.describe());
  173. }
  174. DatabaseHelper.prototype.rawUpdate = async function(model, where, values) {
  175. let columns = [];
  176. let args = [];
  177. for (let [key, val] of Object.entries(values)) {
  178. columns.push("`"+key+"`=?");
  179. args.push(val);
  180. }
  181. let whereArgs = this.buildWhere(where, [], args);
  182. let query = "update `" +(model.prototype?.getTableName ? model.prototype.getTableName() : model.getTableName())
  183. +"` set " +columns.join(", ")
  184. +" WHERE " +whereArgs.columns.join(" and ");
  185. await this.runSql(query, args, false);
  186. }
  187. module.exports.DatabaseHelper = new DatabaseHelper();