UNPKG

sequelize

Version:

Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more.

672 lines (671 loc) 32 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; const Utils = require("../../utils"); const util = require("util"); const DataTypes = require("../../data-types"); const AbstractQueryGenerator = require("../abstract/query-generator"); const semver = require("semver"); const _ = require("lodash"); 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(","); class PostgresQueryGenerator extends AbstractQueryGenerator { setSearchPath(searchPath) { return `SET search_path to ${searchPath};`; } createDatabaseQuery(databaseName, options) { options = __spreadValues({ encoding: null, collate: null }, options); const values = { database: this.quoteTable(databaseName), encoding: options.encoding ? ` ENCODING = ${this.escape(options.encoding)}` : "", collation: options.collate ? ` LC_COLLATE = ${this.escape(options.collate)}` : "", ctype: options.ctype ? ` LC_CTYPE = ${this.escape(options.ctype)}` : "", template: options.template ? ` TEMPLATE = ${this.escape(options.template)}` : "" }; return `CREATE DATABASE ${values.database}${values.encoding}${values.collation}${values.ctype}${values.template};`; } dropDatabaseQuery(databaseName) { return `DROP DATABASE IF EXISTS ${this.quoteTable(databaseName)};`; } createSchema(schema) { const databaseVersion = _.get(this, "sequelize.options.databaseVersion", 0); if (databaseVersion && semver.gte(databaseVersion, "9.2.0")) { return `CREATE SCHEMA IF NOT EXISTS ${this.quoteIdentifier(schema)};`; } return `CREATE SCHEMA ${this.quoteIdentifier(schema)};`; } dropSchema(schema) { return `DROP SCHEMA IF EXISTS ${this.quoteIdentifier(schema)} CASCADE;`; } showSchemasQuery() { return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';"; } versionQuery() { return "SHOW SERVER_VERSION"; } createTableQuery(tableName, attributes, options) { options = __spreadValues({}, options); const databaseVersion = _.get(this, "sequelize.options.databaseVersion", 0); const attrStr = []; let comments = ""; let columnComments = ""; const quotedTable = this.quoteTable(tableName); if (options.comment && typeof options.comment === "string") { comments += `; COMMENT ON TABLE ${quotedTable} IS ${this.escape(options.comment)}`; } for (const attr in attributes) { const quotedAttr = this.quoteIdentifier(attr); const i = attributes[attr].indexOf("COMMENT "); if (i !== -1) { const escapedCommentText = this.escape(attributes[attr].substring(i + 8)); columnComments += `; COMMENT ON COLUMN ${quotedTable}.${quotedAttr} IS ${escapedCommentText}`; attributes[attr] = attributes[attr].substring(0, i); } const dataType = this.dataTypeMapping(tableName, attr, attributes[attr]); attrStr.push(`${quotedAttr} ${dataType}`); } let attributesClause = attrStr.join(", "); if (options.uniqueKeys) { _.each(options.uniqueKeys, (columns) => { if (columns.customIndex) { attributesClause += `, UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`; } }); } const pks = _.reduce(attributes, (acc, attribute, key) => { if (attribute.includes("PRIMARY KEY")) { acc.push(this.quoteIdentifier(key)); } return acc; }, []).join(","); if (pks.length > 0) { attributesClause += `, PRIMARY KEY (${pks})`; } return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, "9.1.0") ? "IF NOT EXISTS " : ""}${quotedTable} (${attributesClause})${comments}${columnComments};`; } dropTableQuery(tableName, options) { options = options || {}; return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)}${options.cascade ? " CASCADE" : ""};`; } showTablesQuery() { const schema = this.options.schema || "public"; 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';`; } tableExistsQuery(tableName) { const table = tableName.tableName || tableName; const schema = tableName.schema || "public"; return `SELECT table_name FROM information_schema.tables WHERE table_schema = ${this.escape(schema)} AND table_name = ${this.escape(table)}`; } describeTableQuery(tableName, schema) { schema = schema || this.options.schema || "public"; 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)}`; } _checkValidJsonStatement(stmt) { if (typeof stmt !== "string") { return false; } const jsonFunctionRegex = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i; const jsonOperatorRegex = /^\s*(->>?|#>>?|@>|<@|\?[|&]?|\|{2}|#-)/i; const tokenCaptureRegex = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i; let currentIndex = 0; let openingBrackets = 0; let closingBrackets = 0; let hasJsonFunction = false; let hasInvalidToken = false; while (currentIndex < stmt.length) { const string = stmt.substr(currentIndex); const functionMatches = jsonFunctionRegex.exec(string); if (functionMatches) { currentIndex += functionMatches[0].indexOf("("); hasJsonFunction = true; continue; } const operatorMatches = jsonOperatorRegex.exec(string); if (operatorMatches) { currentIndex += operatorMatches[0].length; hasJsonFunction = true; continue; } const tokenMatches = tokenCaptureRegex.exec(string); if (tokenMatches) { const capturedToken = tokenMatches[1]; if (capturedToken === "(") { openingBrackets++; } else if (capturedToken === ")") { closingBrackets++; } else if (capturedToken === ";") { hasInvalidToken = true; break; } currentIndex += tokenMatches[0].length; continue; } break; } hasInvalidToken |= openingBrackets !== closingBrackets; if (hasJsonFunction && hasInvalidToken) { throw new Error(`Invalid json statement: ${stmt}`); } return hasJsonFunction; } handleSequelizeMethod(smth, tableName, factory, options, prepend) { if (smth instanceof Utils.Json) { if (smth.conditions) { const conditions = this.parseConditionObject(smth.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`); return conditions.join(" AND "); } if (smth.path) { let str; if (this._checkValidJsonStatement(smth.path)) { str = smth.path; } else { const paths = _.toPath(smth.path); const column = paths.shift(); str = this.jsonPathExtractionQuery(column, paths); } if (smth.value) { str += util.format(" = %s", this.escape(smth.value)); } return str; } } return super.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend); } addColumnQuery(table, key, attribute) { const dbDataType = this.attributeToSQL(attribute, { context: "addColumn", table, key }); const dataType = attribute.type || attribute; const definition = this.dataTypeMapping(table, key, dbDataType); const quotedKey = this.quoteIdentifier(key); const quotedTable = this.quoteTable(this.extractTableDetails(table)); let query = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedKey} ${definition};`; if (dataType instanceof DataTypes.ENUM) { query = this.pgEnum(table, key, dataType) + query; } else if (dataType.type && dataType.type instanceof DataTypes.ENUM) { query = this.pgEnum(table, key, dataType.type) + query; } return query; } removeColumnQuery(tableName, attributeName) { const quotedTableName = this.quoteTable(this.extractTableDetails(tableName)); const quotedAttributeName = this.quoteIdentifier(attributeName); return `ALTER TABLE ${quotedTableName} DROP COLUMN ${quotedAttributeName};`; } changeColumnQuery(tableName, attributes) { const query = (subQuery) => `ALTER TABLE ${this.quoteTable(tableName)} ALTER COLUMN ${subQuery};`; const sql = []; for (const attributeName in attributes) { let definition = this.dataTypeMapping(tableName, attributeName, attributes[attributeName]); let attrSql = ""; if (definition.includes("NOT NULL")) { attrSql += query(`${this.quoteIdentifier(attributeName)} SET NOT NULL`); definition = definition.replace("NOT NULL", "").trim(); } else if (!definition.includes("REFERENCES")) { attrSql += query(`${this.quoteIdentifier(attributeName)} DROP NOT NULL`); } if (definition.includes("DEFAULT")) { attrSql += query(`${this.quoteIdentifier(attributeName)} SET DEFAULT ${definition.match(/DEFAULT ([^;]+)/)[1]}`); definition = definition.replace(/(DEFAULT[^;]+)/, "").trim(); } else if (!definition.includes("REFERENCES")) { attrSql += query(`${this.quoteIdentifier(attributeName)} DROP DEFAULT`); } if (attributes[attributeName].startsWith("ENUM(")) { attrSql += this.pgEnum(tableName, attributeName, attributes[attributeName]); definition = definition.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attributeName, { schema: false })); definition += ` USING (${this.quoteIdentifier(attributeName)}::${this.pgEnumName(tableName, attributeName)})`; } if (definition.match(/UNIQUE;*$/)) { definition = definition.replace(/UNIQUE;*$/, ""); attrSql += query(`ADD UNIQUE (${this.quoteIdentifier(attributeName)})`).replace("ALTER COLUMN", ""); } if (definition.includes("REFERENCES")) { definition = definition.replace(/.+?(?=REFERENCES)/, ""); attrSql += query(`ADD FOREIGN KEY (${this.quoteIdentifier(attributeName)}) ${definition}`).replace("ALTER COLUMN", ""); } else { attrSql += query(`${this.quoteIdentifier(attributeName)} TYPE ${definition}`); } sql.push(attrSql); } return sql.join(""); } renameColumnQuery(tableName, attrBefore, attributes) { const attrString = []; for (const attributeName in attributes) { attrString.push(`${this.quoteIdentifier(attrBefore)} TO ${this.quoteIdentifier(attributeName)}`); } return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(", ")};`; } fn(fnName, tableName, parameters, body, returns, language) { fnName = fnName || "testfunc"; language = language || "plpgsql"; returns = returns ? `RETURNS ${returns}` : ""; parameters = parameters || ""; return `CREATE OR REPLACE FUNCTION pg_temp.${fnName}(${parameters}) ${returns} AS $func$ BEGIN ${body} END; $func$ LANGUAGE ${language}; SELECT * FROM pg_temp.${fnName}();`; } truncateTableQuery(tableName, options = {}) { return [ `TRUNCATE ${this.quoteTable(tableName)}`, options.restartIdentity ? " RESTART IDENTITY" : "", options.cascade ? " CASCADE" : "" ].join(""); } deleteQuery(tableName, where, options = {}, model) { const table = this.quoteTable(tableName); let whereClause = this.getWhereConditions(where, null, model, options); const limit = options.limit ? ` LIMIT ${this.escape(options.limit)}` : ""; let primaryKeys = ""; let primaryKeysSelection = ""; if (whereClause) { whereClause = ` WHERE ${whereClause}`; } if (options.limit) { if (!model) { throw new Error("Cannot LIMIT delete without a model."); } const pks = Object.values(model.primaryKeys).map((pk) => this.quoteIdentifier(pk.field)).join(","); primaryKeys = model.primaryKeyAttributes.length > 1 ? `(${pks})` : pks; primaryKeysSelection = pks; return `DELETE FROM ${table} WHERE ${primaryKeys} IN (SELECT ${primaryKeysSelection} FROM ${table}${whereClause}${limit})`; } return `DELETE FROM ${table}${whereClause}`; } showIndexesQuery(tableName) { let schemaJoin = ""; let schemaWhere = ""; if (typeof tableName !== "string") { schemaJoin = ", pg_namespace s"; schemaWhere = ` AND s.oid = t.relnamespace AND s.nspname = '${tableName.schema}'`; tableName = tableName.tableName; } 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;`; } showConstraintsQuery(tableName) { return [ 'SELECT constraint_catalog AS "constraintCatalog",', 'constraint_schema AS "constraintSchema",', 'constraint_name AS "constraintName",', 'table_catalog AS "tableCatalog",', 'table_schema AS "tableSchema",', 'table_name AS "tableName",', 'constraint_type AS "constraintType",', 'is_deferrable AS "isDeferrable",', 'initially_deferred AS "initiallyDeferred"', "from INFORMATION_SCHEMA.table_constraints", `WHERE table_name='${tableName}';` ].join(" "); } removeIndexQuery(tableName, indexNameOrAttributes, options) { let indexName = indexNameOrAttributes; if (typeof indexName !== "string") { indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); } return [ "DROP INDEX", options && options.concurrently && "CONCURRENTLY", `IF EXISTS ${this.quoteIdentifiers(indexName)}` ].filter(Boolean).join(" "); } addLimitAndOffset(options) { let fragment = ""; if (options.limit != null) { fragment += " LIMIT " + this.escape(options.limit); } if (options.offset != null) { fragment += " OFFSET " + this.escape(options.offset); } return fragment; } attributeToSQL(attribute, options) { if (!_.isPlainObject(attribute)) { attribute = { type: attribute }; } let type; if (attribute.type instanceof DataTypes.ENUM || attribute.type instanceof DataTypes.ARRAY && attribute.type.type instanceof DataTypes.ENUM) { const enumType = attribute.type.type || attribute.type; let values = attribute.values; if (enumType.values && !attribute.values) { values = enumType.values; } if (Array.isArray(values) && values.length > 0) { type = `ENUM(${values.map((value) => this.escape(value)).join(", ")})`; if (attribute.type instanceof DataTypes.ARRAY) { type += "[]"; } } else { throw new Error("Values for ENUM haven't been defined."); } } if (!type) { type = attribute.type; } let sql = type.toString(); if (Object.prototype.hasOwnProperty.call(attribute, "allowNull") && !attribute.allowNull) { sql += " NOT NULL"; } if (attribute.autoIncrement) { if (attribute.autoIncrementIdentity) { sql += " GENERATED BY DEFAULT AS IDENTITY"; } else { sql += " SERIAL"; } } if (Utils.defaultValueSchemable(attribute.defaultValue)) { sql += ` DEFAULT ${this.escape(attribute.defaultValue, attribute)}`; } if (attribute.unique === true) { sql += " UNIQUE"; } if (attribute.primaryKey) { sql += " PRIMARY KEY"; } if (attribute.references) { let referencesTable = this.quoteTable(attribute.references.model); let schema; if (options.schema) { schema = options.schema; } else if ((!attribute.references.model || typeof attribute.references.model == "string") && options.table && options.table.schema) { schema = options.table.schema; } if (schema) { referencesTable = this.quoteTable(this.addSchema({ tableName: referencesTable, _schema: schema })); } let referencesKey; if (!options.withoutForeignKeyConstraints) { if (attribute.references.key) { referencesKey = this.quoteIdentifiers(attribute.references.key); } else { referencesKey = this.quoteIdentifier("id"); } sql += ` REFERENCES ${referencesTable} (${referencesKey})`; if (attribute.onDelete) { sql += ` ON DELETE ${attribute.onDelete.toUpperCase()}`; } if (attribute.onUpdate) { sql += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`; } if (attribute.references.deferrable) { sql += ` ${attribute.references.deferrable.toString(this)}`; } } } if (attribute.comment && typeof attribute.comment === "string") { if (options && ["addColumn", "changeColumn"].includes(options.context)) { const quotedAttr = this.quoteIdentifier(options.key); const escapedCommentText = this.escape(attribute.comment); sql += `; COMMENT ON COLUMN ${this.quoteTable(options.table)}.${quotedAttr} IS ${escapedCommentText}`; } else { sql += ` COMMENT ${attribute.comment}`; } } return sql; } deferConstraintsQuery(options) { return options.deferrable.toString(this); } setConstraintQuery(columns, type) { let columnFragment = "ALL"; if (columns) { columnFragment = columns.map((column) => this.quoteIdentifier(column)).join(", "); } return `SET CONSTRAINTS ${columnFragment} ${type}`; } setDeferredQuery(columns) { return this.setConstraintQuery(columns, "DEFERRED"); } setImmediateQuery(columns) { return this.setConstraintQuery(columns, "IMMEDIATE"); } attributesToSQL(attributes, options) { const result = {}; for (const key in attributes) { const attribute = attributes[key]; result[attribute.field || key] = this.attributeToSQL(attribute, __spreadValues({ key }, options)); } return result; } createTrigger(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) { const decodedEventType = this.decodeTriggerEventType(eventType); const eventSpec = this.expandTriggerEventSpec(fireOnSpec); const expandedOptions = this.expandOptions(optionsArray); const paramList = this._expandFunctionParamList(functionParams); return `CREATE ${this.triggerEventTypeIsConstraint(eventType)}TRIGGER ${this.quoteIdentifier(triggerName)} ${decodedEventType} ${eventSpec} ON ${this.quoteTable(tableName)}${expandedOptions ? ` ${expandedOptions}` : ""} EXECUTE PROCEDURE ${functionName}(${paramList});`; } dropTrigger(tableName, triggerName) { return `DROP TRIGGER ${this.quoteIdentifier(triggerName)} ON ${this.quoteTable(tableName)} RESTRICT;`; } renameTrigger(tableName, oldTriggerName, newTriggerName) { return `ALTER TRIGGER ${this.quoteIdentifier(oldTriggerName)} ON ${this.quoteTable(tableName)} RENAME TO ${this.quoteIdentifier(newTriggerName)};`; } createFunction(functionName, params, returnType, language, body, optionsArray, options) { if (!functionName || !returnType || !language || !body) throw new Error("createFunction missing some parameters. Did you pass functionName, returnType, language and body?"); const paramList = this._expandFunctionParamList(params); const variableList = options && options.variables ? this._expandFunctionVariableList(options.variables) : ""; const expandedOptionsArray = this.expandOptions(optionsArray); const statement = options && options.force ? "CREATE OR REPLACE FUNCTION" : "CREATE FUNCTION"; return `${statement} ${functionName}(${paramList}) RETURNS ${returnType} AS $func$ ${variableList} BEGIN ${body} END; $func$ language '${language}'${expandedOptionsArray};`; } dropFunction(functionName, params) { if (!functionName) throw new Error("requires functionName"); const paramList = this._expandFunctionParamList(params); return `DROP FUNCTION ${functionName}(${paramList}) RESTRICT;`; } renameFunction(oldFunctionName, params, newFunctionName) { const paramList = this._expandFunctionParamList(params); return `ALTER FUNCTION ${oldFunctionName}(${paramList}) RENAME TO ${newFunctionName};`; } pgEscapeAndQuote(val) { return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'")); } _expandFunctionParamList(params) { if (params === void 0 || !Array.isArray(params)) { throw new Error("_expandFunctionParamList: function parameters array required, including an empty one for no arguments"); } const paramList = []; params.forEach((curParam) => { const paramDef = []; if (curParam.type) { if (curParam.direction) { paramDef.push(curParam.direction); } if (curParam.name) { paramDef.push(curParam.name); } paramDef.push(curParam.type); } else { throw new Error("function or trigger used with a parameter without any type"); } const joined = paramDef.join(" "); if (joined) paramList.push(joined); }); return paramList.join(", "); } _expandFunctionVariableList(variables) { if (!Array.isArray(variables)) { throw new Error("_expandFunctionVariableList: function variables must be an array"); } const variableDefinitions = []; variables.forEach((variable) => { if (!variable.name || !variable.type) { throw new Error("function variable must have a name and type"); } let variableDefinition = `DECLARE ${variable.name} ${variable.type}`; if (variable.default) { variableDefinition += ` := ${variable.default}`; } variableDefinition += ";"; variableDefinitions.push(variableDefinition); }); return variableDefinitions.join(" "); } expandOptions(options) { return options === void 0 || _.isEmpty(options) ? "" : options.join(" "); } decodeTriggerEventType(eventSpecifier) { const EVENT_DECODER = { "after": "AFTER", "before": "BEFORE", "instead_of": "INSTEAD OF", "after_constraint": "AFTER" }; if (!EVENT_DECODER[eventSpecifier]) { throw new Error(`Invalid trigger event specified: ${eventSpecifier}`); } return EVENT_DECODER[eventSpecifier]; } triggerEventTypeIsConstraint(eventSpecifier) { return eventSpecifier === "after_constraint" ? "CONSTRAINT " : ""; } expandTriggerEventSpec(fireOnSpec) { if (_.isEmpty(fireOnSpec)) { throw new Error("no table change events specified to trigger on"); } return _.map(fireOnSpec, (fireValue, fireKey) => { const EVENT_MAP = { "insert": "INSERT", "update": "UPDATE", "delete": "DELETE", "truncate": "TRUNCATE" }; if (!EVENT_MAP[fireValue]) { throw new Error(`parseTriggerEventSpec: undefined trigger event ${fireKey}`); } let eventSpec = EVENT_MAP[fireValue]; if (eventSpec === "UPDATE") { if (Array.isArray(fireValue) && fireValue.length > 0) { eventSpec += ` OF ${fireValue.join(", ")}`; } } return eventSpec; }).join(" OR "); } pgEnumName(tableName, attr, options) { options = options || {}; const tableDetails = this.extractTableDetails(tableName, options); let enumName = Utils.addTicks(Utils.generateEnumName(tableDetails.tableName, attr), '"'); if (options.schema !== false && tableDetails.schema) { enumName = this.quoteIdentifier(tableDetails.schema) + tableDetails.delimiter + enumName; } return enumName; } pgListEnums(tableName, attrName, options) { let enumName = ""; const tableDetails = this.extractTableDetails(tableName, options); if (tableDetails.tableName && attrName) { enumName = ` AND t.typname=${this.pgEnumName(tableDetails.tableName, attrName, { schema: false }).replace(/"/g, "'")}`; } 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`; } pgEnum(tableName, attr, dataType, options) { const enumName = this.pgEnumName(tableName, attr, options); let values; if (dataType.values) { values = `ENUM(${dataType.values.map((value) => this.escape(value)).join(", ")})`; } else { values = dataType.toString().match(/^ENUM\(.+\)/)[0]; } let sql = `DO ${this.escape(`BEGIN CREATE TYPE ${enumName} AS ${values}; EXCEPTION WHEN duplicate_object THEN null; END`)};`; if (!!options && options.force === true) { sql = this.pgEnumDrop(tableName, attr) + sql; } return sql; } pgEnumAdd(tableName, attr, value, options) { const enumName = this.pgEnumName(tableName, attr); let sql = `ALTER TYPE ${enumName} ADD VALUE `; if (semver.gte(this.sequelize.options.databaseVersion, "9.3.0")) { sql += "IF NOT EXISTS "; } sql += this.escape(value); if (options.before) { sql += ` BEFORE ${this.escape(options.before)}`; } else if (options.after) { sql += ` AFTER ${this.escape(options.after)}`; } return sql; } pgEnumDrop(tableName, attr, enumName) { enumName = enumName || this.pgEnumName(tableName, attr); return `DROP TYPE IF EXISTS ${enumName}; `; } fromArray(text) { text = text.replace(/^{/, "").replace(/}$/, ""); let matches = text.match(/("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|\s*$)/ig); if (matches.length < 1) { return []; } matches = matches.map((m) => m.replace(/",$/, "").replace(/,$/, "").replace(/(^"|"$)/g, "")); return matches.slice(0, -1); } dataTypeMapping(tableName, attr, dataType) { if (dataType.includes("PRIMARY KEY")) { dataType = dataType.replace("PRIMARY KEY", ""); } if (dataType.includes("SERIAL")) { if (dataType.includes("BIGINT")) { dataType = dataType.replace("SERIAL", "BIGSERIAL"); dataType = dataType.replace("BIGINT", ""); } else if (dataType.includes("SMALLINT")) { dataType = dataType.replace("SERIAL", "SMALLSERIAL"); dataType = dataType.replace("SMALLINT", ""); } else { dataType = dataType.replace("INTEGER", ""); } dataType = dataType.replace("NOT NULL", ""); } if (dataType.startsWith("ENUM(")) { dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attr)); } return dataType; } getForeignKeysQuery(tableName) { 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;`; } _getForeignKeyReferencesQueryPrefix() { 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 "; } getForeignKeyReferencesQuery(tableName, catalogName, schemaName) { 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}'` : ""}`; } getForeignKeyReferenceQuery(table, columnName) { const tableName = table.tableName || table; const schema = table.schema; return `${this._getForeignKeyReferencesQueryPrefix()}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}' AND kcu.column_name = '${columnName}'${schema ? ` AND tc.table_schema = '${schema}'` : ""}`; } dropForeignKeyQuery(tableName, foreignKey) { return `ALTER TABLE ${this.quoteTable(tableName)} DROP CONSTRAINT ${this.quoteIdentifier(foreignKey)};`; } quoteIdentifier(identifier, force) { const optForceQuote = force || false; const optQuoteIdentifiers = this.options.quoteIdentifiers !== false; const rawIdentifier = Utils.removeTicks(identifier, '"'); if (optForceQuote === true || optQuoteIdentifiers !== false || identifier.includes(".") || identifier.includes("->") || POSTGRES_RESERVED_WORDS.includes(rawIdentifier.toLowerCase())) { return Utils.addTicks(rawIdentifier, '"'); } return rawIdentifier; } } module.exports = PostgresQueryGenerator; //# sourceMappingURL=query-generator.js.map