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.

541 lines (540 loc) 20.3 kB
"use strict"; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; 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; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); const _ = require("lodash"); const Utils = require("../../utils"); const AbstractQueryGenerator = require("../abstract/query-generator"); const util = require("util"); const Op = require("../../operators"); const JSON_FUNCTION_REGEX = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i; const JSON_OPERATOR_REGEX = /^\s*(->>?|@>|<@|\?[|&]?|\|{2}|#-)/i; const TOKEN_CAPTURE_REGEX = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i; const FOREIGN_KEY_FIELDS = [ "CONSTRAINT_NAME as constraint_name", "CONSTRAINT_NAME as constraintName", "CONSTRAINT_SCHEMA as constraintSchema", "CONSTRAINT_SCHEMA as constraintCatalog", "TABLE_NAME as tableName", "TABLE_SCHEMA as tableSchema", "TABLE_SCHEMA as tableCatalog", "COLUMN_NAME as columnName", "REFERENCED_TABLE_SCHEMA as referencedTableSchema", "REFERENCED_TABLE_SCHEMA as referencedTableCatalog", "REFERENCED_TABLE_NAME as referencedTableName", "REFERENCED_COLUMN_NAME as referencedColumnName" ].join(","); const SNOWFLAKE_RESERVED_WORDS = "account,all,alter,and,any,as,between,by,case,cast,check,column,connect,connections,constraint,create,cross,current,current_date,current_time,current_timestamp,current_user,database,delete,distinct,drop,else,exists,false,following,for,from,full,grant,group,gscluster,having,ilike,in,increment,inner,insert,intersect,into,is,issue,join,lateral,left,like,localtime,localtimestamp,minus,natural,not,null,of,on,or,order,organization,qualify,regexp,revoke,right,rlike,row,rows,sample,schema,select,set,some,start,table,tablesample,then,to,trigger,true,try_cast,union,unique,update,using,values,view,when,whenever,where,with".split(","); const typeWithoutDefault = /* @__PURE__ */ new Set(["BLOB", "TEXT", "GEOMETRY", "JSON"]); class SnowflakeQueryGenerator extends AbstractQueryGenerator { constructor(options) { super(options); this.OperatorMap = __spreadProps(__spreadValues({}, this.OperatorMap), { [Op.regexp]: "REGEXP", [Op.notRegexp]: "NOT REGEXP" }); } createDatabaseQuery(databaseName, options) { options = __spreadValues({ charset: null, collate: null }, options); return Utils.joinSQLFragments([ "CREATE DATABASE IF NOT EXISTS", this.quoteIdentifier(databaseName), options.charset && `DEFAULT CHARACTER SET ${this.escape(options.charset)}`, options.collate && `DEFAULT COLLATE ${this.escape(options.collate)}`, ";" ]); } dropDatabaseQuery(databaseName) { return `DROP DATABASE IF EXISTS ${this.quoteIdentifier(databaseName)};`; } createSchema() { return "SHOW TABLES"; } showSchemasQuery() { return "SHOW TABLES"; } versionQuery() { return "SELECT CURRENT_VERSION()"; } createTableQuery(tableName, attributes, options) { options = __spreadValues({ charset: null, rowFormat: null }, options); const primaryKeys = []; const foreignKeys = {}; const attrStr = []; for (const attr in attributes) { if (!Object.prototype.hasOwnProperty.call(attributes, attr)) continue; const dataType = attributes[attr]; let match; if (dataType.includes("PRIMARY KEY")) { primaryKeys.push(attr); if (dataType.includes("REFERENCES")) { match = dataType.match(/^(.+) (REFERENCES.*)$/); attrStr.push(`${this.quoteIdentifier(attr)} ${match[1].replace("PRIMARY KEY", "")}`); foreignKeys[attr] = match[2]; } else { attrStr.push(`${this.quoteIdentifier(attr)} ${dataType.replace("PRIMARY KEY", "")}`); } } else if (dataType.includes("REFERENCES")) { match = dataType.match(/^(.+) (REFERENCES.*)$/); attrStr.push(`${this.quoteIdentifier(attr)} ${match[1]}`); foreignKeys[attr] = match[2]; } else { attrStr.push(`${this.quoteIdentifier(attr)} ${dataType}`); } } const table = this.quoteTable(tableName); let attributesClause = attrStr.join(", "); const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", "); if (options.uniqueKeys) { _.each(options.uniqueKeys, (columns, indexName) => { if (columns.customIndex) { if (typeof indexName !== "string") { indexName = `uniq_${tableName}_${columns.fields.join("_")}`; } attributesClause += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`; } }); } if (pkString.length > 0) { attributesClause += `, PRIMARY KEY (${pkString})`; } for (const fkey in foreignKeys) { if (Object.prototype.hasOwnProperty.call(foreignKeys, fkey)) { attributesClause += `, FOREIGN KEY (${this.quoteIdentifier(fkey)}) ${foreignKeys[fkey]}`; } } return Utils.joinSQLFragments([ "CREATE TABLE IF NOT EXISTS", table, `(${attributesClause})`, options.comment && typeof options.comment === "string" && `COMMENT ${this.escape(options.comment)}`, options.charset && `DEFAULT CHARSET=${options.charset}`, options.collate && `COLLATE ${options.collate}`, options.rowFormat && `ROW_FORMAT=${options.rowFormat}`, ";" ]); } describeTableQuery(tableName, schema, schemaDelimiter) { const table = this.quoteTable(this.addSchema({ tableName, _schema: schema, _schemaDelimiter: schemaDelimiter })); return `SHOW FULL COLUMNS FROM ${table};`; } showTablesQuery(database) { return Utils.joinSQLFragments([ "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", database ? `AND TABLE_SCHEMA = ${this.escape(database)}` : "AND TABLE_SCHEMA NOT IN ( 'INFORMATION_SCHEMA', 'PERFORMANCE_SCHEMA', 'SYS')", ";" ]); } tableExistsQuery(table) { const tableName = table.tableName || table; const schema = table.schema; return Utils.joinSQLFragments([ "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", `AND TABLE_SCHEMA = ${schema !== void 0 ? this.escape(schema) : "CURRENT_SCHEMA()"}`, `AND TABLE_NAME = ${this.escape(tableName)}`, ";" ]); } addColumnQuery(table, key, dataType) { return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(table), "ADD", this.quoteIdentifier(key), this.attributeToSQL(dataType, { context: "addColumn", tableName: table, foreignKey: key }), ";" ]); } removeColumnQuery(tableName, attributeName) { return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "DROP", this.quoteIdentifier(attributeName), ";" ]); } changeColumnQuery(tableName, attributes) { const query = (...subQuerys) => Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "ALTER COLUMN", ...subQuerys, ";" ]); const sql = []; for (const attributeName in attributes) { let definition = this.dataTypeMapping(tableName, attributeName, attributes[attributeName]); const attrSql = []; if (definition.includes("NOT NULL")) { attrSql.push(query(this.quoteIdentifier(attributeName), "SET NOT NULL")); definition = definition.replace("NOT NULL", "").trim(); } else if (!definition.includes("REFERENCES")) { attrSql.push(query(this.quoteIdentifier(attributeName), "DROP NOT NULL")); } if (definition.includes("DEFAULT")) { attrSql.push(query(this.quoteIdentifier(attributeName), "SET DEFAULT", definition.match(/DEFAULT ([^;]+)/)[1])); definition = definition.replace(/(DEFAULT[^;]+)/, "").trim(); } else if (!definition.includes("REFERENCES")) { attrSql.push(query(this.quoteIdentifier(attributeName), "DROP DEFAULT")); } if (definition.match(/UNIQUE;*$/)) { definition = definition.replace(/UNIQUE;*$/, ""); attrSql.push(query("ADD UNIQUE (", this.quoteIdentifier(attributeName), ")").replace("ALTER COLUMN", "")); } if (definition.includes("REFERENCES")) { definition = definition.replace(/.+?(?=REFERENCES)/, ""); attrSql.push(query("ADD FOREIGN KEY (", this.quoteIdentifier(attributeName), ")", definition).replace("ALTER COLUMN", "")); } else { attrSql.push(query(this.quoteIdentifier(attributeName), "TYPE", definition)); } sql.push(attrSql.join("")); } return sql.join(""); } renameColumnQuery(tableName, attrBefore, attributes) { const attrString = []; for (const attrName in attributes) { const definition = attributes[attrName]; attrString.push(`'${attrBefore}' '${attrName}' ${definition}`); } return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "RENAME COLUMN", attrString.join(" to "), ";" ]); } handleSequelizeMethod(attr, tableName, factory, options, prepend) { if (attr instanceof Utils.Json) { if (attr.conditions) { const conditions = this.parseConditionObject(attr.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`); return conditions.join(" AND "); } if (attr.path) { let str; if (this._checkValidJsonStatement(attr.path)) { str = attr.path; } else { const paths = _.toPath(attr.path); const column = paths.shift(); str = this.jsonPathExtractionQuery(column, paths); } if (attr.value) { str += util.format(" = %s", this.escape(attr.value)); } return str; } } else if (attr instanceof Utils.Cast) { if (/timestamp/i.test(attr.type)) { attr.type = "datetime"; } else if (attr.json && /boolean/i.test(attr.type)) { attr.type = "char"; } else if (/double precision/i.test(attr.type) || /boolean/i.test(attr.type) || /integer/i.test(attr.type)) { attr.type = "decimal"; } else if (/text/i.test(attr.type)) { attr.type = "char"; } } return super.handleSequelizeMethod(attr, tableName, factory, options, prepend); } truncateTableQuery(tableName) { return Utils.joinSQLFragments([ "TRUNCATE", this.quoteTable(tableName) ]); } 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 (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 Utils.joinSQLFragments([ "DELETE FROM", table, "WHERE", primaryKeys, "IN (SELECT", primaryKeysSelection, "FROM", table, whereClause, limit, ")", ";" ]); } return Utils.joinSQLFragments([ "DELETE FROM", table, whereClause, ";" ]); } showIndexesQuery() { return "SELECT '' FROM DUAL"; } showConstraintsQuery(table, constraintName) { const tableName = table.tableName || table; const schemaName = table.schema; return Utils.joinSQLFragments([ "SELECT CONSTRAINT_CATALOG AS constraintCatalog,", "CONSTRAINT_NAME AS constraintName,", "CONSTRAINT_SCHEMA AS constraintSchema,", "CONSTRAINT_TYPE AS constraintType,", "TABLE_NAME AS tableName,", "TABLE_SCHEMA AS tableSchema", "from INFORMATION_SCHEMA.TABLE_CONSTRAINTS", `WHERE table_name='${tableName}'`, constraintName && `AND constraint_name = '${constraintName}'`, schemaName && `AND TABLE_SCHEMA = '${schemaName}'`, ";" ]); } removeIndexQuery(tableName, indexNameOrAttributes) { let indexName = indexNameOrAttributes; if (typeof indexName !== "string") { indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); } return Utils.joinSQLFragments([ "DROP INDEX", this.quoteIdentifier(indexName), "ON", this.quoteTable(tableName), ";" ]); } attributeToSQL(attribute, options) { if (!_.isPlainObject(attribute)) { attribute = { type: attribute }; } const attributeString = attribute.type.toString({ escape: this.escape.bind(this) }); let template = attributeString; if (attribute.allowNull === false) { template += " NOT NULL"; } if (attribute.autoIncrement) { template += " AUTOINCREMENT"; } if (!typeWithoutDefault.has(attributeString) && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) { template += ` DEFAULT ${this.escape(attribute.defaultValue)}`; } if (attribute.unique === true) { template += " UNIQUE"; } if (attribute.primaryKey) { template += " PRIMARY KEY"; } if (attribute.comment) { template += ` COMMENT ${this.escape(attribute.comment)}`; } if (attribute.first) { template += " FIRST"; } if (attribute.after) { template += ` AFTER ${this.quoteIdentifier(attribute.after)}`; } if (attribute.references) { if (options && options.context === "addColumn" && options.foreignKey) { const attrName = this.quoteIdentifier(options.foreignKey); const fkName = this.quoteIdentifier(`${options.tableName}_${attrName}_foreign_idx`); template += `, ADD CONSTRAINT ${fkName} FOREIGN KEY (${attrName})`; } template += ` REFERENCES ${this.quoteTable(attribute.references.model)}`; if (attribute.references.key) { template += ` (${this.quoteIdentifier(attribute.references.key)})`; } else { template += ` (${this.quoteIdentifier("id")})`; } if (attribute.onDelete) { template += ` ON DELETE ${attribute.onDelete.toUpperCase()}`; } if (attribute.onUpdate) { template += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`; } } return template; } attributesToSQL(attributes, options) { const result = {}; for (const key in attributes) { const attribute = attributes[key]; result[attribute.field || key] = this.attributeToSQL(attribute, options); } return result; } _checkValidJsonStatement(stmt) { if (typeof stmt !== "string") { return false; } 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 = JSON_FUNCTION_REGEX.exec(string); if (functionMatches) { currentIndex += functionMatches[0].indexOf("("); hasJsonFunction = true; continue; } const operatorMatches = JSON_OPERATOR_REGEX.exec(string); if (operatorMatches) { currentIndex += operatorMatches[0].length; hasJsonFunction = true; continue; } const tokenMatches = TOKEN_CAPTURE_REGEX.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; } if (hasJsonFunction && (hasInvalidToken || openingBrackets !== closingBrackets)) { throw new Error(`Invalid json statement: ${stmt}`); } return hasJsonFunction; } 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", ""); } return dataType; } getForeignKeysQuery(table, schemaName) { const tableName = table.tableName || table; return Utils.joinSQLFragments([ "SELECT", FOREIGN_KEY_FIELDS, `FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '${tableName}'`, `AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='${schemaName}'`, "AND REFERENCED_TABLE_NAME IS NOT NULL", ";" ]); } getForeignKeyQuery(table, columnName) { const quotedSchemaName = table.schema ? wrapSingleQuote(table.schema) : ""; const quotedTableName = wrapSingleQuote(table.tableName || table); const quotedColumnName = wrapSingleQuote(columnName); return Utils.joinSQLFragments([ "SELECT", FOREIGN_KEY_FIELDS, "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE", "WHERE (", [ `REFERENCED_TABLE_NAME = ${quotedTableName}`, table.schema && `AND REFERENCED_TABLE_SCHEMA = ${quotedSchemaName}`, `AND REFERENCED_COLUMN_NAME = ${quotedColumnName}` ], ") OR (", [ `TABLE_NAME = ${quotedTableName}`, table.schema && `AND TABLE_SCHEMA = ${quotedSchemaName}`, `AND COLUMN_NAME = ${quotedColumnName}`, "AND REFERENCED_TABLE_NAME IS NOT NULL" ], ")" ]); } dropForeignKeyQuery(tableName, foreignKey) { return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "DROP FOREIGN KEY", this.quoteIdentifier(foreignKey), ";" ]); } addLimitAndOffset(options) { let fragment = []; if (options.offset !== null && options.offset !== void 0 && options.offset !== 0) { fragment = fragment.concat([" LIMIT ", this.escape(options.limit), " OFFSET ", this.escape(options.offset)]); } else if (options.limit !== null && options.limit !== void 0) { fragment = [" LIMIT ", this.escape(options.limit)]; } return fragment.join(""); } 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("->") || SNOWFLAKE_RESERVED_WORDS.includes(rawIdentifier.toLowerCase())) { return Utils.addTicks(rawIdentifier, '"'); } return rawIdentifier; } } function wrapSingleQuote(identifier) { return Utils.addTicks(identifier, "'"); } module.exports = SnowflakeQueryGenerator; //# sourceMappingURL=query-generator.js.map