query.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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. class Query extends AbstractQuery {
  11. getInsertIdField() {
  12. return 'lastID';
  13. }
  14. /**
  15. * rewrite query with parameters.
  16. *
  17. * @param {string} sql
  18. * @param {Array|object} values
  19. * @param {string} dialect
  20. * @private
  21. */
  22. static formatBindParameters(sql, values, dialect) {
  23. let bindParam;
  24. if (Array.isArray(values)) {
  25. bindParam = {};
  26. values.forEach((v, i) => {
  27. bindParam[`$${i + 1}`] = v;
  28. });
  29. sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
  30. } else {
  31. bindParam = {};
  32. if (typeof values === 'object') {
  33. for (const k of Object.keys(values)) {
  34. bindParam[`$${k}`] = values[k];
  35. }
  36. }
  37. sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
  38. }
  39. return [sql, bindParam];
  40. }
  41. _collectModels(include, prefix) {
  42. const ret = {};
  43. if (include) {
  44. for (const _include of include) {
  45. let key;
  46. if (!prefix) {
  47. key = _include.as;
  48. } else {
  49. key = `${prefix}.${_include.as}`;
  50. }
  51. ret[key] = _include.model;
  52. if (_include.include) {
  53. _.merge(ret, this._collectModels(_include.include, key));
  54. }
  55. }
  56. }
  57. return ret;
  58. }
  59. _handleQueryResponse(metaData, columnTypes, err, results) {
  60. if (err) {
  61. err.sql = this.sql;
  62. throw this.formatError(err);
  63. }
  64. let result = this.instance;
  65. // add the inserted row id to the instance
  66. if (this.isInsertQuery(results, metaData)) {
  67. this.handleInsertQuery(results, metaData);
  68. if (!this.instance) {
  69. // handle bulkCreate AI primary key
  70. if (
  71. metaData.constructor.name === 'Statement'
  72. && this.model
  73. && this.model.autoIncrementAttribute
  74. && this.model.autoIncrementAttribute === this.model.primaryKeyAttribute
  75. && this.model.rawAttributes[this.model.primaryKeyAttribute]
  76. ) {
  77. const startId = metaData[this.getInsertIdField()] - metaData.changes + 1;
  78. result = [];
  79. for (let i = startId; i < startId + metaData.changes; i++) {
  80. result.push({ [this.model.rawAttributes[this.model.primaryKeyAttribute].field]: i });
  81. }
  82. } else {
  83. result = metaData[this.getInsertIdField()];
  84. }
  85. }
  86. }
  87. if (this.isShowTablesQuery()) {
  88. return results.map(row => row.name);
  89. }
  90. if (this.isShowConstraintsQuery()) {
  91. result = results;
  92. if (results && results[0] && results[0].sql) {
  93. result = this.parseConstraintsFromSql(results[0].sql);
  94. }
  95. return result;
  96. }
  97. if (this.isSelectQuery()) {
  98. if (this.options.raw) {
  99. return this.handleSelectQuery(results);
  100. }
  101. // This is a map of prefix strings to models, e.g. user.projects -> Project model
  102. const prefixes = this._collectModels(this.options.include);
  103. results = results.map(result => {
  104. return _.mapValues(result, (value, name) => {
  105. let model;
  106. if (name.includes('.')) {
  107. const lastind = name.lastIndexOf('.');
  108. model = prefixes[name.substr(0, lastind)];
  109. name = name.substr(lastind + 1);
  110. } else {
  111. model = this.options.model;
  112. }
  113. const tableName = model.getTableName().toString().replace(/`/g, '');
  114. const tableTypes = columnTypes[tableName] || {};
  115. if (tableTypes && !(name in tableTypes)) {
  116. // The column is aliased
  117. _.forOwn(model.rawAttributes, (attribute, key) => {
  118. if (name === key && attribute.field) {
  119. name = attribute.field;
  120. return false;
  121. }
  122. });
  123. }
  124. return Object.prototype.hasOwnProperty.call(tableTypes, name)
  125. ? this.applyParsers(tableTypes[name], value)
  126. : value;
  127. });
  128. });
  129. return this.handleSelectQuery(results);
  130. }
  131. if (this.isShowOrDescribeQuery()) {
  132. return results;
  133. }
  134. if (this.sql.includes('PRAGMA INDEX_LIST')) {
  135. return this.handleShowIndexesQuery(results);
  136. }
  137. if (this.sql.includes('PRAGMA INDEX_INFO')) {
  138. return results;
  139. }
  140. if (this.sql.includes('PRAGMA TABLE_INFO')) {
  141. // this is the sqlite way of getting the metadata of a table
  142. result = {};
  143. let defaultValue;
  144. for (const _result of results) {
  145. if (_result.dflt_value === null) {
  146. // Column schema omits any "DEFAULT ..."
  147. defaultValue = undefined;
  148. } else if (_result.dflt_value === 'NULL') {
  149. // Column schema is a "DEFAULT NULL"
  150. defaultValue = null;
  151. } else {
  152. defaultValue = _result.dflt_value;
  153. }
  154. result[_result.name] = {
  155. type: _result.type,
  156. allowNull: _result.notnull === 0,
  157. defaultValue,
  158. primaryKey: _result.pk !== 0
  159. };
  160. if (result[_result.name].type === 'TINYINT(1)') {
  161. result[_result.name].defaultValue = { '0': false, '1': true }[result[_result.name].defaultValue];
  162. }
  163. if (typeof result[_result.name].defaultValue === 'string') {
  164. result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, '');
  165. }
  166. }
  167. return result;
  168. }
  169. if (this.sql.includes('PRAGMA foreign_keys;')) {
  170. return results[0];
  171. }
  172. if (this.sql.includes('PRAGMA foreign_keys')) {
  173. return results;
  174. }
  175. if (this.sql.includes('PRAGMA foreign_key_list')) {
  176. return results;
  177. }
  178. if ([QueryTypes.BULKUPDATE, QueryTypes.BULKDELETE].includes(this.options.type)) {
  179. return metaData.changes;
  180. }
  181. if (this.options.type === QueryTypes.VERSION) {
  182. return results[0].version;
  183. }
  184. if (this.options.type === QueryTypes.RAW) {
  185. return [results, metaData];
  186. }
  187. if (this.isUpsertQuery()) {
  188. return [result, null];
  189. }
  190. if (this.isUpdateQuery() || this.isInsertQuery()) {
  191. return [result, metaData.changes];
  192. }
  193. return result;
  194. }
  195. async run(sql, parameters) {
  196. const conn = this.connection;
  197. this.sql = sql;
  198. const method = this.getDatabaseMethod();
  199. let complete;
  200. if (method === 'exec') {
  201. // exec does not support bind parameter
  202. sql = AbstractQuery.formatBindParameters(sql, this.options.bind, this.options.dialect || 'sqlite', { skipUnescape: true })[0];
  203. this.sql = sql;
  204. complete = this._logQuery(sql, debug);
  205. } else {
  206. complete = this._logQuery(sql, debug, parameters);
  207. }
  208. return new Promise((resolve, reject) => conn.serialize(async () => {
  209. const columnTypes = {};
  210. const executeSql = () => {
  211. if (sql.startsWith('-- ')) {
  212. return resolve();
  213. }
  214. const query = this;
  215. // cannot use arrow function here because the function is bound to the statement
  216. function afterExecute(executionError, results) {
  217. try {
  218. complete();
  219. // `this` is passed from sqlite, we have no control over this.
  220. // eslint-disable-next-line no-invalid-this
  221. resolve(query._handleQueryResponse(this, columnTypes, executionError, results));
  222. return;
  223. } catch (error) {
  224. reject(error);
  225. }
  226. }
  227. if (method === 'exec') {
  228. // exec does not support bind parameter
  229. conn[method](sql, afterExecute);
  230. } else {
  231. if (!parameters) parameters = [];
  232. conn[method](sql, parameters, afterExecute);
  233. }
  234. return null;
  235. };
  236. if (this.getDatabaseMethod() === 'all') {
  237. let tableNames = [];
  238. if (this.options && this.options.tableNames) {
  239. tableNames = this.options.tableNames;
  240. } else if (/FROM `(.*?)`/i.exec(this.sql)) {
  241. tableNames.push(/FROM `(.*?)`/i.exec(this.sql)[1]);
  242. }
  243. // If we already have the metadata for the table, there's no need to ask for it again
  244. tableNames = tableNames.filter(tableName => !(tableName in columnTypes) && tableName !== 'sqlite_master');
  245. if (!tableNames.length) {
  246. return executeSql();
  247. }
  248. await Promise.all(tableNames.map(tableName =>
  249. new Promise(resolve => {
  250. tableName = tableName.replace(/`/g, '');
  251. columnTypes[tableName] = {};
  252. conn.all(`PRAGMA table_info(\`${tableName}\`)`, (err, results) => {
  253. if (!err) {
  254. for (const result of results) {
  255. columnTypes[tableName][result.name] = result.type;
  256. }
  257. }
  258. resolve();
  259. });
  260. })));
  261. }
  262. return executeSql();
  263. }));
  264. }
  265. parseConstraintsFromSql(sql) {
  266. let constraints = sql.split('CONSTRAINT ');
  267. let referenceTableName, referenceTableKeys, updateAction, deleteAction;
  268. constraints.splice(0, 1);
  269. constraints = constraints.map(constraintSql => {
  270. //Parse foreign key snippets
  271. if (constraintSql.includes('REFERENCES')) {
  272. //Parse out the constraint condition form sql string
  273. updateAction = constraintSql.match(/ON UPDATE (CASCADE|SET NULL|RESTRICT|NO ACTION|SET DEFAULT){1}/);
  274. deleteAction = constraintSql.match(/ON DELETE (CASCADE|SET NULL|RESTRICT|NO ACTION|SET DEFAULT){1}/);
  275. if (updateAction) {
  276. updateAction = updateAction[1];
  277. }
  278. if (deleteAction) {
  279. deleteAction = deleteAction[1];
  280. }
  281. const referencesRegex = /REFERENCES.+\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/;
  282. const referenceConditions = constraintSql.match(referencesRegex)[0].split(' ');
  283. referenceTableName = Utils.removeTicks(referenceConditions[1]);
  284. let columnNames = referenceConditions[2];
  285. columnNames = columnNames.replace(/\(|\)/g, '').split(', ');
  286. referenceTableKeys = columnNames.map(column => Utils.removeTicks(column));
  287. }
  288. const constraintCondition = constraintSql.match(/\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/)[0];
  289. constraintSql = constraintSql.replace(/\(.+\)/, '');
  290. const constraint = constraintSql.split(' ');
  291. if (constraint[1] === 'PRIMARY' || constraint[1] === 'FOREIGN') {
  292. constraint[1] += ' KEY';
  293. }
  294. return {
  295. constraintName: Utils.removeTicks(constraint[0]),
  296. constraintType: constraint[1],
  297. updateAction,
  298. deleteAction,
  299. sql: sql.replace(/"/g, '`'), //Sqlite returns double quotes for table name
  300. constraintCondition,
  301. referenceTableName,
  302. referenceTableKeys
  303. };
  304. });
  305. return constraints;
  306. }
  307. applyParsers(type, value) {
  308. if (type.includes('(')) {
  309. // Remove the length part
  310. type = type.substr(0, type.indexOf('('));
  311. }
  312. type = type.replace('UNSIGNED', '').replace('ZEROFILL', '');
  313. type = type.trim().toUpperCase();
  314. const parse = parserStore.get(type);
  315. if (value !== null && parse) {
  316. return parse(value, { timezone: this.sequelize.options.timezone });
  317. }
  318. return value;
  319. }
  320. formatError(err) {
  321. switch (err.code) {
  322. case 'SQLITE_CONSTRAINT': {
  323. if (err.message.includes('FOREIGN KEY constraint failed')) {
  324. return new sequelizeErrors.ForeignKeyConstraintError({
  325. parent: err
  326. });
  327. }
  328. let fields = [];
  329. // Sqlite pre 2.2 behavior - Error: SQLITE_CONSTRAINT: columns x, y are not unique
  330. let match = err.message.match(/columns (.*?) are/);
  331. if (match !== null && match.length >= 2) {
  332. fields = match[1].split(', ');
  333. } else {
  334. // Sqlite post 2.2 behavior - Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: table.x, table.y
  335. match = err.message.match(/UNIQUE constraint failed: (.*)/);
  336. if (match !== null && match.length >= 2) {
  337. fields = match[1].split(', ').map(columnWithTable => columnWithTable.split('.')[1]);
  338. }
  339. }
  340. const errors = [];
  341. let message = 'Validation error';
  342. for (const field of fields) {
  343. errors.push(new sequelizeErrors.ValidationErrorItem(
  344. this.getUniqueConstraintErrorMessage(field),
  345. 'unique violation', // sequelizeErrors.ValidationErrorItem.Origins.DB,
  346. field,
  347. this.instance && this.instance[field],
  348. this.instance,
  349. 'not_unique'
  350. ));
  351. }
  352. if (this.model) {
  353. _.forOwn(this.model.uniqueKeys, constraint => {
  354. if (_.isEqual(constraint.fields, fields) && !!constraint.msg) {
  355. message = constraint.msg;
  356. return false;
  357. }
  358. });
  359. }
  360. return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields });
  361. }
  362. case 'SQLITE_BUSY':
  363. return new sequelizeErrors.TimeoutError(err);
  364. default:
  365. return new sequelizeErrors.DatabaseError(err);
  366. }
  367. }
  368. async handleShowIndexesQuery(data) {
  369. // Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that!
  370. return Promise.all(data.reverse().map(async item => {
  371. item.fields = [];
  372. item.primary = false;
  373. item.unique = !!item.unique;
  374. item.constraintName = item.name;
  375. const columns = await this.run(`PRAGMA INDEX_INFO(\`${item.name}\`)`);
  376. for (const column of columns) {
  377. item.fields[column.seqno] = {
  378. attribute: column.name,
  379. length: undefined,
  380. order: undefined
  381. };
  382. }
  383. return item;
  384. }));
  385. }
  386. getDatabaseMethod() {
  387. if (this.isUpsertQuery()) {
  388. return 'exec'; // Needed to run multiple queries in one
  389. }
  390. if (this.isInsertQuery() || this.isUpdateQuery() || this.isBulkUpdateQuery() || this.sql.toLowerCase().includes('CREATE TEMPORARY TABLE'.toLowerCase()) || this.options.type === QueryTypes.BULKDELETE) {
  391. return 'run';
  392. }
  393. return 'all';
  394. }
  395. }
  396. module.exports = Query;
  397. module.exports.Query = Query;
  398. module.exports.default = Query;