query.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. "use strict";
  2. const util = require("util");
  3. const AbstractQuery = require("../abstract/query");
  4. const sequelizeErrors = require("../../errors");
  5. const parserStore = require("../parserStore")("db2");
  6. const _ = require("lodash");
  7. const { logger } = require("../../utils/logger");
  8. const moment = require("moment");
  9. const debug = logger.debugContext("sql:db2");
  10. class Query extends AbstractQuery {
  11. getInsertIdField() {
  12. return "id";
  13. }
  14. getSQLTypeFromJsType(value) {
  15. if (Buffer.isBuffer(value)) {
  16. return { ParamType: "INPUT", DataType: "BLOB", Data: value };
  17. }
  18. if (typeof value === "bigint") {
  19. return value.toString();
  20. }
  21. return value;
  22. }
  23. async _run(connection, sql, parameters) {
  24. this.sql = sql;
  25. const benchmark = this.sequelize.options.benchmark || this.options.benchmark;
  26. let queryBegin;
  27. if (benchmark) {
  28. queryBegin = Date.now();
  29. } else {
  30. this.sequelize.log(`Executing (${this.connection.uuid || "default"}): ${this.sql}`, this.options);
  31. }
  32. const errStack = new Error().stack;
  33. return new Promise((resolve, reject) => {
  34. if (_.startsWith(this.sql, "BEGIN TRANSACTION")) {
  35. connection.beginTransaction((err) => {
  36. if (err) {
  37. reject(this.formatError(err, errStack));
  38. } else {
  39. resolve(this.formatResults());
  40. }
  41. });
  42. } else if (_.startsWith(this.sql, "COMMIT TRANSACTION")) {
  43. connection.commitTransaction((err) => {
  44. if (err) {
  45. reject(this.formatError(err, errStack));
  46. } else {
  47. resolve(this.formatResults());
  48. }
  49. });
  50. } else if (_.startsWith(this.sql, "ROLLBACK TRANSACTION")) {
  51. connection.rollbackTransaction((err) => {
  52. if (err) {
  53. reject(this.formatError(err, errStack));
  54. } else {
  55. resolve(this.formatResults());
  56. }
  57. });
  58. } else if (_.startsWith(this.sql, "SAVE TRANSACTION")) {
  59. connection.commitTransaction((err) => {
  60. if (err) {
  61. reject(this.formatError(err, errStack));
  62. } else {
  63. connection.beginTransaction((err2) => {
  64. if (err2) {
  65. reject(this.formatError(err2, errStack));
  66. } else {
  67. resolve(this.formatResults());
  68. }
  69. });
  70. }
  71. }, this.options.transaction.name);
  72. } else {
  73. const params = [];
  74. if (parameters) {
  75. _.forOwn(parameters, (value, key) => {
  76. const param = this.getSQLTypeFromJsType(value, key);
  77. params.push(param);
  78. });
  79. }
  80. const SQL = this.sql.toUpperCase();
  81. let newSql = this.sql;
  82. if ((this.isSelectQuery() || _.startsWith(SQL, "SELECT ")) && SQL.indexOf(" FROM ", 8) === -1) {
  83. if (this.sql.charAt(this.sql.length - 1) === ";") {
  84. newSql = this.sql.slice(0, this.sql.length - 1);
  85. }
  86. newSql += " FROM SYSIBM.SYSDUMMY1;";
  87. }
  88. connection.prepare(newSql, (err, stmt) => {
  89. if (err) {
  90. reject(this.formatError(err, errStack));
  91. }
  92. stmt.execute(params, (err2, result, outparams) => {
  93. debug(`executed(${this.connection.uuid || "default"}):${newSql} ${parameters ? util.inspect(parameters, { compact: true, breakLength: Infinity }) : ""}`);
  94. if (benchmark) {
  95. this.sequelize.log(`Executed (${this.connection.uuid || "default"}): ${newSql} ${parameters ? util.inspect(parameters, { compact: true, breakLength: Infinity }) : ""}`, Date.now() - queryBegin, this.options);
  96. }
  97. if (err2 && err2.message) {
  98. err2 = this.filterSQLError(err2, this.sql, connection);
  99. if (err2 === null) {
  100. stmt.closeSync();
  101. resolve(this.formatResults([], 0));
  102. }
  103. }
  104. if (err2) {
  105. err2.sql = sql;
  106. stmt.closeSync();
  107. reject(this.formatError(err2, errStack, connection, parameters));
  108. } else {
  109. let data = [];
  110. let metadata = [];
  111. let affectedRows = 0;
  112. if (typeof result === "object") {
  113. if (_.startsWith(this.sql, "DELETE FROM ")) {
  114. affectedRows = result.getAffectedRowsSync();
  115. } else {
  116. data = result.fetchAllSync();
  117. metadata = result.getColumnMetadataSync();
  118. }
  119. result.closeSync();
  120. }
  121. stmt.closeSync();
  122. const datalen = data.length;
  123. if (datalen > 0) {
  124. const coltypes = {};
  125. for (let i = 0; i < metadata.length; i++) {
  126. coltypes[metadata[i].SQL_DESC_NAME] = metadata[i].SQL_DESC_TYPE_NAME;
  127. }
  128. for (let i = 0; i < datalen; i++) {
  129. for (const column in data[i]) {
  130. const parse = parserStore.get(coltypes[column]);
  131. const value = data[i][column];
  132. if (value !== null) {
  133. if (parse) {
  134. data[i][column] = parse(value);
  135. } else if (coltypes[column] === "TIMESTAMP") {
  136. data[i][column] = new Date(moment.utc(value));
  137. } else if (coltypes[column] === "BLOB") {
  138. data[i][column] = new Buffer.from(value);
  139. } else if (coltypes[column].indexOf("FOR BIT DATA") > 0) {
  140. data[i][column] = new Buffer.from(value, "hex");
  141. }
  142. }
  143. }
  144. }
  145. if (outparams && outparams.length) {
  146. data.unshift(outparams);
  147. }
  148. resolve(this.formatResults(data, datalen, metadata, connection));
  149. } else {
  150. resolve(this.formatResults(data, affectedRows));
  151. }
  152. }
  153. });
  154. });
  155. }
  156. });
  157. }
  158. async run(sql, parameters) {
  159. return await this._run(this.connection, sql, parameters);
  160. }
  161. static formatBindParameters(sql, values, dialect) {
  162. let bindParam = {};
  163. const replacementFunc = (match, key, values2) => {
  164. if (values2[key] !== void 0) {
  165. bindParam[key] = values2[key];
  166. return "?";
  167. }
  168. return void 0;
  169. };
  170. sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0];
  171. if (Array.isArray(values) && typeof values[0] === "object") {
  172. bindParam = values;
  173. }
  174. return [sql, bindParam];
  175. }
  176. filterSQLError(err, sql, connection) {
  177. if (err.message.search("SQL0204N") != -1 && _.startsWith(sql, "DROP ")) {
  178. err = null;
  179. } else if (err.message.search("SQL0443N") != -1) {
  180. if (this.isDropSchemaQuery()) {
  181. connection.querySync("DROP TABLE ERRORSCHEMA.ERRORTABLE;");
  182. connection.querySync(this.sql);
  183. }
  184. err = null;
  185. } else if (err.message.search("SQL0601N") != -1) {
  186. const match = err.message.match(/SQL0601N {2}The name of the object to be created is identical to the existing name "(.*)" of type "(.*)"./);
  187. if (match && match.length > 1 && match[2] === "TABLE") {
  188. let table;
  189. const mtarray = match[1].split(".");
  190. if (mtarray[1]) {
  191. table = `"${mtarray[0]}"."${mtarray[1]}"`;
  192. } else {
  193. table = `"${mtarray[0]}"`;
  194. }
  195. if (connection.dropTable !== false) {
  196. connection.querySync(`DROP TABLE ${table}`);
  197. err = connection.querySync(sql);
  198. } else {
  199. err = null;
  200. }
  201. } else {
  202. err = null;
  203. }
  204. } else if (err.message.search("SQL0911N") != -1) {
  205. if (err.message.search('Reason code "2"') != -1) {
  206. err = null;
  207. }
  208. } else if (err.message.search("SQL0605W") != -1) {
  209. err = null;
  210. } else if (err.message.search("SQL0668N") != -1 && _.startsWith(sql, "ALTER TABLE ")) {
  211. connection.querySync(`CALL SYSPROC.ADMIN_CMD('REORG TABLE ${sql.substring(12).split(" ")[0]}')`);
  212. err = connection.querySync(sql);
  213. }
  214. if (err && err.length === 0) {
  215. err = null;
  216. }
  217. return err;
  218. }
  219. formatResults(data, rowCount, metadata, conn) {
  220. let result = this.instance;
  221. if (this.isInsertQuery(data, metadata)) {
  222. this.handleInsertQuery(data, metadata);
  223. if (!this.instance) {
  224. if (this.options.plain) {
  225. const record = data[0];
  226. result = record[Object.keys(record)[0]];
  227. } else {
  228. result = data;
  229. }
  230. }
  231. }
  232. if (this.isShowTablesQuery()) {
  233. result = data;
  234. } else if (this.isDescribeQuery()) {
  235. result = {};
  236. for (const _result of data) {
  237. if (_result.Default) {
  238. _result.Default = _result.Default.replace("('", "").replace("')", "").replace(/'/g, "");
  239. }
  240. result[_result.Name] = {
  241. type: _result.Type.toUpperCase(),
  242. allowNull: _result.IsNull === "Y" ? true : false,
  243. defaultValue: _result.Default,
  244. primaryKey: _result.KeySeq > 0,
  245. autoIncrement: _result.IsIdentity === "Y" ? true : false,
  246. comment: _result.Comment
  247. };
  248. }
  249. } else if (this.isShowIndexesQuery()) {
  250. result = this.handleShowIndexesQuery(data);
  251. } else if (this.isSelectQuery()) {
  252. result = this.handleSelectQuery(data);
  253. } else if (this.isUpsertQuery()) {
  254. result = data;
  255. } else if (this.isDropSchemaQuery()) {
  256. result = data[0];
  257. if (conn) {
  258. const query = "DROP TABLE ERRORSCHEMA.ERRORTABLE";
  259. conn.querySync(query);
  260. }
  261. } else if (this.isCallQuery()) {
  262. result = data;
  263. } else if (this.isBulkUpdateQuery()) {
  264. result = data.length;
  265. } else if (this.isBulkDeleteQuery()) {
  266. result = rowCount;
  267. } else if (this.isVersionQuery()) {
  268. result = data[0].VERSION;
  269. } else if (this.isForeignKeysQuery()) {
  270. result = data;
  271. } else if (this.isInsertQuery() || this.isUpdateQuery()) {
  272. result = [result, rowCount];
  273. } else if (this.isShowConstraintsQuery()) {
  274. result = this.handleShowConstraintsQuery(data);
  275. } else if (this.isRawQuery()) {
  276. result = [data, metadata];
  277. } else {
  278. result = data;
  279. }
  280. return result;
  281. }
  282. handleShowTablesQuery(results) {
  283. return results.map((resultSet) => {
  284. return {
  285. tableName: resultSet.TABLE_NAME,
  286. schema: resultSet.TABLE_SCHEMA
  287. };
  288. });
  289. }
  290. handleShowConstraintsQuery(data) {
  291. return _.remove(data, (constraint) => {
  292. return !_.startsWith(constraint.constraintName, "SQL");
  293. });
  294. }
  295. formatError(err, errStack, conn, parameters) {
  296. let match;
  297. if (!(err && err.message)) {
  298. err["message"] = "No error message found.";
  299. }
  300. match = err.message.match(/SQL0803N {2}One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by "(\d)+" constrains table "(.*)\.(.*)" from having duplicate values for the index key./);
  301. if (match && match.length > 0) {
  302. let uniqueIndexName = "";
  303. let uniqueKey = "";
  304. const fields = {};
  305. let message = err.message;
  306. const query = `SELECT INDNAME FROM SYSCAT.INDEXES WHERE IID = ${match[1]} AND TABSCHEMA = '${match[2]}' AND TABNAME = '${match[3]}'`;
  307. if (!!conn && match.length > 3) {
  308. uniqueIndexName = conn.querySync(query);
  309. uniqueIndexName = uniqueIndexName[0]["INDNAME"];
  310. }
  311. if (this.model && !!uniqueIndexName) {
  312. uniqueKey = this.model.uniqueKeys[uniqueIndexName];
  313. }
  314. if (!uniqueKey && this.options.fields) {
  315. uniqueKey = this.options.fields[match[1] - 1];
  316. }
  317. if (uniqueKey) {
  318. if (this.options.where && this.options.where[uniqueKey.column] !== void 0) {
  319. fields[uniqueKey.column] = this.options.where[uniqueKey.column];
  320. } else if (this.options.instance && this.options.instance.dataValues && this.options.instance.dataValues[uniqueKey.column]) {
  321. fields[uniqueKey.column] = this.options.instance.dataValues[uniqueKey.column];
  322. } else if (parameters) {
  323. fields[uniqueKey.column] = parameters["0"];
  324. }
  325. }
  326. if (uniqueKey && !!uniqueKey.msg) {
  327. message = uniqueKey.msg;
  328. }
  329. const errors = [];
  330. _.forOwn(fields, (value, field) => {
  331. errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique"));
  332. });
  333. return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack });
  334. }
  335. match = err.message.match(/SQL0532N {2}A parent row cannot be deleted because the relationship "(.*)" restricts the deletion/) || err.message.match(/SQL0530N/) || err.message.match(/SQL0531N/);
  336. if (match && match.length > 0) {
  337. return new sequelizeErrors.ForeignKeyConstraintError({
  338. fields: null,
  339. index: match[1],
  340. parent: err,
  341. stack: errStack
  342. });
  343. }
  344. match = err.message.match(/SQL0204N {2}"(.*)" is an undefined name./);
  345. if (match && match.length > 1) {
  346. const constraint = match[1];
  347. let table = err.sql.match(/table "(.+?)"/i);
  348. table = table ? table[1] : void 0;
  349. return new sequelizeErrors.UnknownConstraintError({
  350. message: match[0],
  351. constraint,
  352. table,
  353. parent: err,
  354. stack: errStack
  355. });
  356. }
  357. return new sequelizeErrors.DatabaseError(err, { stack: errStack });
  358. }
  359. isDropSchemaQuery() {
  360. let result = false;
  361. if (_.startsWith(this.sql, "CALL SYSPROC.ADMIN_DROP_SCHEMA")) {
  362. result = true;
  363. }
  364. return result;
  365. }
  366. isShowOrDescribeQuery() {
  367. let result = false;
  368. result = result || this.sql.toLowerCase().startsWith("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'");
  369. result = result || this.sql.toLowerCase().startsWith("select tablename = t.name, name = ind.name,");
  370. result = result || this.sql.toLowerCase().startsWith("exec sys.sp_helpindex @objname");
  371. return result;
  372. }
  373. isShowIndexesQuery() {
  374. let result = false;
  375. result = result || this.sql.toLowerCase().startsWith("exec sys.sp_helpindex @objname");
  376. result = result || this.sql.startsWith('SELECT NAME AS "name", TBNAME AS "tableName", UNIQUERULE AS "keyType", COLNAMES, INDEXTYPE AS "type" FROM SYSIBM.SYSINDEXES');
  377. return result;
  378. }
  379. handleShowIndexesQuery(data) {
  380. let currItem;
  381. const result = [];
  382. data.forEach((item) => {
  383. if (!currItem || currItem.name !== item.Key_name) {
  384. currItem = {
  385. primary: item.keyType === "P",
  386. fields: [],
  387. name: item.name,
  388. tableName: item.tableName,
  389. unique: item.keyType === "U",
  390. type: item.type
  391. };
  392. _.forEach(item.COLNAMES.replace(/\+|-/g, (x) => {
  393. return ` ${x}`;
  394. }).split(" "), (column) => {
  395. let columnName = column.trim();
  396. if (columnName) {
  397. columnName = columnName.replace(/\+|-/, "");
  398. currItem.fields.push({
  399. attribute: columnName,
  400. length: void 0,
  401. order: column.indexOf("-") === -1 ? "ASC" : "DESC",
  402. collate: void 0
  403. });
  404. }
  405. });
  406. result.push(currItem);
  407. }
  408. });
  409. return result;
  410. }
  411. handleInsertQuery(results, metaData) {
  412. if (this.instance) {
  413. const autoIncrementAttribute = this.model.autoIncrementAttribute;
  414. let id = null;
  415. let autoIncrementAttributeAlias = null;
  416. if (Object.prototype.hasOwnProperty.call(this.model.rawAttributes, autoIncrementAttribute) && this.model.rawAttributes[autoIncrementAttribute].field !== void 0)
  417. autoIncrementAttributeAlias = this.model.rawAttributes[autoIncrementAttribute].field;
  418. id = id || results && results[0][this.getInsertIdField()];
  419. id = id || metaData && metaData[this.getInsertIdField()];
  420. id = id || results && results[0][autoIncrementAttribute];
  421. id = id || autoIncrementAttributeAlias && results && results[0][autoIncrementAttributeAlias];
  422. this.instance[autoIncrementAttribute] = id;
  423. }
  424. }
  425. }
  426. module.exports = Query;
  427. module.exports.Query = Query;
  428. module.exports.default = Query;
  429. //# sourceMappingURL=query.js.map