query.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. "use strict";
  2. const _ = require("lodash");
  3. const Utils = require("../../utils");
  4. const AbstractQuery = require("../abstract/query");
  5. const QueryTypes = require("../../query-types");
  6. const sequelizeErrors = require("../../errors");
  7. const parserStore = require("../parserStore")("sqlite");
  8. const { logger } = require("../../utils/logger");
  9. const debug = logger.debugContext("sql:sqlite");
  10. function stringifyIfBigint(value) {
  11. if (typeof value === "bigint") {
  12. return value.toString();
  13. }
  14. return value;
  15. }
  16. class Query extends AbstractQuery {
  17. getInsertIdField() {
  18. return "lastID";
  19. }
  20. static formatBindParameters(sql, values, dialect) {
  21. let bindParam;
  22. if (Array.isArray(values)) {
  23. bindParam = {};
  24. values.forEach((v, i) => {
  25. bindParam[`$${i + 1}`] = v;
  26. });
  27. sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
  28. } else {
  29. bindParam = {};
  30. if (typeof values === "object") {
  31. for (const k of Object.keys(values)) {
  32. bindParam[`$${k}`] = values[k];
  33. }
  34. }
  35. sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
  36. }
  37. return [sql, bindParam];
  38. }
  39. _collectModels(include, prefix) {
  40. const ret = {};
  41. if (include) {
  42. for (const _include of include) {
  43. let key;
  44. if (!prefix) {
  45. key = _include.as;
  46. } else {
  47. key = `${prefix}.${_include.as}`;
  48. }
  49. ret[key] = _include.model;
  50. if (_include.include) {
  51. _.merge(ret, this._collectModels(_include.include, key));
  52. }
  53. }
  54. }
  55. return ret;
  56. }
  57. _handleQueryResponse(metaData, columnTypes, err, results, errStack) {
  58. if (err) {
  59. err.sql = this.sql;
  60. throw this.formatError(err, errStack);
  61. }
  62. let result = this.instance;
  63. if (this.isInsertQuery(results, metaData) || this.isUpsertQuery()) {
  64. this.handleInsertQuery(results, metaData);
  65. if (!this.instance) {
  66. if (metaData.constructor.name === "Statement" && this.model && this.model.autoIncrementAttribute && this.model.autoIncrementAttribute === this.model.primaryKeyAttribute && this.model.rawAttributes[this.model.primaryKeyAttribute]) {
  67. const startId = metaData[this.getInsertIdField()] - metaData.changes + 1;
  68. result = [];
  69. for (let i = startId; i < startId + metaData.changes; i++) {
  70. result.push({ [this.model.rawAttributes[this.model.primaryKeyAttribute].field]: i });
  71. }
  72. } else {
  73. result = metaData[this.getInsertIdField()];
  74. }
  75. }
  76. }
  77. if (this.isShowTablesQuery()) {
  78. return results.map((row) => row.name);
  79. }
  80. if (this.isShowConstraintsQuery()) {
  81. result = results;
  82. if (results && results[0] && results[0].sql) {
  83. result = this.parseConstraintsFromSql(results[0].sql);
  84. }
  85. return result;
  86. }
  87. if (this.isSelectQuery()) {
  88. if (this.options.raw) {
  89. return this.handleSelectQuery(results);
  90. }
  91. const prefixes = this._collectModels(this.options.include);
  92. results = results.map((result2) => {
  93. return _.mapValues(result2, (value, name) => {
  94. let model;
  95. if (name.includes(".")) {
  96. const lastind = name.lastIndexOf(".");
  97. model = prefixes[name.substr(0, lastind)];
  98. name = name.substr(lastind + 1);
  99. } else {
  100. model = this.options.model;
  101. }
  102. const tableName = model.getTableName().toString().replace(/`/g, "");
  103. const tableTypes = columnTypes[tableName] || {};
  104. if (tableTypes && !(name in tableTypes)) {
  105. _.forOwn(model.rawAttributes, (attribute, key) => {
  106. if (name === key && attribute.field) {
  107. name = attribute.field;
  108. return false;
  109. }
  110. });
  111. }
  112. return Object.prototype.hasOwnProperty.call(tableTypes, name) ? this.applyParsers(tableTypes[name], value) : value;
  113. });
  114. });
  115. return this.handleSelectQuery(results);
  116. }
  117. if (this.isShowOrDescribeQuery()) {
  118. return results;
  119. }
  120. if (this.sql.includes("PRAGMA INDEX_LIST")) {
  121. return this.handleShowIndexesQuery(results);
  122. }
  123. if (this.sql.includes("PRAGMA INDEX_INFO")) {
  124. return results;
  125. }
  126. if (this.sql.includes("PRAGMA TABLE_INFO")) {
  127. result = {};
  128. let defaultValue;
  129. for (const _result of results) {
  130. if (_result.dflt_value === null) {
  131. defaultValue = void 0;
  132. } else if (_result.dflt_value === "NULL") {
  133. defaultValue = null;
  134. } else {
  135. defaultValue = _result.dflt_value;
  136. }
  137. result[_result.name] = {
  138. type: _result.type,
  139. allowNull: _result.notnull === 0,
  140. defaultValue,
  141. primaryKey: _result.pk !== 0
  142. };
  143. if (result[_result.name].type === "TINYINT(1)") {
  144. result[_result.name].defaultValue = { "0": false, "1": true }[result[_result.name].defaultValue];
  145. }
  146. if (typeof result[_result.name].defaultValue === "string") {
  147. result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, "");
  148. }
  149. }
  150. return result;
  151. }
  152. if (this.sql.includes("PRAGMA foreign_keys;")) {
  153. return results[0];
  154. }
  155. if (this.sql.includes("PRAGMA foreign_keys")) {
  156. return results;
  157. }
  158. if (this.sql.includes("PRAGMA foreign_key_list")) {
  159. return results;
  160. }
  161. if ([QueryTypes.BULKUPDATE, QueryTypes.BULKDELETE].includes(this.options.type)) {
  162. return metaData.changes;
  163. }
  164. if (this.options.type === QueryTypes.VERSION) {
  165. return results[0].version;
  166. }
  167. if (this.options.type === QueryTypes.RAW) {
  168. return [results, metaData];
  169. }
  170. if (this.isUpsertQuery()) {
  171. return [result, null];
  172. }
  173. if (this.isUpdateQuery() || this.isInsertQuery()) {
  174. return [result, metaData.changes];
  175. }
  176. return result;
  177. }
  178. async run(sql, parameters) {
  179. const conn = this.connection;
  180. this.sql = sql;
  181. const method = this.getDatabaseMethod();
  182. const complete = this._logQuery(sql, debug, parameters);
  183. return new Promise((resolve, reject) => conn.serialize(async () => {
  184. const columnTypes = {};
  185. const errForStack = new Error();
  186. const executeSql = () => {
  187. if (sql.startsWith("-- ")) {
  188. return resolve();
  189. }
  190. const query = this;
  191. function afterExecute(executionError, results) {
  192. try {
  193. complete();
  194. resolve(query._handleQueryResponse(this, columnTypes, executionError, results, errForStack.stack));
  195. return;
  196. } catch (error) {
  197. reject(error);
  198. }
  199. }
  200. if (!parameters)
  201. parameters = [];
  202. if (_.isPlainObject(parameters)) {
  203. const newParameters = Object.create(null);
  204. for (const key of Object.keys(parameters)) {
  205. newParameters[`${key}`] = stringifyIfBigint(parameters[key]);
  206. }
  207. parameters = newParameters;
  208. } else {
  209. parameters = parameters.map(stringifyIfBigint);
  210. }
  211. conn[method](sql, parameters, afterExecute);
  212. return null;
  213. };
  214. if (this.getDatabaseMethod() === "all") {
  215. let tableNames = [];
  216. if (this.options && this.options.tableNames) {
  217. tableNames = this.options.tableNames;
  218. } else if (/FROM `(.*?)`/i.exec(this.sql)) {
  219. tableNames.push(/FROM `(.*?)`/i.exec(this.sql)[1]);
  220. }
  221. tableNames = tableNames.filter((tableName) => !(tableName in columnTypes) && tableName !== "sqlite_master");
  222. if (!tableNames.length) {
  223. return executeSql();
  224. }
  225. await Promise.all(tableNames.map((tableName) => new Promise((resolve2) => {
  226. tableName = tableName.replace(/`/g, "");
  227. columnTypes[tableName] = {};
  228. conn.all(`PRAGMA table_info(\`${tableName}\`)`, (err, results) => {
  229. if (!err) {
  230. for (const result of results) {
  231. columnTypes[tableName][result.name] = result.type;
  232. }
  233. }
  234. resolve2();
  235. });
  236. })));
  237. }
  238. return executeSql();
  239. }));
  240. }
  241. parseConstraintsFromSql(sql) {
  242. let constraints = sql.split("CONSTRAINT ");
  243. let referenceTableName, referenceTableKeys, updateAction, deleteAction;
  244. constraints.splice(0, 1);
  245. constraints = constraints.map((constraintSql) => {
  246. if (constraintSql.includes("REFERENCES")) {
  247. updateAction = constraintSql.match(/ON UPDATE (CASCADE|SET NULL|RESTRICT|NO ACTION|SET DEFAULT){1}/);
  248. deleteAction = constraintSql.match(/ON DELETE (CASCADE|SET NULL|RESTRICT|NO ACTION|SET DEFAULT){1}/);
  249. if (updateAction) {
  250. updateAction = updateAction[1];
  251. }
  252. if (deleteAction) {
  253. deleteAction = deleteAction[1];
  254. }
  255. const referencesRegex = /REFERENCES.+\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/;
  256. const referenceConditions = constraintSql.match(referencesRegex)[0].split(" ");
  257. referenceTableName = Utils.removeTicks(referenceConditions[1]);
  258. let columnNames = referenceConditions[2];
  259. columnNames = columnNames.replace(/\(|\)/g, "").split(", ");
  260. referenceTableKeys = columnNames.map((column) => Utils.removeTicks(column));
  261. }
  262. const constraintCondition = constraintSql.match(/\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/)[0];
  263. constraintSql = constraintSql.replace(/\(.+\)/, "");
  264. const constraint = constraintSql.split(" ");
  265. if (["PRIMARY", "FOREIGN"].includes(constraint[1])) {
  266. constraint[1] += " KEY";
  267. }
  268. return {
  269. constraintName: Utils.removeTicks(constraint[0]),
  270. constraintType: constraint[1],
  271. updateAction,
  272. deleteAction,
  273. sql: sql.replace(/"/g, "`"),
  274. constraintCondition,
  275. referenceTableName,
  276. referenceTableKeys
  277. };
  278. });
  279. return constraints;
  280. }
  281. applyParsers(type, value) {
  282. if (type.includes("(")) {
  283. type = type.substr(0, type.indexOf("("));
  284. }
  285. type = type.replace("UNSIGNED", "").replace("ZEROFILL", "");
  286. type = type.trim().toUpperCase();
  287. const parse = parserStore.get(type);
  288. if (value !== null && parse) {
  289. return parse(value, { timezone: this.sequelize.options.timezone });
  290. }
  291. return value;
  292. }
  293. formatError(err, errStack) {
  294. switch (err.code) {
  295. case "SQLITE_CONSTRAINT_UNIQUE":
  296. case "SQLITE_CONSTRAINT_PRIMARYKEY":
  297. case "SQLITE_CONSTRAINT_TRIGGER":
  298. case "SQLITE_CONSTRAINT_FOREIGNKEY":
  299. case "SQLITE_CONSTRAINT": {
  300. if (err.message.includes("FOREIGN KEY constraint failed")) {
  301. return new sequelizeErrors.ForeignKeyConstraintError({
  302. parent: err,
  303. stack: errStack
  304. });
  305. }
  306. let fields = [];
  307. let match = err.message.match(/columns (.*?) are/);
  308. if (match !== null && match.length >= 2) {
  309. fields = match[1].split(", ");
  310. } else {
  311. match = err.message.match(/UNIQUE constraint failed: (.*)/);
  312. if (match !== null && match.length >= 2) {
  313. fields = match[1].split(", ").map((columnWithTable) => columnWithTable.split(".")[1]);
  314. }
  315. }
  316. const errors = [];
  317. let message = "Validation error";
  318. for (const field of fields) {
  319. errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, this.instance && this.instance[field], this.instance, "not_unique"));
  320. }
  321. if (this.model) {
  322. _.forOwn(this.model.uniqueKeys, (constraint) => {
  323. if (_.isEqual(constraint.fields, fields) && !!constraint.msg) {
  324. message = constraint.msg;
  325. return false;
  326. }
  327. });
  328. }
  329. return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack });
  330. }
  331. case "SQLITE_BUSY":
  332. return new sequelizeErrors.TimeoutError(err, { stack: errStack });
  333. default:
  334. return new sequelizeErrors.DatabaseError(err, { stack: errStack });
  335. }
  336. }
  337. async handleShowIndexesQuery(data) {
  338. return Promise.all(data.reverse().map(async (item) => {
  339. item.fields = [];
  340. item.primary = false;
  341. item.unique = !!item.unique;
  342. item.constraintName = item.name;
  343. const columns = await this.run(`PRAGMA INDEX_INFO(\`${item.name}\`)`);
  344. for (const column of columns) {
  345. item.fields[column.seqno] = {
  346. attribute: column.name,
  347. length: void 0,
  348. order: void 0
  349. };
  350. }
  351. return item;
  352. }));
  353. }
  354. getDatabaseMethod() {
  355. if (this.isInsertQuery() || this.isUpdateQuery() || this.isUpsertQuery() || this.isBulkUpdateQuery() || this.sql.toLowerCase().includes("CREATE TEMPORARY TABLE".toLowerCase()) || this.options.type === QueryTypes.BULKDELETE) {
  356. return "run";
  357. }
  358. return "all";
  359. }
  360. }
  361. module.exports = Query;
  362. module.exports.Query = Query;
  363. module.exports.default = Query;
  364. //# sourceMappingURL=query.js.map