query.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. "use strict";
  2. const AbstractQuery = require("../abstract/query");
  3. const sequelizeErrors = require("../../errors");
  4. const parserStore = require("../parserStore")("mssql");
  5. const _ = require("lodash");
  6. const { logger } = require("../../utils/logger");
  7. const debug = logger.debugContext("sql:mssql");
  8. const minSafeIntegerAsBigInt = BigInt(Number.MIN_SAFE_INTEGER);
  9. const maxSafeIntegerAsBigInt = BigInt(Number.MAX_SAFE_INTEGER);
  10. function getScale(aNum) {
  11. if (!Number.isFinite(aNum))
  12. return 0;
  13. let e = 1;
  14. while (Math.round(aNum * e) / e !== aNum)
  15. e *= 10;
  16. return Math.log10(e);
  17. }
  18. class Query extends AbstractQuery {
  19. getInsertIdField() {
  20. return "id";
  21. }
  22. getSQLTypeFromJsType(value, TYPES) {
  23. const paramType = { type: TYPES.NVarChar, typeOptions: {}, value };
  24. if (typeof value === "number") {
  25. if (Number.isInteger(value)) {
  26. if (value >= -2147483648 && value <= 2147483647) {
  27. paramType.type = TYPES.Int;
  28. } else {
  29. paramType.type = TYPES.BigInt;
  30. }
  31. } else {
  32. paramType.type = TYPES.Numeric;
  33. paramType.typeOptions = { precision: 30, scale: getScale(value) };
  34. }
  35. } else if (typeof value === "bigint") {
  36. if (value < minSafeIntegerAsBigInt || value > maxSafeIntegerAsBigInt) {
  37. paramType.type = TYPES.VarChar;
  38. paramType.value = value.toString();
  39. } else {
  40. return this.getSQLTypeFromJsType(Number(value), TYPES);
  41. }
  42. } else if (typeof value === "boolean") {
  43. paramType.type = TYPES.Bit;
  44. }
  45. if (Buffer.isBuffer(value)) {
  46. paramType.type = TYPES.VarBinary;
  47. }
  48. return paramType;
  49. }
  50. async _run(connection, sql, parameters, errStack) {
  51. this.sql = sql;
  52. const { options } = this;
  53. const complete = this._logQuery(sql, debug, parameters);
  54. const query = new Promise((resolve, reject) => {
  55. if (sql.startsWith("BEGIN TRANSACTION")) {
  56. return connection.beginTransaction((error) => error ? reject(error) : resolve([]), options.transaction.name, connection.lib.ISOLATION_LEVEL[options.isolationLevel]);
  57. }
  58. if (sql.startsWith("COMMIT TRANSACTION")) {
  59. return connection.commitTransaction((error) => error ? reject(error) : resolve([]));
  60. }
  61. if (sql.startsWith("ROLLBACK TRANSACTION")) {
  62. return connection.rollbackTransaction((error) => error ? reject(error) : resolve([]), options.transaction.name);
  63. }
  64. if (sql.startsWith("SAVE TRANSACTION")) {
  65. return connection.saveTransaction((error) => error ? reject(error) : resolve([]), options.transaction.name);
  66. }
  67. const rows2 = [];
  68. const request = new connection.lib.Request(sql, (err, rowCount2) => err ? reject(err) : resolve([rows2, rowCount2]));
  69. if (parameters) {
  70. _.forOwn(parameters, (value, key) => {
  71. const paramType = this.getSQLTypeFromJsType(value, connection.lib.TYPES);
  72. request.addParameter(key, paramType.type, value, paramType.typeOptions);
  73. });
  74. }
  75. request.on("row", (columns) => {
  76. rows2.push(columns);
  77. });
  78. connection.execSql(request);
  79. });
  80. let rows, rowCount;
  81. try {
  82. [rows, rowCount] = await query;
  83. } catch (err) {
  84. err.sql = sql;
  85. err.parameters = parameters;
  86. throw this.formatError(err, errStack);
  87. }
  88. complete();
  89. if (Array.isArray(rows)) {
  90. rows = rows.map((columns) => {
  91. const row = {};
  92. for (const column of columns) {
  93. const typeid = column.metadata.type.id;
  94. const parse = parserStore.get(typeid);
  95. let value = column.value;
  96. if (value !== null & !!parse) {
  97. value = parse(value);
  98. }
  99. row[column.metadata.colName] = value;
  100. }
  101. return row;
  102. });
  103. }
  104. return this.formatResults(rows, rowCount);
  105. }
  106. run(sql, parameters) {
  107. const errForStack = new Error();
  108. return this.connection.queue.enqueue(() => this._run(this.connection, sql, parameters, errForStack.stack));
  109. }
  110. static formatBindParameters(sql, values, dialect) {
  111. const bindParam = {};
  112. const replacementFunc = (match, key, values2) => {
  113. if (values2[key] !== void 0) {
  114. bindParam[key] = values2[key];
  115. return `@${key}`;
  116. }
  117. return void 0;
  118. };
  119. sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0];
  120. return [sql, bindParam];
  121. }
  122. formatResults(data, rowCount) {
  123. if (this.isInsertQuery(data)) {
  124. this.handleInsertQuery(data);
  125. return [this.instance || data, rowCount];
  126. }
  127. if (this.isShowTablesQuery()) {
  128. return this.handleShowTablesQuery(data);
  129. }
  130. if (this.isDescribeQuery()) {
  131. const result = {};
  132. for (const _result of data) {
  133. if (_result.Default) {
  134. _result.Default = _result.Default.replace("('", "").replace("')", "").replace(/'/g, "");
  135. }
  136. result[_result.Name] = {
  137. type: _result.Type.toUpperCase(),
  138. allowNull: _result.IsNull === "YES" ? true : false,
  139. defaultValue: _result.Default,
  140. primaryKey: _result.Constraint === "PRIMARY KEY",
  141. autoIncrement: _result.IsIdentity === 1,
  142. comment: _result.Comment
  143. };
  144. if (result[_result.Name].type.includes("CHAR") && _result.Length) {
  145. if (_result.Length === -1) {
  146. result[_result.Name].type += "(MAX)";
  147. } else {
  148. result[_result.Name].type += `(${_result.Length})`;
  149. }
  150. }
  151. }
  152. return result;
  153. }
  154. if (this.isSelectQuery()) {
  155. return this.handleSelectQuery(data);
  156. }
  157. if (this.isShowIndexesQuery()) {
  158. return this.handleShowIndexesQuery(data);
  159. }
  160. if (this.isCallQuery()) {
  161. return data[0];
  162. }
  163. if (this.isBulkUpdateQuery()) {
  164. if (this.options.returning) {
  165. return this.handleSelectQuery(data);
  166. }
  167. return rowCount;
  168. }
  169. if (this.isBulkDeleteQuery()) {
  170. return data[0] ? data[0].AFFECTEDROWS : 0;
  171. }
  172. if (this.isVersionQuery()) {
  173. return data[0].version;
  174. }
  175. if (this.isForeignKeysQuery()) {
  176. return data;
  177. }
  178. if (this.isUpsertQuery()) {
  179. if (data && data.length === 0) {
  180. return [this.instance || data, false];
  181. }
  182. this.handleInsertQuery(data);
  183. return [this.instance || data, data[0].$action === "INSERT"];
  184. }
  185. if (this.isUpdateQuery()) {
  186. return [this.instance || data, rowCount];
  187. }
  188. if (this.isShowConstraintsQuery()) {
  189. return this.handleShowConstraintsQuery(data);
  190. }
  191. if (this.isRawQuery()) {
  192. return [data, rowCount];
  193. }
  194. return data;
  195. }
  196. handleShowTablesQuery(results) {
  197. return results.map((resultSet) => {
  198. return {
  199. tableName: resultSet.TABLE_NAME,
  200. schema: resultSet.TABLE_SCHEMA
  201. };
  202. });
  203. }
  204. handleShowConstraintsQuery(data) {
  205. return data.slice(1).map((result) => {
  206. const constraint = {};
  207. for (const key in result) {
  208. constraint[_.camelCase(key)] = result[key];
  209. }
  210. return constraint;
  211. });
  212. }
  213. formatError(err, errStack) {
  214. let match;
  215. match = err.message.match(/Violation of (?:UNIQUE|PRIMARY) KEY constraint '([^']*)'. Cannot insert duplicate key in object '.*'.(:? The duplicate key value is \((.*)\).)?/);
  216. match = match || err.message.match(/Cannot insert duplicate key row in object .* with unique index '(.*)'/);
  217. if (match && match.length > 1) {
  218. let fields = {};
  219. const uniqueKey = this.model && this.model.uniqueKeys[match[1]];
  220. let message = "Validation error";
  221. if (uniqueKey && !!uniqueKey.msg) {
  222. message = uniqueKey.msg;
  223. }
  224. if (match[3]) {
  225. const values = match[3].split(",").map((part) => part.trim());
  226. if (uniqueKey) {
  227. fields = _.zipObject(uniqueKey.fields, values);
  228. } else {
  229. fields[match[1]] = match[3];
  230. }
  231. }
  232. const errors = [];
  233. _.forOwn(fields, (value, field) => {
  234. errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique"));
  235. });
  236. return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack });
  237. }
  238. match = err.message.match(/Failed on step '(.*)'.Could not create constraint. See previous errors./) || err.message.match(/The DELETE statement conflicted with the REFERENCE constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) || err.message.match(/The (?:INSERT|MERGE|UPDATE) statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./);
  239. if (match && match.length > 0) {
  240. return new sequelizeErrors.ForeignKeyConstraintError({
  241. fields: null,
  242. index: match[1],
  243. parent: err,
  244. stack: errStack
  245. });
  246. }
  247. match = err.message.match(/Could not drop constraint. See previous errors./);
  248. if (match && match.length > 0) {
  249. let constraint = err.sql.match(/(?:constraint|index) \[(.+?)\]/i);
  250. constraint = constraint ? constraint[1] : void 0;
  251. let table = err.sql.match(/table \[(.+?)\]/i);
  252. table = table ? table[1] : void 0;
  253. return new sequelizeErrors.UnknownConstraintError({
  254. message: match[1],
  255. constraint,
  256. table,
  257. parent: err,
  258. stack: errStack
  259. });
  260. }
  261. return new sequelizeErrors.DatabaseError(err, { stack: errStack });
  262. }
  263. isShowOrDescribeQuery() {
  264. let result = false;
  265. result = result || this.sql.toLowerCase().startsWith("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'");
  266. result = result || this.sql.toLowerCase().startsWith("select tablename = t.name, name = ind.name,");
  267. result = result || this.sql.toLowerCase().startsWith("exec sys.sp_helpindex @objname");
  268. return result;
  269. }
  270. isShowIndexesQuery() {
  271. return this.sql.toLowerCase().startsWith("exec sys.sp_helpindex @objname");
  272. }
  273. handleShowIndexesQuery(data) {
  274. data = data.reduce((acc, item) => {
  275. if (!(item.index_name in acc)) {
  276. acc[item.index_name] = item;
  277. item.fields = [];
  278. }
  279. item.index_keys.split(",").forEach((column) => {
  280. let columnName = column.trim();
  281. if (columnName.includes("(-)")) {
  282. columnName = columnName.replace("(-)", "");
  283. }
  284. acc[item.index_name].fields.push({
  285. attribute: columnName,
  286. length: void 0,
  287. order: column.includes("(-)") ? "DESC" : "ASC",
  288. collate: void 0
  289. });
  290. });
  291. delete item.index_keys;
  292. return acc;
  293. }, {});
  294. return _.map(data, (item) => ({
  295. primary: item.index_name.toLowerCase().startsWith("pk"),
  296. fields: item.fields,
  297. name: item.index_name,
  298. tableName: void 0,
  299. unique: item.index_description.toLowerCase().includes("unique"),
  300. type: void 0
  301. }));
  302. }
  303. handleInsertQuery(results, metaData) {
  304. if (this.instance) {
  305. const autoIncrementAttribute = this.model.autoIncrementAttribute;
  306. let id = null;
  307. let autoIncrementAttributeAlias = null;
  308. if (Object.prototype.hasOwnProperty.call(this.model.rawAttributes, autoIncrementAttribute) && this.model.rawAttributes[autoIncrementAttribute].field !== void 0)
  309. autoIncrementAttributeAlias = this.model.rawAttributes[autoIncrementAttribute].field;
  310. id = id || results && results[0][this.getInsertIdField()];
  311. id = id || metaData && metaData[this.getInsertIdField()];
  312. id = id || results && results[0][autoIncrementAttribute];
  313. id = id || autoIncrementAttributeAlias && results && results[0][autoIncrementAttributeAlias];
  314. this.instance[autoIncrementAttribute] = id;
  315. if (this.instance.dataValues) {
  316. for (const key in results[0]) {
  317. if (Object.prototype.hasOwnProperty.call(results[0], key)) {
  318. const record = results[0][key];
  319. const attr = _.find(this.model.rawAttributes, (attribute) => attribute.fieldName === key || attribute.field === key);
  320. this.instance.dataValues[attr && attr.fieldName || key] = record;
  321. }
  322. }
  323. }
  324. }
  325. }
  326. }
  327. module.exports = Query;
  328. module.exports.Query = Query;
  329. module.exports.default = Query;
  330. //# sourceMappingURL=query.js.map