query-generator.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  4. var __hasOwnProp = Object.prototype.hasOwnProperty;
  5. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  6. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  7. var __spreadValues = (a, b) => {
  8. for (var prop in b || (b = {}))
  9. if (__hasOwnProp.call(b, prop))
  10. __defNormalProp(a, prop, b[prop]);
  11. if (__getOwnPropSymbols)
  12. for (var prop of __getOwnPropSymbols(b)) {
  13. if (__propIsEnum.call(b, prop))
  14. __defNormalProp(a, prop, b[prop]);
  15. }
  16. return a;
  17. };
  18. const Utils = require("../../utils");
  19. const util = require("util");
  20. const DataTypes = require("../../data-types");
  21. const AbstractQueryGenerator = require("../abstract/query-generator");
  22. const semver = require("semver");
  23. const _ = require("lodash");
  24. const POSTGRES_RESERVED_WORDS = "all,analyse,analyze,and,any,array,as,asc,asymmetric,authorization,binary,both,case,cast,check,collate,collation,column,concurrently,constraint,create,cross,current_catalog,current_date,current_role,current_schema,current_time,current_timestamp,current_user,default,deferrable,desc,distinct,do,else,end,except,false,fetch,for,foreign,freeze,from,full,grant,group,having,ilike,in,initially,inner,intersect,into,is,isnull,join,lateral,leading,left,like,limit,localtime,localtimestamp,natural,not,notnull,null,offset,on,only,or,order,outer,overlaps,placing,primary,references,returning,right,select,session_user,similar,some,symmetric,table,tablesample,then,to,trailing,true,union,unique,user,using,variadic,verbose,when,where,window,with".split(",");
  25. class PostgresQueryGenerator extends AbstractQueryGenerator {
  26. setSearchPath(searchPath) {
  27. return `SET search_path to ${searchPath};`;
  28. }
  29. createDatabaseQuery(databaseName, options) {
  30. options = __spreadValues({
  31. encoding: null,
  32. collate: null
  33. }, options);
  34. const values = {
  35. database: this.quoteTable(databaseName),
  36. encoding: options.encoding ? ` ENCODING = ${this.escape(options.encoding)}` : "",
  37. collation: options.collate ? ` LC_COLLATE = ${this.escape(options.collate)}` : "",
  38. ctype: options.ctype ? ` LC_CTYPE = ${this.escape(options.ctype)}` : "",
  39. template: options.template ? ` TEMPLATE = ${this.escape(options.template)}` : ""
  40. };
  41. return `CREATE DATABASE ${values.database}${values.encoding}${values.collation}${values.ctype}${values.template};`;
  42. }
  43. dropDatabaseQuery(databaseName) {
  44. return `DROP DATABASE IF EXISTS ${this.quoteTable(databaseName)};`;
  45. }
  46. createSchema(schema) {
  47. const databaseVersion = _.get(this, "sequelize.options.databaseVersion", 0);
  48. if (databaseVersion && semver.gte(databaseVersion, "9.2.0")) {
  49. return `CREATE SCHEMA IF NOT EXISTS ${this.quoteIdentifier(schema)};`;
  50. }
  51. return `CREATE SCHEMA ${this.quoteIdentifier(schema)};`;
  52. }
  53. dropSchema(schema) {
  54. return `DROP SCHEMA IF EXISTS ${this.quoteIdentifier(schema)} CASCADE;`;
  55. }
  56. showSchemasQuery() {
  57. return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';";
  58. }
  59. versionQuery() {
  60. return "SHOW SERVER_VERSION";
  61. }
  62. createTableQuery(tableName, attributes, options) {
  63. options = __spreadValues({}, options);
  64. const databaseVersion = _.get(this, "sequelize.options.databaseVersion", 0);
  65. const attrStr = [];
  66. let comments = "";
  67. let columnComments = "";
  68. const quotedTable = this.quoteTable(tableName);
  69. if (options.comment && typeof options.comment === "string") {
  70. comments += `; COMMENT ON TABLE ${quotedTable} IS ${this.escape(options.comment)}`;
  71. }
  72. for (const attr in attributes) {
  73. const quotedAttr = this.quoteIdentifier(attr);
  74. const i = attributes[attr].indexOf("COMMENT ");
  75. if (i !== -1) {
  76. const escapedCommentText = this.escape(attributes[attr].substring(i + 8));
  77. columnComments += `; COMMENT ON COLUMN ${quotedTable}.${quotedAttr} IS ${escapedCommentText}`;
  78. attributes[attr] = attributes[attr].substring(0, i);
  79. }
  80. const dataType = this.dataTypeMapping(tableName, attr, attributes[attr]);
  81. attrStr.push(`${quotedAttr} ${dataType}`);
  82. }
  83. let attributesClause = attrStr.join(", ");
  84. if (options.uniqueKeys) {
  85. _.each(options.uniqueKeys, (columns) => {
  86. if (columns.customIndex) {
  87. attributesClause += `, UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`;
  88. }
  89. });
  90. }
  91. const pks = _.reduce(attributes, (acc, attribute, key) => {
  92. if (attribute.includes("PRIMARY KEY")) {
  93. acc.push(this.quoteIdentifier(key));
  94. }
  95. return acc;
  96. }, []).join(",");
  97. if (pks.length > 0) {
  98. attributesClause += `, PRIMARY KEY (${pks})`;
  99. }
  100. return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, "9.1.0") ? "IF NOT EXISTS " : ""}${quotedTable} (${attributesClause})${comments}${columnComments};`;
  101. }
  102. dropTableQuery(tableName, options) {
  103. options = options || {};
  104. return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)}${options.cascade ? " CASCADE" : ""};`;
  105. }
  106. showTablesQuery() {
  107. const schema = this.options.schema || "public";
  108. return `SELECT table_name FROM information_schema.tables WHERE table_schema = ${this.escape(schema)} AND table_type LIKE '%TABLE' AND table_name != 'spatial_ref_sys';`;
  109. }
  110. tableExistsQuery(tableName) {
  111. const table = tableName.tableName || tableName;
  112. const schema = tableName.schema || "public";
  113. return `SELECT table_name FROM information_schema.tables WHERE table_schema = ${this.escape(schema)} AND table_name = ${this.escape(table)}`;
  114. }
  115. describeTableQuery(tableName, schema) {
  116. schema = schema || this.options.schema || "public";
  117. return `SELECT pk.constraint_type as "Constraint",c.column_name as "Field", c.column_default as "Default",c.is_nullable as "Null", (CASE WHEN c.udt_name = 'hstore' THEN c.udt_name ELSE c.data_type END) || (CASE WHEN c.character_maximum_length IS NOT NULL THEN '(' || c.character_maximum_length || ')' ELSE '' END) as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special", (SELECT pgd.description FROM pg_catalog.pg_statio_all_tables AS st INNER JOIN pg_catalog.pg_description pgd on (pgd.objoid=st.relid) WHERE c.ordinal_position=pgd.objsubid AND c.table_name=st.relname) AS "Comment" FROM information_schema.columns c LEFT JOIN (SELECT tc.table_schema, tc.table_name, cu.column_name, tc.constraint_type FROM information_schema.TABLE_CONSTRAINTS tc JOIN information_schema.KEY_COLUMN_USAGE cu ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name and tc.constraint_name=cu.constraint_name and tc.constraint_type='PRIMARY KEY') pk ON pk.table_schema=c.table_schema AND pk.table_name=c.table_name AND pk.column_name=c.column_name WHERE c.table_name = ${this.escape(tableName)} AND c.table_schema = ${this.escape(schema)}`;
  118. }
  119. _checkValidJsonStatement(stmt) {
  120. if (typeof stmt !== "string") {
  121. return false;
  122. }
  123. const jsonFunctionRegex = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i;
  124. const jsonOperatorRegex = /^\s*(->>?|#>>?|@>|<@|\?[|&]?|\|{2}|#-)/i;
  125. const tokenCaptureRegex = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i;
  126. let currentIndex = 0;
  127. let openingBrackets = 0;
  128. let closingBrackets = 0;
  129. let hasJsonFunction = false;
  130. let hasInvalidToken = false;
  131. while (currentIndex < stmt.length) {
  132. const string = stmt.substr(currentIndex);
  133. const functionMatches = jsonFunctionRegex.exec(string);
  134. if (functionMatches) {
  135. currentIndex += functionMatches[0].indexOf("(");
  136. hasJsonFunction = true;
  137. continue;
  138. }
  139. const operatorMatches = jsonOperatorRegex.exec(string);
  140. if (operatorMatches) {
  141. currentIndex += operatorMatches[0].length;
  142. hasJsonFunction = true;
  143. continue;
  144. }
  145. const tokenMatches = tokenCaptureRegex.exec(string);
  146. if (tokenMatches) {
  147. const capturedToken = tokenMatches[1];
  148. if (capturedToken === "(") {
  149. openingBrackets++;
  150. } else if (capturedToken === ")") {
  151. closingBrackets++;
  152. } else if (capturedToken === ";") {
  153. hasInvalidToken = true;
  154. break;
  155. }
  156. currentIndex += tokenMatches[0].length;
  157. continue;
  158. }
  159. break;
  160. }
  161. hasInvalidToken |= openingBrackets !== closingBrackets;
  162. if (hasJsonFunction && hasInvalidToken) {
  163. throw new Error(`Invalid json statement: ${stmt}`);
  164. }
  165. return hasJsonFunction;
  166. }
  167. handleSequelizeMethod(smth, tableName, factory, options, prepend) {
  168. if (smth instanceof Utils.Json) {
  169. if (smth.conditions) {
  170. const conditions = this.parseConditionObject(smth.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`);
  171. return conditions.join(" AND ");
  172. }
  173. if (smth.path) {
  174. let str;
  175. if (this._checkValidJsonStatement(smth.path)) {
  176. str = smth.path;
  177. } else {
  178. const paths = _.toPath(smth.path);
  179. const column = paths.shift();
  180. str = this.jsonPathExtractionQuery(column, paths);
  181. }
  182. if (smth.value) {
  183. str += util.format(" = %s", this.escape(smth.value));
  184. }
  185. return str;
  186. }
  187. }
  188. return super.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend);
  189. }
  190. addColumnQuery(table, key, attribute) {
  191. const dbDataType = this.attributeToSQL(attribute, { context: "addColumn", table, key });
  192. const dataType = attribute.type || attribute;
  193. const definition = this.dataTypeMapping(table, key, dbDataType);
  194. const quotedKey = this.quoteIdentifier(key);
  195. const quotedTable = this.quoteTable(this.extractTableDetails(table));
  196. let query = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedKey} ${definition};`;
  197. if (dataType instanceof DataTypes.ENUM) {
  198. query = this.pgEnum(table, key, dataType) + query;
  199. } else if (dataType.type && dataType.type instanceof DataTypes.ENUM) {
  200. query = this.pgEnum(table, key, dataType.type) + query;
  201. }
  202. return query;
  203. }
  204. removeColumnQuery(tableName, attributeName) {
  205. const quotedTableName = this.quoteTable(this.extractTableDetails(tableName));
  206. const quotedAttributeName = this.quoteIdentifier(attributeName);
  207. return `ALTER TABLE ${quotedTableName} DROP COLUMN ${quotedAttributeName};`;
  208. }
  209. changeColumnQuery(tableName, attributes) {
  210. const query = (subQuery) => `ALTER TABLE ${this.quoteTable(tableName)} ALTER COLUMN ${subQuery};`;
  211. const sql = [];
  212. for (const attributeName in attributes) {
  213. let definition = this.dataTypeMapping(tableName, attributeName, attributes[attributeName]);
  214. let attrSql = "";
  215. if (definition.includes("NOT NULL")) {
  216. attrSql += query(`${this.quoteIdentifier(attributeName)} SET NOT NULL`);
  217. definition = definition.replace("NOT NULL", "").trim();
  218. } else if (!definition.includes("REFERENCES")) {
  219. attrSql += query(`${this.quoteIdentifier(attributeName)} DROP NOT NULL`);
  220. }
  221. if (definition.includes("DEFAULT")) {
  222. attrSql += query(`${this.quoteIdentifier(attributeName)} SET DEFAULT ${definition.match(/DEFAULT ([^;]+)/)[1]}`);
  223. definition = definition.replace(/(DEFAULT[^;]+)/, "").trim();
  224. } else if (!definition.includes("REFERENCES")) {
  225. attrSql += query(`${this.quoteIdentifier(attributeName)} DROP DEFAULT`);
  226. }
  227. if (attributes[attributeName].startsWith("ENUM(")) {
  228. attrSql += this.pgEnum(tableName, attributeName, attributes[attributeName]);
  229. definition = definition.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attributeName, { schema: false }));
  230. definition += ` USING (${this.quoteIdentifier(attributeName)}::${this.pgEnumName(tableName, attributeName)})`;
  231. }
  232. if (definition.match(/UNIQUE;*$/)) {
  233. definition = definition.replace(/UNIQUE;*$/, "");
  234. attrSql += query(`ADD UNIQUE (${this.quoteIdentifier(attributeName)})`).replace("ALTER COLUMN", "");
  235. }
  236. if (definition.includes("REFERENCES")) {
  237. definition = definition.replace(/.+?(?=REFERENCES)/, "");
  238. attrSql += query(`ADD FOREIGN KEY (${this.quoteIdentifier(attributeName)}) ${definition}`).replace("ALTER COLUMN", "");
  239. } else {
  240. attrSql += query(`${this.quoteIdentifier(attributeName)} TYPE ${definition}`);
  241. }
  242. sql.push(attrSql);
  243. }
  244. return sql.join("");
  245. }
  246. renameColumnQuery(tableName, attrBefore, attributes) {
  247. const attrString = [];
  248. for (const attributeName in attributes) {
  249. attrString.push(`${this.quoteIdentifier(attrBefore)} TO ${this.quoteIdentifier(attributeName)}`);
  250. }
  251. return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(", ")};`;
  252. }
  253. fn(fnName, tableName, parameters, body, returns, language) {
  254. fnName = fnName || "testfunc";
  255. language = language || "plpgsql";
  256. returns = returns ? `RETURNS ${returns}` : "";
  257. parameters = parameters || "";
  258. return `CREATE OR REPLACE FUNCTION pg_temp.${fnName}(${parameters}) ${returns} AS $func$ BEGIN ${body} END; $func$ LANGUAGE ${language}; SELECT * FROM pg_temp.${fnName}();`;
  259. }
  260. truncateTableQuery(tableName, options = {}) {
  261. return [
  262. `TRUNCATE ${this.quoteTable(tableName)}`,
  263. options.restartIdentity ? " RESTART IDENTITY" : "",
  264. options.cascade ? " CASCADE" : ""
  265. ].join("");
  266. }
  267. deleteQuery(tableName, where, options = {}, model) {
  268. const table = this.quoteTable(tableName);
  269. let whereClause = this.getWhereConditions(where, null, model, options);
  270. const limit = options.limit ? ` LIMIT ${this.escape(options.limit)}` : "";
  271. let primaryKeys = "";
  272. let primaryKeysSelection = "";
  273. if (whereClause) {
  274. whereClause = ` WHERE ${whereClause}`;
  275. }
  276. if (options.limit) {
  277. if (!model) {
  278. throw new Error("Cannot LIMIT delete without a model.");
  279. }
  280. const pks = Object.values(model.primaryKeys).map((pk) => this.quoteIdentifier(pk.field)).join(",");
  281. primaryKeys = model.primaryKeyAttributes.length > 1 ? `(${pks})` : pks;
  282. primaryKeysSelection = pks;
  283. return `DELETE FROM ${table} WHERE ${primaryKeys} IN (SELECT ${primaryKeysSelection} FROM ${table}${whereClause}${limit})`;
  284. }
  285. return `DELETE FROM ${table}${whereClause}`;
  286. }
  287. showIndexesQuery(tableName) {
  288. let schemaJoin = "";
  289. let schemaWhere = "";
  290. if (typeof tableName !== "string") {
  291. schemaJoin = ", pg_namespace s";
  292. schemaWhere = ` AND s.oid = t.relnamespace AND s.nspname = '${tableName.schema}'`;
  293. tableName = tableName.tableName;
  294. }
  295. return `SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a${schemaJoin} WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = '${tableName}'${schemaWhere} GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;`;
  296. }
  297. showConstraintsQuery(tableName) {
  298. return [
  299. 'SELECT constraint_catalog AS "constraintCatalog",',
  300. 'constraint_schema AS "constraintSchema",',
  301. 'constraint_name AS "constraintName",',
  302. 'table_catalog AS "tableCatalog",',
  303. 'table_schema AS "tableSchema",',
  304. 'table_name AS "tableName",',
  305. 'constraint_type AS "constraintType",',
  306. 'is_deferrable AS "isDeferrable",',
  307. 'initially_deferred AS "initiallyDeferred"',
  308. "from INFORMATION_SCHEMA.table_constraints",
  309. `WHERE table_name='${tableName}';`
  310. ].join(" ");
  311. }
  312. removeIndexQuery(tableName, indexNameOrAttributes, options) {
  313. let indexName = indexNameOrAttributes;
  314. if (typeof indexName !== "string") {
  315. indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`);
  316. }
  317. return [
  318. "DROP INDEX",
  319. options && options.concurrently && "CONCURRENTLY",
  320. `IF EXISTS ${this.quoteIdentifiers(indexName)}`
  321. ].filter(Boolean).join(" ");
  322. }
  323. addLimitAndOffset(options) {
  324. let fragment = "";
  325. if (options.limit != null) {
  326. fragment += " LIMIT " + this.escape(options.limit);
  327. }
  328. if (options.offset != null) {
  329. fragment += " OFFSET " + this.escape(options.offset);
  330. }
  331. return fragment;
  332. }
  333. attributeToSQL(attribute, options) {
  334. if (!_.isPlainObject(attribute)) {
  335. attribute = {
  336. type: attribute
  337. };
  338. }
  339. let type;
  340. if (attribute.type instanceof DataTypes.ENUM || attribute.type instanceof DataTypes.ARRAY && attribute.type.type instanceof DataTypes.ENUM) {
  341. const enumType = attribute.type.type || attribute.type;
  342. let values = attribute.values;
  343. if (enumType.values && !attribute.values) {
  344. values = enumType.values;
  345. }
  346. if (Array.isArray(values) && values.length > 0) {
  347. type = `ENUM(${values.map((value) => this.escape(value)).join(", ")})`;
  348. if (attribute.type instanceof DataTypes.ARRAY) {
  349. type += "[]";
  350. }
  351. } else {
  352. throw new Error("Values for ENUM haven't been defined.");
  353. }
  354. }
  355. if (!type) {
  356. type = attribute.type;
  357. }
  358. let sql = type.toString();
  359. if (Object.prototype.hasOwnProperty.call(attribute, "allowNull") && !attribute.allowNull) {
  360. sql += " NOT NULL";
  361. }
  362. if (attribute.autoIncrement) {
  363. if (attribute.autoIncrementIdentity) {
  364. sql += " GENERATED BY DEFAULT AS IDENTITY";
  365. } else {
  366. sql += " SERIAL";
  367. }
  368. }
  369. if (Utils.defaultValueSchemable(attribute.defaultValue)) {
  370. sql += ` DEFAULT ${this.escape(attribute.defaultValue, attribute)}`;
  371. }
  372. if (attribute.unique === true) {
  373. sql += " UNIQUE";
  374. }
  375. if (attribute.primaryKey) {
  376. sql += " PRIMARY KEY";
  377. }
  378. if (attribute.references) {
  379. let referencesTable = this.quoteTable(attribute.references.model);
  380. let schema;
  381. if (options.schema) {
  382. schema = options.schema;
  383. } else if ((!attribute.references.model || typeof attribute.references.model == "string") && options.table && options.table.schema) {
  384. schema = options.table.schema;
  385. }
  386. if (schema) {
  387. referencesTable = this.quoteTable(this.addSchema({
  388. tableName: referencesTable,
  389. _schema: schema
  390. }));
  391. }
  392. let referencesKey;
  393. if (!options.withoutForeignKeyConstraints) {
  394. if (attribute.references.key) {
  395. referencesKey = this.quoteIdentifiers(attribute.references.key);
  396. } else {
  397. referencesKey = this.quoteIdentifier("id");
  398. }
  399. sql += ` REFERENCES ${referencesTable} (${referencesKey})`;
  400. if (attribute.onDelete) {
  401. sql += ` ON DELETE ${attribute.onDelete.toUpperCase()}`;
  402. }
  403. if (attribute.onUpdate) {
  404. sql += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`;
  405. }
  406. if (attribute.references.deferrable) {
  407. sql += ` ${attribute.references.deferrable.toString(this)}`;
  408. }
  409. }
  410. }
  411. if (attribute.comment && typeof attribute.comment === "string") {
  412. if (options && ["addColumn", "changeColumn"].includes(options.context)) {
  413. const quotedAttr = this.quoteIdentifier(options.key);
  414. const escapedCommentText = this.escape(attribute.comment);
  415. sql += `; COMMENT ON COLUMN ${this.quoteTable(options.table)}.${quotedAttr} IS ${escapedCommentText}`;
  416. } else {
  417. sql += ` COMMENT ${attribute.comment}`;
  418. }
  419. }
  420. return sql;
  421. }
  422. deferConstraintsQuery(options) {
  423. return options.deferrable.toString(this);
  424. }
  425. setConstraintQuery(columns, type) {
  426. let columnFragment = "ALL";
  427. if (columns) {
  428. columnFragment = columns.map((column) => this.quoteIdentifier(column)).join(", ");
  429. }
  430. return `SET CONSTRAINTS ${columnFragment} ${type}`;
  431. }
  432. setDeferredQuery(columns) {
  433. return this.setConstraintQuery(columns, "DEFERRED");
  434. }
  435. setImmediateQuery(columns) {
  436. return this.setConstraintQuery(columns, "IMMEDIATE");
  437. }
  438. attributesToSQL(attributes, options) {
  439. const result = {};
  440. for (const key in attributes) {
  441. const attribute = attributes[key];
  442. result[attribute.field || key] = this.attributeToSQL(attribute, __spreadValues({ key }, options));
  443. }
  444. return result;
  445. }
  446. createTrigger(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) {
  447. const decodedEventType = this.decodeTriggerEventType(eventType);
  448. const eventSpec = this.expandTriggerEventSpec(fireOnSpec);
  449. const expandedOptions = this.expandOptions(optionsArray);
  450. const paramList = this._expandFunctionParamList(functionParams);
  451. return `CREATE ${this.triggerEventTypeIsConstraint(eventType)}TRIGGER ${this.quoteIdentifier(triggerName)} ${decodedEventType} ${eventSpec} ON ${this.quoteTable(tableName)}${expandedOptions ? ` ${expandedOptions}` : ""} EXECUTE PROCEDURE ${functionName}(${paramList});`;
  452. }
  453. dropTrigger(tableName, triggerName) {
  454. return `DROP TRIGGER ${this.quoteIdentifier(triggerName)} ON ${this.quoteTable(tableName)} RESTRICT;`;
  455. }
  456. renameTrigger(tableName, oldTriggerName, newTriggerName) {
  457. return `ALTER TRIGGER ${this.quoteIdentifier(oldTriggerName)} ON ${this.quoteTable(tableName)} RENAME TO ${this.quoteIdentifier(newTriggerName)};`;
  458. }
  459. createFunction(functionName, params, returnType, language, body, optionsArray, options) {
  460. if (!functionName || !returnType || !language || !body)
  461. throw new Error("createFunction missing some parameters. Did you pass functionName, returnType, language and body?");
  462. const paramList = this._expandFunctionParamList(params);
  463. const variableList = options && options.variables ? this._expandFunctionVariableList(options.variables) : "";
  464. const expandedOptionsArray = this.expandOptions(optionsArray);
  465. const statement = options && options.force ? "CREATE OR REPLACE FUNCTION" : "CREATE FUNCTION";
  466. return `${statement} ${functionName}(${paramList}) RETURNS ${returnType} AS $func$ ${variableList} BEGIN ${body} END; $func$ language '${language}'${expandedOptionsArray};`;
  467. }
  468. dropFunction(functionName, params) {
  469. if (!functionName)
  470. throw new Error("requires functionName");
  471. const paramList = this._expandFunctionParamList(params);
  472. return `DROP FUNCTION ${functionName}(${paramList}) RESTRICT;`;
  473. }
  474. renameFunction(oldFunctionName, params, newFunctionName) {
  475. const paramList = this._expandFunctionParamList(params);
  476. return `ALTER FUNCTION ${oldFunctionName}(${paramList}) RENAME TO ${newFunctionName};`;
  477. }
  478. pgEscapeAndQuote(val) {
  479. return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'"));
  480. }
  481. _expandFunctionParamList(params) {
  482. if (params === void 0 || !Array.isArray(params)) {
  483. throw new Error("_expandFunctionParamList: function parameters array required, including an empty one for no arguments");
  484. }
  485. const paramList = [];
  486. params.forEach((curParam) => {
  487. const paramDef = [];
  488. if (curParam.type) {
  489. if (curParam.direction) {
  490. paramDef.push(curParam.direction);
  491. }
  492. if (curParam.name) {
  493. paramDef.push(curParam.name);
  494. }
  495. paramDef.push(curParam.type);
  496. } else {
  497. throw new Error("function or trigger used with a parameter without any type");
  498. }
  499. const joined = paramDef.join(" ");
  500. if (joined)
  501. paramList.push(joined);
  502. });
  503. return paramList.join(", ");
  504. }
  505. _expandFunctionVariableList(variables) {
  506. if (!Array.isArray(variables)) {
  507. throw new Error("_expandFunctionVariableList: function variables must be an array");
  508. }
  509. const variableDefinitions = [];
  510. variables.forEach((variable) => {
  511. if (!variable.name || !variable.type) {
  512. throw new Error("function variable must have a name and type");
  513. }
  514. let variableDefinition = `DECLARE ${variable.name} ${variable.type}`;
  515. if (variable.default) {
  516. variableDefinition += ` := ${variable.default}`;
  517. }
  518. variableDefinition += ";";
  519. variableDefinitions.push(variableDefinition);
  520. });
  521. return variableDefinitions.join(" ");
  522. }
  523. expandOptions(options) {
  524. return options === void 0 || _.isEmpty(options) ? "" : options.join(" ");
  525. }
  526. decodeTriggerEventType(eventSpecifier) {
  527. const EVENT_DECODER = {
  528. "after": "AFTER",
  529. "before": "BEFORE",
  530. "instead_of": "INSTEAD OF",
  531. "after_constraint": "AFTER"
  532. };
  533. if (!EVENT_DECODER[eventSpecifier]) {
  534. throw new Error(`Invalid trigger event specified: ${eventSpecifier}`);
  535. }
  536. return EVENT_DECODER[eventSpecifier];
  537. }
  538. triggerEventTypeIsConstraint(eventSpecifier) {
  539. return eventSpecifier === "after_constraint" ? "CONSTRAINT " : "";
  540. }
  541. expandTriggerEventSpec(fireOnSpec) {
  542. if (_.isEmpty(fireOnSpec)) {
  543. throw new Error("no table change events specified to trigger on");
  544. }
  545. return _.map(fireOnSpec, (fireValue, fireKey) => {
  546. const EVENT_MAP = {
  547. "insert": "INSERT",
  548. "update": "UPDATE",
  549. "delete": "DELETE",
  550. "truncate": "TRUNCATE"
  551. };
  552. if (!EVENT_MAP[fireValue]) {
  553. throw new Error(`parseTriggerEventSpec: undefined trigger event ${fireKey}`);
  554. }
  555. let eventSpec = EVENT_MAP[fireValue];
  556. if (eventSpec === "UPDATE") {
  557. if (Array.isArray(fireValue) && fireValue.length > 0) {
  558. eventSpec += ` OF ${fireValue.join(", ")}`;
  559. }
  560. }
  561. return eventSpec;
  562. }).join(" OR ");
  563. }
  564. pgEnumName(tableName, attr, options) {
  565. options = options || {};
  566. const tableDetails = this.extractTableDetails(tableName, options);
  567. let enumName = Utils.addTicks(Utils.generateEnumName(tableDetails.tableName, attr), '"');
  568. if (options.schema !== false && tableDetails.schema) {
  569. enumName = this.quoteIdentifier(tableDetails.schema) + tableDetails.delimiter + enumName;
  570. }
  571. return enumName;
  572. }
  573. pgListEnums(tableName, attrName, options) {
  574. let enumName = "";
  575. const tableDetails = this.extractTableDetails(tableName, options);
  576. if (tableDetails.tableName && attrName) {
  577. enumName = ` AND t.typname=${this.pgEnumName(tableDetails.tableName, attrName, { schema: false }).replace(/"/g, "'")}`;
  578. }
  579. return `SELECT t.typname enum_name, array_agg(e.enumlabel ORDER BY enumsortorder) enum_value FROM pg_type t JOIN pg_enum e ON t.oid = e.enumtypid JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = '${tableDetails.schema}'${enumName} GROUP BY 1`;
  580. }
  581. pgEnum(tableName, attr, dataType, options) {
  582. const enumName = this.pgEnumName(tableName, attr, options);
  583. let values;
  584. if (dataType.values) {
  585. values = `ENUM(${dataType.values.map((value) => this.escape(value)).join(", ")})`;
  586. } else {
  587. values = dataType.toString().match(/^ENUM\(.+\)/)[0];
  588. }
  589. let sql = `DO ${this.escape(`BEGIN CREATE TYPE ${enumName} AS ${values}; EXCEPTION WHEN duplicate_object THEN null; END`)};`;
  590. if (!!options && options.force === true) {
  591. sql = this.pgEnumDrop(tableName, attr) + sql;
  592. }
  593. return sql;
  594. }
  595. pgEnumAdd(tableName, attr, value, options) {
  596. const enumName = this.pgEnumName(tableName, attr);
  597. let sql = `ALTER TYPE ${enumName} ADD VALUE `;
  598. if (semver.gte(this.sequelize.options.databaseVersion, "9.3.0")) {
  599. sql += "IF NOT EXISTS ";
  600. }
  601. sql += this.escape(value);
  602. if (options.before) {
  603. sql += ` BEFORE ${this.escape(options.before)}`;
  604. } else if (options.after) {
  605. sql += ` AFTER ${this.escape(options.after)}`;
  606. }
  607. return sql;
  608. }
  609. pgEnumDrop(tableName, attr, enumName) {
  610. enumName = enumName || this.pgEnumName(tableName, attr);
  611. return `DROP TYPE IF EXISTS ${enumName}; `;
  612. }
  613. fromArray(text) {
  614. text = text.replace(/^{/, "").replace(/}$/, "");
  615. let matches = text.match(/("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|\s*$)/ig);
  616. if (matches.length < 1) {
  617. return [];
  618. }
  619. matches = matches.map((m) => m.replace(/",$/, "").replace(/,$/, "").replace(/(^"|"$)/g, ""));
  620. return matches.slice(0, -1);
  621. }
  622. dataTypeMapping(tableName, attr, dataType) {
  623. if (dataType.includes("PRIMARY KEY")) {
  624. dataType = dataType.replace("PRIMARY KEY", "");
  625. }
  626. if (dataType.includes("SERIAL")) {
  627. if (dataType.includes("BIGINT")) {
  628. dataType = dataType.replace("SERIAL", "BIGSERIAL");
  629. dataType = dataType.replace("BIGINT", "");
  630. } else if (dataType.includes("SMALLINT")) {
  631. dataType = dataType.replace("SERIAL", "SMALLSERIAL");
  632. dataType = dataType.replace("SMALLINT", "");
  633. } else {
  634. dataType = dataType.replace("INTEGER", "");
  635. }
  636. dataType = dataType.replace("NOT NULL", "");
  637. }
  638. if (dataType.startsWith("ENUM(")) {
  639. dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attr));
  640. }
  641. return dataType;
  642. }
  643. getForeignKeysQuery(tableName) {
  644. 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 = '${tableName}' LIMIT 1) AND r.contype = 'f' ORDER BY 1;`;
  645. }
  646. _getForeignKeyReferencesQueryPrefix() {
  647. return "SELECT DISTINCT tc.constraint_name as constraint_name, tc.constraint_schema as constraint_schema, tc.constraint_catalog as constraint_catalog, tc.table_name as table_name,tc.table_schema as table_schema,tc.table_catalog as table_catalog,tc.initially_deferred as initially_deferred,tc.is_deferrable as is_deferrable,kcu.column_name as column_name,ccu.table_schema AS referenced_table_schema,ccu.table_catalog AS referenced_table_catalog,ccu.table_name AS referenced_table_name,ccu.column_name AS referenced_column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name ";
  648. }
  649. getForeignKeyReferencesQuery(tableName, catalogName, schemaName) {
  650. return `${this._getForeignKeyReferencesQueryPrefix()}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '${tableName}'${catalogName ? ` AND tc.table_catalog = '${catalogName}'` : ""}${schemaName ? ` AND tc.table_schema = '${schemaName}'` : ""}`;
  651. }
  652. getForeignKeyReferenceQuery(table, columnName) {
  653. const tableName = table.tableName || table;
  654. const schema = table.schema;
  655. return `${this._getForeignKeyReferencesQueryPrefix()}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}' AND kcu.column_name = '${columnName}'${schema ? ` AND tc.table_schema = '${schema}'` : ""}`;
  656. }
  657. dropForeignKeyQuery(tableName, foreignKey) {
  658. return `ALTER TABLE ${this.quoteTable(tableName)} DROP CONSTRAINT ${this.quoteIdentifier(foreignKey)};`;
  659. }
  660. quoteIdentifier(identifier, force) {
  661. const optForceQuote = force || false;
  662. const optQuoteIdentifiers = this.options.quoteIdentifiers !== false;
  663. const rawIdentifier = Utils.removeTicks(identifier, '"');
  664. if (optForceQuote === true || optQuoteIdentifiers !== false || identifier.includes(".") || identifier.includes("->") || POSTGRES_RESERVED_WORDS.includes(rawIdentifier.toLowerCase())) {
  665. return Utils.addTicks(rawIdentifier, '"');
  666. }
  667. return rawIdentifier;
  668. }
  669. }
  670. module.exports = PostgresQueryGenerator;
  671. //# sourceMappingURL=query-generator.js.map