query.js 14 KB

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