"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 _ = require("lodash"); const Utils = require("../../utils"); const DataTypes = require("../../data-types"); const TableHints = require("../../table-hints"); const AbstractQueryGenerator = require("../abstract/query-generator"); const randomBytes = require("crypto").randomBytes; const semver = require("semver"); const Op = require("../../operators"); const throwMethodUndefined = function(methodName) { throw new Error(`The method "${methodName}" is not defined! Please add it to your sql dialect.`); }; class MSSQLQueryGenerator extends AbstractQueryGenerator { createDatabaseQuery(databaseName, options) { options = __spreadValues({ collate: null }, options); const collation = options.collate ? `COLLATE ${this.escape(options.collate)}` : ""; return [ "IF NOT EXISTS (SELECT * FROM sys.databases WHERE name =", wrapSingleQuote(databaseName), ")", "BEGIN", "CREATE DATABASE", this.quoteIdentifier(databaseName), `${collation};`, "END;" ].join(" "); } dropDatabaseQuery(databaseName) { return [ "IF EXISTS (SELECT * FROM sys.databases WHERE name =", wrapSingleQuote(databaseName), ")", "BEGIN", "DROP DATABASE", this.quoteIdentifier(databaseName), ";", "END;" ].join(" "); } createSchema(schema) { return [ "IF NOT EXISTS (SELECT schema_name", "FROM information_schema.schemata", "WHERE schema_name =", wrapSingleQuote(schema), ")", "BEGIN", "EXEC sp_executesql N'CREATE SCHEMA", this.quoteIdentifier(schema), ";'", "END;" ].join(" "); } dropSchema(schema) { const quotedSchema = wrapSingleQuote(schema); return [ "IF EXISTS (SELECT schema_name", "FROM information_schema.schemata", "WHERE schema_name =", quotedSchema, ")", "BEGIN", "DECLARE @id INT, @ms_sql NVARCHAR(2000);", "DECLARE @cascade TABLE (", "id INT NOT NULL IDENTITY PRIMARY KEY,", "ms_sql NVARCHAR(2000) NOT NULL );", "INSERT INTO @cascade ( ms_sql )", "SELECT CASE WHEN o.type IN ('F','PK')", "THEN N'ALTER TABLE ['+ s.name + N'].[' + p.name + N'] DROP CONSTRAINT [' + o.name + N']'", "ELSE N'DROP TABLE ['+ s.name + N'].[' + o.name + N']' END", "FROM sys.objects o", "JOIN sys.schemas s on o.schema_id = s.schema_id", "LEFT OUTER JOIN sys.objects p on o.parent_object_id = p.object_id", "WHERE o.type IN ('F', 'PK', 'U') AND s.name = ", quotedSchema, "ORDER BY o.type ASC;", "SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id;", "WHILE @id IS NOT NULL", "BEGIN", "BEGIN TRY EXEC sp_executesql @ms_sql; END TRY", "BEGIN CATCH BREAK; THROW; END CATCH;", "DELETE FROM @cascade WHERE id = @id;", "SELECT @id = NULL, @ms_sql = NULL;", "SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id;", "END", "EXEC sp_executesql N'DROP SCHEMA", this.quoteIdentifier(schema), ";'", "END;" ].join(" "); } showSchemasQuery() { return [ 'SELECT "name" as "schema_name" FROM sys.schemas as s', 'WHERE "s"."name" NOT IN (', "'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'", ")", "AND", '"s"."name" NOT LIKE', "'db_%'" ].join(" "); } versionQuery() { return [ "DECLARE @ms_ver NVARCHAR(20);", "SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion')));", "SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'" ].join(" "); } createTableQuery(tableName, attributes, options) { const primaryKeys = [], foreignKeys = {}, attributesClauseParts = []; let commentStr = ""; for (const attr in attributes) { if (Object.prototype.hasOwnProperty.call(attributes, attr)) { let dataType = attributes[attr]; let match; if (dataType.includes("COMMENT ")) { const commentMatch = dataType.match(/^(.+) (COMMENT.*)$/); const commentText = commentMatch[2].replace("COMMENT", "").trim(); commentStr += this.commentTemplate(commentText, tableName, attr); dataType = commentMatch[1]; } if (dataType.includes("PRIMARY KEY")) { primaryKeys.push(attr); if (dataType.includes("REFERENCES")) { match = dataType.match(/^(.+) (REFERENCES.*)$/); attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1].replace("PRIMARY KEY", "")}`); foreignKeys[attr] = match[2]; } else { attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${dataType.replace("PRIMARY KEY", "")}`); } } else if (dataType.includes("REFERENCES")) { match = dataType.match(/^(.+) (REFERENCES.*)$/); attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1]}`); foreignKeys[attr] = match[2]; } else { attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${dataType}`); } } } 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("_")}`; } attributesClauseParts.push(`CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`); } }); } if (pkString.length > 0) { attributesClauseParts.push(`PRIMARY KEY (${pkString})`); } for (const fkey in foreignKeys) { if (Object.prototype.hasOwnProperty.call(foreignKeys, fkey)) { attributesClauseParts.push(`FOREIGN KEY (${this.quoteIdentifier(fkey)}) ${foreignKeys[fkey]}`); } } const quotedTableName = this.quoteTable(tableName); return Utils.joinSQLFragments([ `IF OBJECT_ID('${quotedTableName}', 'U') IS NULL`, `CREATE TABLE ${quotedTableName} (${attributesClauseParts.join(", ")})`, ";", commentStr ]); } describeTableQuery(tableName, schema) { let sql = [ "SELECT", "c.COLUMN_NAME AS 'Name',", "c.DATA_TYPE AS 'Type',", "c.CHARACTER_MAXIMUM_LENGTH AS 'Length',", "c.IS_NULLABLE as 'IsNull',", "COLUMN_DEFAULT AS 'Default',", "pk.CONSTRAINT_TYPE AS 'Constraint',", "COLUMNPROPERTY(OBJECT_ID('[' + c.TABLE_SCHEMA + '].[' + c.TABLE_NAME + ']'), c.COLUMN_NAME, 'IsIdentity') as 'IsIdentity',", "CAST(prop.value AS NVARCHAR) AS 'Comment'", "FROM", "INFORMATION_SCHEMA.TABLES t", "INNER JOIN", "INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME AND t.TABLE_SCHEMA = c.TABLE_SCHEMA", "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 ", "INNER JOIN sys.columns AS sc", "ON sc.object_id = OBJECT_ID('[' + t.TABLE_SCHEMA + '].[' + t.TABLE_NAME + ']') AND sc.name = c.column_name", "LEFT JOIN sys.extended_properties prop ON prop.major_id = sc.object_id", "AND prop.minor_id = sc.column_id", "AND prop.name = 'MS_Description'", "WHERE t.TABLE_NAME =", wrapSingleQuote(tableName) ].join(" "); if (schema) { sql += `AND t.TABLE_SCHEMA =${wrapSingleQuote(schema)}`; } return sql; } renameTableQuery(before, after) { return `EXEC sp_rename ${this.quoteTable(before)}, ${this.quoteTable(after)};`; } showTablesQuery() { return "SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';"; } tableExistsQuery(table) { const tableName = table.tableName || table; const schemaName = table.schema || "dbo"; return `SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = ${this.escape(tableName)} AND TABLE_SCHEMA = ${this.escape(schemaName)}`; } dropTableQuery(tableName) { const quoteTbl = this.quoteTable(tableName); return Utils.joinSQLFragments([ `IF OBJECT_ID('${quoteTbl}', 'U') IS NOT NULL`, "DROP TABLE", quoteTbl, ";" ]); } addColumnQuery(table, key, dataType) { dataType.field = key; let commentStr = ""; if (dataType.comment && _.isString(dataType.comment)) { commentStr = this.commentTemplate(dataType.comment, table, key); delete dataType["comment"]; } return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(table), "ADD", this.quoteIdentifier(key), this.attributeToSQL(dataType, { context: "addColumn" }), ";", commentStr ]); } commentTemplate(comment, table, column) { return ` EXEC sp_addextendedproperty @name = N'MS_Description', @value = ${this.escape(comment)}, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = ${this.quoteIdentifier(table)}, @level2type = N'Column', @level2name = ${this.quoteIdentifier(column)};`; } removeColumnQuery(tableName, attributeName) { return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "DROP COLUMN", this.quoteIdentifier(attributeName), ";" ]); } changeColumnQuery(tableName, attributes) { const attrString = [], constraintString = []; let commentString = ""; for (const attributeName in attributes) { const quotedAttrName = this.quoteIdentifier(attributeName); let definition = attributes[attributeName]; if (definition.includes("COMMENT ")) { const commentMatch = definition.match(/^(.+) (COMMENT.*)$/); const commentText = commentMatch[2].replace("COMMENT", "").trim(); commentString += this.commentTemplate(commentText, tableName, attributeName); definition = commentMatch[1]; } if (definition.includes("REFERENCES")) { constraintString.push(`FOREIGN KEY (${quotedAttrName}) ${definition.replace(/.+?(?=REFERENCES)/, "")}`); } else { attrString.push(`${quotedAttrName} ${definition}`); } } return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), attrString.length && `ALTER COLUMN ${attrString.join(", ")}`, constraintString.length && `ADD ${constraintString.join(", ")}`, ";", commentString ]); } renameColumnQuery(tableName, attrBefore, attributes) { const newName = Object.keys(attributes)[0]; return Utils.joinSQLFragments([ "EXEC sp_rename", `'${this.quoteTable(tableName)}.${attrBefore}',`, `'${newName}',`, "'COLUMN'", ";" ]); } bulkInsertQuery(tableName, attrValueHashes, options, attributes) { const quotedTable = this.quoteTable(tableName); options = options || {}; attributes = attributes || {}; const tuples = []; const allAttributes = []; const allQueries = []; let needIdentityInsertWrapper = false, outputFragment = ""; if (options.returning) { const returnValues = this.generateReturnValues(attributes, options); outputFragment = returnValues.outputFragment; } const emptyQuery = `INSERT INTO ${quotedTable}${outputFragment} DEFAULT VALUES`; attrValueHashes.forEach((attrValueHash) => { const fields = Object.keys(attrValueHash); const firstAttr = attributes[fields[0]]; if (fields.length === 1 && firstAttr && firstAttr.autoIncrement && attrValueHash[fields[0]] === null) { allQueries.push(emptyQuery); return; } _.forOwn(attrValueHash, (value, key) => { if (value !== null && attributes[key] && attributes[key].autoIncrement) { needIdentityInsertWrapper = true; } if (!allAttributes.includes(key)) { if (value === null && attributes[key] && attributes[key].autoIncrement) return; allAttributes.push(key); } }); }); if (allAttributes.length > 0) { attrValueHashes.forEach((attrValueHash) => { tuples.push(`(${allAttributes.map((key) => this.escape(attrValueHash[key])).join(",")})`); }); const quotedAttributes = allAttributes.map((attr) => this.quoteIdentifier(attr)).join(","); allQueries.push((tupleStr) => `INSERT INTO ${quotedTable} (${quotedAttributes})${outputFragment} VALUES ${tupleStr};`); } const commands = []; let offset = 0; const batch = Math.floor(250 / (allAttributes.length + 1)) + 1; while (offset < Math.max(tuples.length, 1)) { const tupleStr = tuples.slice(offset, Math.min(tuples.length, offset + batch)); let generatedQuery = allQueries.map((v) => typeof v === "string" ? v : v(tupleStr)).join(";"); if (needIdentityInsertWrapper) { generatedQuery = `SET IDENTITY_INSERT ${quotedTable} ON; ${generatedQuery}; SET IDENTITY_INSERT ${quotedTable} OFF;`; } commands.push(generatedQuery); offset += batch; } return commands.join(";"); } updateQuery(tableName, attrValueHash, where, options, attributes) { const sql = super.updateQuery(tableName, attrValueHash, where, options, attributes); if (options.limit) { const updateArgs = `UPDATE TOP(${this.escape(options.limit)})`; sql.query = sql.query.replace("UPDATE", updateArgs); } return sql; } upsertQuery(tableName, insertValues, updateValues, where, model) { const targetTableAlias = this.quoteTable(`${tableName}_target`); const sourceTableAlias = this.quoteTable(`${tableName}_source`); const primaryKeysAttrs = []; const identityAttrs = []; const uniqueAttrs = []; const tableNameQuoted = this.quoteTable(tableName); let needIdentityInsertWrapper = false; for (const key in model.rawAttributes) { if (model.rawAttributes[key].primaryKey) { primaryKeysAttrs.push(model.rawAttributes[key].field || key); } if (model.rawAttributes[key].unique) { uniqueAttrs.push(model.rawAttributes[key].field || key); } if (model.rawAttributes[key].autoIncrement) { identityAttrs.push(model.rawAttributes[key].field || key); } } for (const index of model._indexes) { if (index.unique && index.fields) { for (const field of index.fields) { const fieldName = typeof field === "string" ? field : field.name || field.attribute; if (!uniqueAttrs.includes(fieldName) && model.rawAttributes[fieldName]) { uniqueAttrs.push(fieldName); } } } } const updateKeys = Object.keys(updateValues); const insertKeys = Object.keys(insertValues); const insertKeysQuoted = insertKeys.map((key) => this.quoteIdentifier(key)).join(", "); const insertValuesEscaped = insertKeys.map((key) => this.escape(insertValues[key])).join(", "); const sourceTableQuery = `VALUES(${insertValuesEscaped})`; let joinCondition; identityAttrs.forEach((key) => { if (insertValues[key] && insertValues[key] !== null) { needIdentityInsertWrapper = true; } }); const clauses = where[Op.or].filter((clause) => { let valid = true; for (const key in clause) { if (typeof clause[key] === "undefined" || clause[key] == null) { valid = false; break; } } return valid; }); const getJoinSnippet = (array) => { return array.map((key) => { key = this.quoteIdentifier(key); return `${targetTableAlias}.${key} = ${sourceTableAlias}.${key}`; }); }; if (clauses.length === 0) { throw new Error("Primary Key or Unique key should be passed to upsert query"); } else { for (const key in clauses) { const keys = Object.keys(clauses[key]); if (primaryKeysAttrs.includes(keys[0])) { joinCondition = getJoinSnippet(primaryKeysAttrs).join(" AND "); break; } } if (!joinCondition) { joinCondition = getJoinSnippet(uniqueAttrs).join(" AND "); } } const filteredUpdateClauses = updateKeys.filter((key) => !identityAttrs.includes(key)).map((key) => { const value = this.escape(updateValues[key]); key = this.quoteIdentifier(key); return `${targetTableAlias}.${key} = ${value}`; }); const updateSnippet = filteredUpdateClauses.length > 0 ? `WHEN MATCHED THEN UPDATE SET ${filteredUpdateClauses.join(", ")}` : ""; const insertSnippet = `(${insertKeysQuoted}) VALUES(${insertValuesEscaped})`; let query = `MERGE INTO ${tableNameQuoted} WITH(HOLDLOCK) AS ${targetTableAlias} USING (${sourceTableQuery}) AS ${sourceTableAlias}(${insertKeysQuoted}) ON ${joinCondition}`; query += ` ${updateSnippet} WHEN NOT MATCHED THEN INSERT ${insertSnippet} OUTPUT $action, INSERTED.*;`; if (needIdentityInsertWrapper) { query = `SET IDENTITY_INSERT ${tableNameQuoted} ON; ${query} SET IDENTITY_INSERT ${tableNameQuoted} OFF;`; } return query; } truncateTableQuery(tableName) { return `TRUNCATE TABLE ${this.quoteTable(tableName)}`; } deleteQuery(tableName, where, options = {}, model) { const table = this.quoteTable(tableName); const whereClause = this.getWhereConditions(where, null, model, options); return Utils.joinSQLFragments([ "DELETE", options.limit && `TOP(${this.escape(options.limit)})`, "FROM", table, whereClause && `WHERE ${whereClause}`, ";", "SELECT @@ROWCOUNT AS AFFECTEDROWS", ";" ]); } showIndexesQuery(tableName) { return `EXEC sys.sp_helpindex @objname = N'${this.quoteTable(tableName)}';`; } showConstraintsQuery(tableName) { return `EXEC sp_helpconstraint @objname = ${this.escape(this.quoteTable(tableName))};`; } removeIndexQuery(tableName, indexNameOrAttributes) { let indexName = indexNameOrAttributes; if (typeof indexName !== "string") { indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); } return `DROP INDEX ${this.quoteIdentifiers(indexName)} ON ${this.quoteIdentifiers(tableName)}`; } attributeToSQL(attribute, options) { if (!_.isPlainObject(attribute)) { attribute = { type: attribute }; } if (attribute.references) { if (attribute.Model && attribute.Model.tableName === attribute.references.model) { this.sequelize.log("MSSQL does not support self referencial constraints, we will remove it but we recommend restructuring your query"); attribute.onDelete = ""; attribute.onUpdate = ""; } } let template; if (attribute.type instanceof DataTypes.ENUM) { if (attribute.type.values && !attribute.values) attribute.values = attribute.type.values; template = attribute.type.toSql(); template += ` CHECK (${this.quoteIdentifier(attribute.field)} IN(${attribute.values.map((value) => { return this.escape(value); }).join(", ")}))`; return template; } template = attribute.type.toString(); if (attribute.allowNull === false) { template += " NOT NULL"; } else if (!attribute.primaryKey && !Utils.defaultValueSchemable(attribute.defaultValue)) { template += " NULL"; } if (attribute.autoIncrement) { template += " IDENTITY(1,1)"; } if (attribute.type !== "TEXT" && 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 ((!options || !options.withoutForeignKeyConstraints) && attribute.references) { 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()}`; } } if (attribute.comment && typeof attribute.comment === "string") { template += ` COMMENT ${attribute.comment}`; } return template; } attributesToSQL(attributes, options) { const result = {}, existingConstraints = []; let key, attribute; for (key in attributes) { attribute = attributes[key]; if (attribute.references) { if (existingConstraints.includes(attribute.references.model.toString())) { attribute.onDelete = ""; attribute.onUpdate = ""; } else { existingConstraints.push(attribute.references.model.toString()); attribute.onUpdate = ""; } } if (key && !attribute.field) attribute.field = key; result[attribute.field || key] = this.attributeToSQL(attribute, options); } return result; } createTrigger() { throwMethodUndefined("createTrigger"); } dropTrigger() { throwMethodUndefined("dropTrigger"); } renameTrigger() { throwMethodUndefined("renameTrigger"); } createFunction() { throwMethodUndefined("createFunction"); } dropFunction() { throwMethodUndefined("dropFunction"); } renameFunction() { throwMethodUndefined("renameFunction"); } _getForeignKeysQueryPrefix(catalogName) { return `${"SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, "}${catalogName ? `constraintCatalog = '${catalogName}', ` : ""}constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), ${catalogName ? `tableCatalog = '${catalogName}', ` : ""}columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), ${catalogName ? `referencedCatalog = '${catalogName}', ` : ""}referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM sys.foreign_key_columns FKC INNER JOIN sys.objects OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN sys.tables TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN sys.columns COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN sys.tables RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN sys.columns RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID`; } getForeignKeysQuery(table, catalogName) { const tableName = table.tableName || table; let sql = `${this._getForeignKeysQueryPrefix(catalogName)} WHERE TB.NAME =${wrapSingleQuote(tableName)}`; if (table.schema) { sql += ` AND SCHEMA_NAME(TB.SCHEMA_ID) =${wrapSingleQuote(table.schema)}`; } return sql; } getForeignKeyQuery(table, attributeName) { const tableName = table.tableName || table; return Utils.joinSQLFragments([ this._getForeignKeysQueryPrefix(), "WHERE", `TB.NAME =${wrapSingleQuote(tableName)}`, "AND", `COL.NAME =${wrapSingleQuote(attributeName)}`, table.schema && `AND SCHEMA_NAME(TB.SCHEMA_ID) =${wrapSingleQuote(table.schema)}` ]); } getPrimaryKeyConstraintQuery(table, attributeName) { const tableName = wrapSingleQuote(table.tableName || table); return Utils.joinSQLFragments([ "SELECT K.TABLE_NAME AS tableName,", "K.COLUMN_NAME AS columnName,", "K.CONSTRAINT_NAME AS constraintName", "FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS C", "JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS K", "ON C.TABLE_NAME = K.TABLE_NAME", "AND C.CONSTRAINT_CATALOG = K.CONSTRAINT_CATALOG", "AND C.CONSTRAINT_SCHEMA = K.CONSTRAINT_SCHEMA", "AND C.CONSTRAINT_NAME = K.CONSTRAINT_NAME", "WHERE C.CONSTRAINT_TYPE = 'PRIMARY KEY'", `AND K.COLUMN_NAME = ${wrapSingleQuote(attributeName)}`, `AND K.TABLE_NAME = ${tableName}`, ";" ]); } dropForeignKeyQuery(tableName, foreignKey) { return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "DROP", this.quoteIdentifier(foreignKey) ]); } getDefaultConstraintQuery(tableName, attributeName) { const quotedTable = this.quoteTable(tableName); return Utils.joinSQLFragments([ "SELECT name FROM sys.default_constraints", `WHERE PARENT_OBJECT_ID = OBJECT_ID('${quotedTable}', 'U')`, `AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('${attributeName}')`, `AND object_id = OBJECT_ID('${quotedTable}', 'U'))`, ";" ]); } dropConstraintQuery(tableName, constraintName) { return Utils.joinSQLFragments([ "ALTER TABLE", this.quoteTable(tableName), "DROP CONSTRAINT", this.quoteIdentifier(constraintName), ";" ]); } setIsolationLevelQuery() { } generateTransactionId() { return randomBytes(10).toString("hex"); } startTransactionQuery(transaction) { if (transaction.parent) { return `SAVE TRANSACTION ${this.quoteIdentifier(transaction.name)};`; } return "BEGIN TRANSACTION;"; } commitTransactionQuery(transaction) { if (transaction.parent) { return; } return "COMMIT TRANSACTION;"; } rollbackTransactionQuery(transaction) { if (transaction.parent) { return `ROLLBACK TRANSACTION ${this.quoteIdentifier(transaction.name)};`; } return "ROLLBACK TRANSACTION;"; } selectFromTableFragment(options, model, attributes, tables, mainTableAs, where) { this._throwOnEmptyAttributes(attributes, { modelName: model && model.name, as: mainTableAs }); const dbVersion = this.sequelize.options.databaseVersion; const isSQLServer2008 = semver.valid(dbVersion) && semver.lt(dbVersion, "11.0.0"); if (isSQLServer2008 && options.offset) { const offset = options.offset || 0; const isSubQuery = options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation; let orders = { mainQueryOrder: [] }; if (options.order) { orders = this.getQueryOrders(options, model, isSubQuery); } if (orders.mainQueryOrder.length === 0) { orders.mainQueryOrder.push(this.quoteIdentifier(model.primaryKeyField)); } const tmpTable = mainTableAs || "OffsetTable"; if (options.include) { const subQuery = options.subQuery === void 0 ? options.limit && options.hasMultiAssociation : options.subQuery; const mainTable = { name: mainTableAs, quotedName: null, as: null, model }; const topLevelInfo = { names: mainTable, options, subQuery }; let mainJoinQueries = []; for (const include of options.include) { if (include.separate) { continue; } const joinQueries = this.generateInclude(include, { externalAs: mainTableAs, internalAs: mainTableAs }, topLevelInfo); mainJoinQueries = mainJoinQueries.concat(joinQueries.mainQuery); } return Utils.joinSQLFragments([ "SELECT TOP 100 PERCENT", attributes.join(", "), "FROM (", [ "SELECT", options.limit && `TOP ${options.limit}`, "* FROM (", [ "SELECT ROW_NUMBER() OVER (", [ "ORDER BY", orders.mainQueryOrder.join(", ") ], `) as row_num, ${tmpTable}.* FROM (`, [ "SELECT DISTINCT", `${tmpTable}.* FROM ${tables} AS ${tmpTable}`, mainJoinQueries, where && `WHERE ${where}` ], `) AS ${tmpTable}` ], `) AS ${tmpTable} WHERE row_num > ${offset}` ], `) AS ${tmpTable}` ]); } return Utils.joinSQLFragments([ "SELECT TOP 100 PERCENT", attributes.join(", "), "FROM (", [ "SELECT", options.limit && `TOP ${options.limit}`, "* FROM (", [ "SELECT ROW_NUMBER() OVER (", [ "ORDER BY", orders.mainQueryOrder.join(", ") ], `) as row_num, * FROM ${tables} AS ${tmpTable}`, where && `WHERE ${where}` ], `) AS ${tmpTable} WHERE row_num > ${offset}` ], `) AS ${tmpTable}` ]); } return Utils.joinSQLFragments([ "SELECT", isSQLServer2008 && options.limit && `TOP ${options.limit}`, attributes.join(", "), `FROM ${tables}`, mainTableAs && `AS ${mainTableAs}`, options.tableHint && TableHints[options.tableHint] && `WITH (${TableHints[options.tableHint]})` ]); } addLimitAndOffset(options, model) { if (semver.valid(this.sequelize.options.databaseVersion) && semver.lt(this.sequelize.options.databaseVersion, "11.0.0")) { return ""; } const offset = options.offset || 0; const isSubQuery = options.subQuery === void 0 ? options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation : options.subQuery; let fragment = ""; let orders = {}; if (options.order) { orders = this.getQueryOrders(options, model, isSubQuery); } if (options.limit || options.offset) { if (!options.order || options.order.length === 0 || options.include && orders.subQueryOrder.length === 0) { let primaryKey = model.primaryKeyField; const tablePkFragment = `${this.quoteTable(options.tableAs || model.name)}.${this.quoteIdentifier(primaryKey)}`; const aliasedAttribute = (options.attributes || []).find((attr) => Array.isArray(attr) && attr[1] && (attr[0] === primaryKey || attr[1] === primaryKey)); if (aliasedAttribute) { const modelName = this.quoteIdentifier(options.tableAs || model.name); const alias = this._getAliasForField(modelName, aliasedAttribute[1], options); primaryKey = new Utils.Col(alias || aliasedAttribute[1]); } if (!options.order || !options.order.length) { fragment += ` ORDER BY ${tablePkFragment}`; } else { const orderFieldNames = (options.order || []).map((order) => { const value = Array.isArray(order) ? order[0] : order; if (value instanceof Utils.Col) { return value.col; } if (value instanceof Utils.Literal) { return value.val; } return value; }); const primaryKeyFieldAlreadyPresent = orderFieldNames.some((fieldName) => fieldName === (primaryKey.col || primaryKey)); if (!primaryKeyFieldAlreadyPresent) { fragment += options.order && !isSubQuery ? ", " : " ORDER BY "; fragment += tablePkFragment; } } } if (options.offset || options.limit) { fragment += ` OFFSET ${this.escape(offset)} ROWS`; } if (options.limit) { fragment += ` FETCH NEXT ${this.escape(options.limit)} ROWS ONLY`; } } return fragment; } booleanValue(value) { return value ? 1 : 0; } quoteIdentifier(identifier, force) { return `[${identifier.replace(/[[\]']+/g, "")}]`; } } function wrapSingleQuote(identifier) { return Utils.addTicks(Utils.removeTicks(identifier, "'"), "'"); } module.exports = MSSQLQueryGenerator; //# sourceMappingURL=query-generator.js.map