query.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. "use strict";
  2. const AbstractQuery = require("../abstract/query");
  3. const QueryTypes = require("../../query-types");
  4. const sequelizeErrors = require("../../errors");
  5. const _ = require("lodash");
  6. const { logger } = require("../../utils/logger");
  7. const debug = logger.debugContext("sql:pg");
  8. class Query extends AbstractQuery {
  9. static formatBindParameters(sql, values, dialect) {
  10. const stringReplaceFunc = (value) => typeof value === "string" ? value.replace(/\0/g, "\\0") : value;
  11. let bindParam;
  12. if (Array.isArray(values)) {
  13. bindParam = values.map(stringReplaceFunc);
  14. sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
  15. } else {
  16. bindParam = [];
  17. let i = 0;
  18. const seen = {};
  19. const replacementFunc = (match, key, values2) => {
  20. if (seen[key] !== void 0) {
  21. return seen[key];
  22. }
  23. if (values2[key] !== void 0) {
  24. i = i + 1;
  25. bindParam.push(stringReplaceFunc(values2[key]));
  26. seen[key] = `$${i}`;
  27. return `$${i}`;
  28. }
  29. return void 0;
  30. };
  31. sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0];
  32. }
  33. return [sql, bindParam];
  34. }
  35. async run(sql, parameters) {
  36. const { connection } = this;
  37. if (!_.isEmpty(this.options.searchPath)) {
  38. sql = this.sequelize.getQueryInterface().queryGenerator.setSearchPath(this.options.searchPath) + sql;
  39. }
  40. if (this.sequelize.options.minifyAliases && this.options.includeAliases) {
  41. _.toPairs(this.options.includeAliases).sort((a, b) => b[1].length - a[1].length).forEach(([alias, original]) => {
  42. const reg = new RegExp(_.escapeRegExp(original), "g");
  43. sql = sql.replace(reg, alias);
  44. });
  45. }
  46. this.sql = sql;
  47. const query = parameters && parameters.length ? new Promise((resolve, reject) => connection.query(sql, parameters, (error, result) => error ? reject(error) : resolve(result))) : new Promise((resolve, reject) => connection.query(sql, (error, result) => error ? reject(error) : resolve(result)));
  48. const complete = this._logQuery(sql, debug, parameters);
  49. let queryResult;
  50. const errForStack = new Error();
  51. try {
  52. queryResult = await query;
  53. } catch (error) {
  54. if (error.code === "ECONNRESET" || /Unable to set non-blocking to true/i.test(error) || /SSL SYSCALL error: EOF detected/i.test(error) || /Local: Authentication failure/i.test(error) || error.message === "Query read timeout") {
  55. connection._invalid = true;
  56. }
  57. error.sql = sql;
  58. error.parameters = parameters;
  59. throw this.formatError(error, errForStack.stack);
  60. }
  61. complete();
  62. let rows = Array.isArray(queryResult) ? queryResult.reduce((allRows, r) => allRows.concat(r.rows || []), []) : queryResult.rows;
  63. const rowCount = Array.isArray(queryResult) ? queryResult.reduce((count, r) => Number.isFinite(r.rowCount) ? count + r.rowCount : count, 0) : queryResult.rowCount || 0;
  64. if (this.sequelize.options.minifyAliases && this.options.aliasesMapping) {
  65. rows = rows.map((row) => _.toPairs(row).reduce((acc, [key, value]) => {
  66. const mapping = this.options.aliasesMapping.get(key);
  67. acc[mapping || key] = value;
  68. return acc;
  69. }, {}));
  70. }
  71. const isTableNameQuery = sql.startsWith("SELECT table_name FROM information_schema.tables");
  72. const isRelNameQuery = sql.startsWith("SELECT relname FROM pg_class WHERE oid IN");
  73. if (isRelNameQuery) {
  74. return rows.map((row) => ({
  75. name: row.relname,
  76. tableName: row.relname.split("_")[0]
  77. }));
  78. }
  79. if (isTableNameQuery) {
  80. return rows.map((row) => Object.values(row));
  81. }
  82. if (rows[0] && rows[0].sequelize_caught_exception !== void 0) {
  83. if (rows[0].sequelize_caught_exception !== null) {
  84. throw this.formatError({
  85. sql,
  86. parameters,
  87. code: "23505",
  88. detail: rows[0].sequelize_caught_exception
  89. });
  90. }
  91. for (const row of rows) {
  92. delete row.sequelize_caught_exception;
  93. }
  94. }
  95. if (this.isShowIndexesQuery()) {
  96. for (const row of rows) {
  97. const attributes = /ON .*? (?:USING .*?\s)?\(([^]*)\)/gi.exec(row.definition)[1].split(",");
  98. const columns = _.zipObject(row.column_indexes, this.sequelize.getQueryInterface().queryGenerator.fromArray(row.column_names));
  99. delete row.column_indexes;
  100. delete row.column_names;
  101. let field;
  102. let attribute;
  103. row.fields = row.indkey.split(" ").map((indKey, index) => {
  104. field = columns[indKey];
  105. if (!field) {
  106. return null;
  107. }
  108. attribute = attributes[index];
  109. return {
  110. attribute: field,
  111. collate: attribute.match(/COLLATE "(.*?)"/) ? /COLLATE "(.*?)"/.exec(attribute)[1] : void 0,
  112. order: attribute.includes("DESC") ? "DESC" : attribute.includes("ASC") ? "ASC" : void 0,
  113. length: void 0
  114. };
  115. }).filter((n) => n !== null);
  116. delete row.columns;
  117. }
  118. return rows;
  119. }
  120. if (this.isForeignKeysQuery()) {
  121. const result = [];
  122. for (const row of rows) {
  123. let defParts;
  124. if (row.condef !== void 0 && (defParts = row.condef.match(/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?/))) {
  125. row.id = row.constraint_name;
  126. row.table = defParts[2];
  127. row.from = defParts[1];
  128. row.to = defParts[3];
  129. let i;
  130. for (i = 5; i <= 8; i += 3) {
  131. if (/(UPDATE|DELETE)/.test(defParts[i])) {
  132. row[`on_${defParts[i].toLowerCase()}`] = defParts[i + 1];
  133. }
  134. }
  135. }
  136. result.push(row);
  137. }
  138. return result;
  139. }
  140. if (this.isSelectQuery()) {
  141. let result = rows;
  142. if (this.options.raw === false && this.sequelize.options.quoteIdentifiers === false) {
  143. const attrsMap = _.reduce(this.model.rawAttributes, (m, v, k) => {
  144. m[k.toLowerCase()] = k;
  145. return m;
  146. }, {});
  147. result = rows.map((row) => {
  148. return _.mapKeys(row, (value, key) => {
  149. const targetAttr = attrsMap[key];
  150. if (typeof targetAttr === "string" && targetAttr !== key) {
  151. return targetAttr;
  152. }
  153. return key;
  154. });
  155. });
  156. }
  157. return this.handleSelectQuery(result);
  158. }
  159. if (QueryTypes.DESCRIBE === this.options.type) {
  160. const result = {};
  161. for (const row of rows) {
  162. result[row.Field] = {
  163. type: row.Type.toUpperCase(),
  164. allowNull: row.Null === "YES",
  165. defaultValue: row.Default,
  166. comment: row.Comment,
  167. special: row.special ? this.sequelize.getQueryInterface().queryGenerator.fromArray(row.special) : [],
  168. primaryKey: row.Constraint === "PRIMARY KEY"
  169. };
  170. if (result[row.Field].type === "BOOLEAN") {
  171. result[row.Field].defaultValue = { "false": false, "true": true }[result[row.Field].defaultValue];
  172. if (result[row.Field].defaultValue === void 0) {
  173. result[row.Field].defaultValue = null;
  174. }
  175. }
  176. if (typeof result[row.Field].defaultValue === "string") {
  177. result[row.Field].defaultValue = result[row.Field].defaultValue.replace(/'/g, "");
  178. if (result[row.Field].defaultValue.includes("::")) {
  179. const split = result[row.Field].defaultValue.split("::");
  180. if (split[1].toLowerCase() !== "regclass)") {
  181. result[row.Field].defaultValue = split[0];
  182. }
  183. }
  184. }
  185. }
  186. return result;
  187. }
  188. if (this.isVersionQuery()) {
  189. return rows[0].server_version;
  190. }
  191. if (this.isShowOrDescribeQuery()) {
  192. return rows;
  193. }
  194. if (QueryTypes.BULKUPDATE === this.options.type) {
  195. if (!this.options.returning) {
  196. return parseInt(rowCount, 10);
  197. }
  198. return this.handleSelectQuery(rows);
  199. }
  200. if (QueryTypes.BULKDELETE === this.options.type) {
  201. return parseInt(rowCount, 10);
  202. }
  203. if (this.isInsertQuery() || this.isUpdateQuery() || this.isUpsertQuery()) {
  204. if (this.instance && this.instance.dataValues) {
  205. if (this.isInsertQuery() && !this.isUpsertQuery() && rowCount === 0) {
  206. throw new sequelizeErrors.EmptyResultError();
  207. }
  208. for (const key in rows[0]) {
  209. if (Object.prototype.hasOwnProperty.call(rows[0], key)) {
  210. const record = rows[0][key];
  211. const attr = _.find(this.model.rawAttributes, (attribute) => attribute.fieldName === key || attribute.field === key);
  212. this.instance.dataValues[attr && attr.fieldName || key] = record;
  213. }
  214. }
  215. }
  216. if (this.isUpsertQuery()) {
  217. return [
  218. this.instance,
  219. null
  220. ];
  221. }
  222. return [
  223. this.instance || rows && (this.options.plain && rows[0] || rows) || void 0,
  224. rowCount
  225. ];
  226. }
  227. if (this.isRawQuery()) {
  228. return [rows, queryResult];
  229. }
  230. return rows;
  231. }
  232. formatError(err, errStack) {
  233. let match;
  234. let table;
  235. let index;
  236. let fields;
  237. let errors;
  238. let message;
  239. const code = err.code || err.sqlState;
  240. const errMessage = err.message || err.messagePrimary;
  241. const errDetail = err.detail || err.messageDetail;
  242. switch (code) {
  243. case "23503":
  244. index = errMessage.match(/violates foreign key constraint "(.+?)"/);
  245. index = index ? index[1] : void 0;
  246. table = errMessage.match(/on table "(.+?)"/);
  247. table = table ? table[1] : void 0;
  248. return new sequelizeErrors.ForeignKeyConstraintError({
  249. message: errMessage,
  250. fields: null,
  251. index,
  252. table,
  253. parent: err,
  254. stack: errStack
  255. });
  256. case "23505":
  257. if (errDetail && (match = errDetail.replace(/"/g, "").match(/Key \((.*?)\)=\((.*?)\)/))) {
  258. fields = _.zipObject(match[1].split(", "), match[2].split(", "));
  259. errors = [];
  260. message = "Validation error";
  261. _.forOwn(fields, (value, field) => {
  262. errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique"));
  263. });
  264. if (this.model && this.model.uniqueKeys) {
  265. _.forOwn(this.model.uniqueKeys, (constraint) => {
  266. if (_.isEqual(constraint.fields, Object.keys(fields)) && !!constraint.msg) {
  267. message = constraint.msg;
  268. return false;
  269. }
  270. });
  271. }
  272. return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack });
  273. }
  274. return new sequelizeErrors.UniqueConstraintError({
  275. message: errMessage,
  276. parent: err,
  277. stack: errStack
  278. });
  279. case "23P01":
  280. match = errDetail.match(/Key \((.*?)\)=\((.*?)\)/);
  281. if (match) {
  282. fields = _.zipObject(match[1].split(", "), match[2].split(", "));
  283. }
  284. message = "Exclusion constraint error";
  285. return new sequelizeErrors.ExclusionConstraintError({
  286. message,
  287. constraint: err.constraint,
  288. fields,
  289. table: err.table,
  290. parent: err,
  291. stack: errStack
  292. });
  293. case "42704":
  294. if (err.sql && /(CONSTRAINT|INDEX)/gi.test(err.sql)) {
  295. message = "Unknown constraint error";
  296. index = errMessage.match(/(?:constraint|index) "(.+?)"/i);
  297. index = index ? index[1] : void 0;
  298. table = errMessage.match(/relation "(.+?)"/i);
  299. table = table ? table[1] : void 0;
  300. throw new sequelizeErrors.UnknownConstraintError({
  301. message,
  302. constraint: index,
  303. fields,
  304. table,
  305. parent: err,
  306. stack: errStack
  307. });
  308. }
  309. default:
  310. return new sequelizeErrors.DatabaseError(err, { stack: errStack });
  311. }
  312. }
  313. isForeignKeysQuery() {
  314. return /SELECT conname as constraint_name, pg_catalog\.pg_get_constraintdef\(r\.oid, true\) as condef FROM pg_catalog\.pg_constraint r WHERE r\.conrelid = \(SELECT oid FROM pg_class WHERE relname = '.*' LIMIT 1\) AND r\.contype = 'f' ORDER BY 1;/.test(this.sql);
  315. }
  316. getInsertIdField() {
  317. return "id";
  318. }
  319. }
  320. module.exports = Query;
  321. module.exports.Query = Query;
  322. module.exports.default = Query;
  323. //# sourceMappingURL=query.js.map