query-generator.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  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 _ = require("lodash");
  19. const Utils = require("../../utils");
  20. const DataTypes = require("../../data-types");
  21. const TableHints = require("../../table-hints");
  22. const AbstractQueryGenerator = require("../abstract/query-generator");
  23. const randomBytes = require("crypto").randomBytes;
  24. const semver = require("semver");
  25. const Op = require("../../operators");
  26. const throwMethodUndefined = function(methodName) {
  27. throw new Error(`The method "${methodName}" is not defined! Please add it to your sql dialect.`);
  28. };
  29. class MSSQLQueryGenerator extends AbstractQueryGenerator {
  30. createDatabaseQuery(databaseName, options) {
  31. options = __spreadValues({ collate: null }, options);
  32. const collation = options.collate ? `COLLATE ${this.escape(options.collate)}` : "";
  33. return [
  34. "IF NOT EXISTS (SELECT * FROM sys.databases WHERE name =",
  35. wrapSingleQuote(databaseName),
  36. ")",
  37. "BEGIN",
  38. "CREATE DATABASE",
  39. this.quoteIdentifier(databaseName),
  40. `${collation};`,
  41. "END;"
  42. ].join(" ");
  43. }
  44. dropDatabaseQuery(databaseName) {
  45. return [
  46. "IF EXISTS (SELECT * FROM sys.databases WHERE name =",
  47. wrapSingleQuote(databaseName),
  48. ")",
  49. "BEGIN",
  50. "DROP DATABASE",
  51. this.quoteIdentifier(databaseName),
  52. ";",
  53. "END;"
  54. ].join(" ");
  55. }
  56. createSchema(schema) {
  57. return [
  58. "IF NOT EXISTS (SELECT schema_name",
  59. "FROM information_schema.schemata",
  60. "WHERE schema_name =",
  61. wrapSingleQuote(schema),
  62. ")",
  63. "BEGIN",
  64. "EXEC sp_executesql N'CREATE SCHEMA",
  65. this.quoteIdentifier(schema),
  66. ";'",
  67. "END;"
  68. ].join(" ");
  69. }
  70. dropSchema(schema) {
  71. const quotedSchema = wrapSingleQuote(schema);
  72. return [
  73. "IF EXISTS (SELECT schema_name",
  74. "FROM information_schema.schemata",
  75. "WHERE schema_name =",
  76. quotedSchema,
  77. ")",
  78. "BEGIN",
  79. "DECLARE @id INT, @ms_sql NVARCHAR(2000);",
  80. "DECLARE @cascade TABLE (",
  81. "id INT NOT NULL IDENTITY PRIMARY KEY,",
  82. "ms_sql NVARCHAR(2000) NOT NULL );",
  83. "INSERT INTO @cascade ( ms_sql )",
  84. "SELECT CASE WHEN o.type IN ('F','PK')",
  85. "THEN N'ALTER TABLE ['+ s.name + N'].[' + p.name + N'] DROP CONSTRAINT [' + o.name + N']'",
  86. "ELSE N'DROP TABLE ['+ s.name + N'].[' + o.name + N']' END",
  87. "FROM sys.objects o",
  88. "JOIN sys.schemas s on o.schema_id = s.schema_id",
  89. "LEFT OUTER JOIN sys.objects p on o.parent_object_id = p.object_id",
  90. "WHERE o.type IN ('F', 'PK', 'U') AND s.name = ",
  91. quotedSchema,
  92. "ORDER BY o.type ASC;",
  93. "SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id;",
  94. "WHILE @id IS NOT NULL",
  95. "BEGIN",
  96. "BEGIN TRY EXEC sp_executesql @ms_sql; END TRY",
  97. "BEGIN CATCH BREAK; THROW; END CATCH;",
  98. "DELETE FROM @cascade WHERE id = @id;",
  99. "SELECT @id = NULL, @ms_sql = NULL;",
  100. "SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id;",
  101. "END",
  102. "EXEC sp_executesql N'DROP SCHEMA",
  103. this.quoteIdentifier(schema),
  104. ";'",
  105. "END;"
  106. ].join(" ");
  107. }
  108. showSchemasQuery() {
  109. return [
  110. 'SELECT "name" as "schema_name" FROM sys.schemas as s',
  111. 'WHERE "s"."name" NOT IN (',
  112. "'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'",
  113. ")",
  114. "AND",
  115. '"s"."name" NOT LIKE',
  116. "'db_%'"
  117. ].join(" ");
  118. }
  119. versionQuery() {
  120. return [
  121. "DECLARE @ms_ver NVARCHAR(20);",
  122. "SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion')));",
  123. "SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'"
  124. ].join(" ");
  125. }
  126. createTableQuery(tableName, attributes, options) {
  127. const primaryKeys = [], foreignKeys = {}, attributesClauseParts = [];
  128. let commentStr = "";
  129. for (const attr in attributes) {
  130. if (Object.prototype.hasOwnProperty.call(attributes, attr)) {
  131. let dataType = attributes[attr];
  132. let match;
  133. if (dataType.includes("COMMENT ")) {
  134. const commentMatch = dataType.match(/^(.+) (COMMENT.*)$/);
  135. const commentText = commentMatch[2].replace("COMMENT", "").trim();
  136. commentStr += this.commentTemplate(commentText, tableName, attr);
  137. dataType = commentMatch[1];
  138. }
  139. if (dataType.includes("PRIMARY KEY")) {
  140. primaryKeys.push(attr);
  141. if (dataType.includes("REFERENCES")) {
  142. match = dataType.match(/^(.+) (REFERENCES.*)$/);
  143. attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1].replace("PRIMARY KEY", "")}`);
  144. foreignKeys[attr] = match[2];
  145. } else {
  146. attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${dataType.replace("PRIMARY KEY", "")}`);
  147. }
  148. } else if (dataType.includes("REFERENCES")) {
  149. match = dataType.match(/^(.+) (REFERENCES.*)$/);
  150. attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1]}`);
  151. foreignKeys[attr] = match[2];
  152. } else {
  153. attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${dataType}`);
  154. }
  155. }
  156. }
  157. const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", ");
  158. if (options.uniqueKeys) {
  159. _.each(options.uniqueKeys, (columns, indexName) => {
  160. if (columns.customIndex) {
  161. if (typeof indexName !== "string") {
  162. indexName = `uniq_${tableName}_${columns.fields.join("_")}`;
  163. }
  164. attributesClauseParts.push(`CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`);
  165. }
  166. });
  167. }
  168. if (pkString.length > 0) {
  169. attributesClauseParts.push(`PRIMARY KEY (${pkString})`);
  170. }
  171. for (const fkey in foreignKeys) {
  172. if (Object.prototype.hasOwnProperty.call(foreignKeys, fkey)) {
  173. attributesClauseParts.push(`FOREIGN KEY (${this.quoteIdentifier(fkey)}) ${foreignKeys[fkey]}`);
  174. }
  175. }
  176. const quotedTableName = this.quoteTable(tableName);
  177. return Utils.joinSQLFragments([
  178. `IF OBJECT_ID('${quotedTableName}', 'U') IS NULL`,
  179. `CREATE TABLE ${quotedTableName} (${attributesClauseParts.join(", ")})`,
  180. ";",
  181. commentStr
  182. ]);
  183. }
  184. describeTableQuery(tableName, schema) {
  185. let sql = [
  186. "SELECT",
  187. "c.COLUMN_NAME AS 'Name',",
  188. "c.DATA_TYPE AS 'Type',",
  189. "c.CHARACTER_MAXIMUM_LENGTH AS 'Length',",
  190. "c.IS_NULLABLE as 'IsNull',",
  191. "COLUMN_DEFAULT AS 'Default',",
  192. "pk.CONSTRAINT_TYPE AS 'Constraint',",
  193. "COLUMNPROPERTY(OBJECT_ID(c.TABLE_SCHEMA+'.'+c.TABLE_NAME), c.COLUMN_NAME, 'IsIdentity') as 'IsIdentity',",
  194. "CAST(prop.value AS NVARCHAR) AS 'Comment'",
  195. "FROM",
  196. "INFORMATION_SCHEMA.TABLES t",
  197. "INNER JOIN",
  198. "INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME AND t.TABLE_SCHEMA = c.TABLE_SCHEMA",
  199. "LEFT JOIN (SELECT tc.table_schema, tc.table_name, ",
  200. "cu.column_name, tc.CONSTRAINT_TYPE ",
  201. "FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc ",
  202. "JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu ",
  203. "ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name ",
  204. "and tc.constraint_name=cu.constraint_name ",
  205. "and tc.CONSTRAINT_TYPE='PRIMARY KEY') pk ",
  206. "ON pk.table_schema=c.table_schema ",
  207. "AND pk.table_name=c.table_name ",
  208. "AND pk.column_name=c.column_name ",
  209. "INNER JOIN sys.columns AS sc",
  210. "ON sc.object_id = object_id(t.table_schema + '.' + t.table_name) AND sc.name = c.column_name",
  211. "LEFT JOIN sys.extended_properties prop ON prop.major_id = sc.object_id",
  212. "AND prop.minor_id = sc.column_id",
  213. "AND prop.name = 'MS_Description'",
  214. "WHERE t.TABLE_NAME =",
  215. wrapSingleQuote(tableName)
  216. ].join(" ");
  217. if (schema) {
  218. sql += `AND t.TABLE_SCHEMA =${wrapSingleQuote(schema)}`;
  219. }
  220. return sql;
  221. }
  222. renameTableQuery(before, after) {
  223. return `EXEC sp_rename ${this.quoteTable(before)}, ${this.quoteTable(after)};`;
  224. }
  225. showTablesQuery() {
  226. return "SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';";
  227. }
  228. tableExistsQuery(table) {
  229. const tableName = table.tableName || table;
  230. const schemaName = table.schema || "dbo";
  231. 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)}`;
  232. }
  233. dropTableQuery(tableName) {
  234. const quoteTbl = this.quoteTable(tableName);
  235. return Utils.joinSQLFragments([
  236. `IF OBJECT_ID('${quoteTbl}', 'U') IS NOT NULL`,
  237. "DROP TABLE",
  238. quoteTbl,
  239. ";"
  240. ]);
  241. }
  242. addColumnQuery(table, key, dataType) {
  243. dataType.field = key;
  244. let commentStr = "";
  245. if (dataType.comment && _.isString(dataType.comment)) {
  246. commentStr = this.commentTemplate(dataType.comment, table, key);
  247. delete dataType["comment"];
  248. }
  249. return Utils.joinSQLFragments([
  250. "ALTER TABLE",
  251. this.quoteTable(table),
  252. "ADD",
  253. this.quoteIdentifier(key),
  254. this.attributeToSQL(dataType, { context: "addColumn" }),
  255. ";",
  256. commentStr
  257. ]);
  258. }
  259. commentTemplate(comment, table, column) {
  260. 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)};`;
  261. }
  262. removeColumnQuery(tableName, attributeName) {
  263. return Utils.joinSQLFragments([
  264. "ALTER TABLE",
  265. this.quoteTable(tableName),
  266. "DROP COLUMN",
  267. this.quoteIdentifier(attributeName),
  268. ";"
  269. ]);
  270. }
  271. changeColumnQuery(tableName, attributes) {
  272. const attrString = [], constraintString = [];
  273. let commentString = "";
  274. for (const attributeName in attributes) {
  275. const quotedAttrName = this.quoteIdentifier(attributeName);
  276. let definition = attributes[attributeName];
  277. if (definition.includes("COMMENT ")) {
  278. const commentMatch = definition.match(/^(.+) (COMMENT.*)$/);
  279. const commentText = commentMatch[2].replace("COMMENT", "").trim();
  280. commentString += this.commentTemplate(commentText, tableName, attributeName);
  281. definition = commentMatch[1];
  282. }
  283. if (definition.includes("REFERENCES")) {
  284. constraintString.push(`FOREIGN KEY (${quotedAttrName}) ${definition.replace(/.+?(?=REFERENCES)/, "")}`);
  285. } else {
  286. attrString.push(`${quotedAttrName} ${definition}`);
  287. }
  288. }
  289. return Utils.joinSQLFragments([
  290. "ALTER TABLE",
  291. this.quoteTable(tableName),
  292. attrString.length && `ALTER COLUMN ${attrString.join(", ")}`,
  293. constraintString.length && `ADD ${constraintString.join(", ")}`,
  294. ";",
  295. commentString
  296. ]);
  297. }
  298. renameColumnQuery(tableName, attrBefore, attributes) {
  299. const newName = Object.keys(attributes)[0];
  300. return Utils.joinSQLFragments([
  301. "EXEC sp_rename",
  302. `'${this.quoteTable(tableName)}.${attrBefore}',`,
  303. `'${newName}',`,
  304. "'COLUMN'",
  305. ";"
  306. ]);
  307. }
  308. bulkInsertQuery(tableName, attrValueHashes, options, attributes) {
  309. const quotedTable = this.quoteTable(tableName);
  310. options = options || {};
  311. attributes = attributes || {};
  312. const tuples = [];
  313. const allAttributes = [];
  314. const allQueries = [];
  315. let needIdentityInsertWrapper = false, outputFragment = "";
  316. if (options.returning) {
  317. const returnValues = this.generateReturnValues(attributes, options);
  318. outputFragment = returnValues.outputFragment;
  319. }
  320. const emptyQuery = `INSERT INTO ${quotedTable}${outputFragment} DEFAULT VALUES`;
  321. attrValueHashes.forEach((attrValueHash) => {
  322. const fields = Object.keys(attrValueHash);
  323. const firstAttr = attributes[fields[0]];
  324. if (fields.length === 1 && firstAttr && firstAttr.autoIncrement && attrValueHash[fields[0]] === null) {
  325. allQueries.push(emptyQuery);
  326. return;
  327. }
  328. _.forOwn(attrValueHash, (value, key) => {
  329. if (value !== null && attributes[key] && attributes[key].autoIncrement) {
  330. needIdentityInsertWrapper = true;
  331. }
  332. if (!allAttributes.includes(key)) {
  333. if (value === null && attributes[key] && attributes[key].autoIncrement)
  334. return;
  335. allAttributes.push(key);
  336. }
  337. });
  338. });
  339. if (allAttributes.length > 0) {
  340. attrValueHashes.forEach((attrValueHash) => {
  341. tuples.push(`(${allAttributes.map((key) => this.escape(attrValueHash[key])).join(",")})`);
  342. });
  343. const quotedAttributes = allAttributes.map((attr) => this.quoteIdentifier(attr)).join(",");
  344. allQueries.push((tupleStr) => `INSERT INTO ${quotedTable} (${quotedAttributes})${outputFragment} VALUES ${tupleStr};`);
  345. }
  346. const commands = [];
  347. let offset = 0;
  348. const batch = Math.floor(250 / (allAttributes.length + 1)) + 1;
  349. while (offset < Math.max(tuples.length, 1)) {
  350. const tupleStr = tuples.slice(offset, Math.min(tuples.length, offset + batch));
  351. let generatedQuery = allQueries.map((v) => typeof v === "string" ? v : v(tupleStr)).join(";");
  352. if (needIdentityInsertWrapper) {
  353. generatedQuery = `SET IDENTITY_INSERT ${quotedTable} ON; ${generatedQuery}; SET IDENTITY_INSERT ${quotedTable} OFF;`;
  354. }
  355. commands.push(generatedQuery);
  356. offset += batch;
  357. }
  358. return commands.join(";");
  359. }
  360. updateQuery(tableName, attrValueHash, where, options, attributes) {
  361. const sql = super.updateQuery(tableName, attrValueHash, where, options, attributes);
  362. if (options.limit) {
  363. const updateArgs = `UPDATE TOP(${this.escape(options.limit)})`;
  364. sql.query = sql.query.replace("UPDATE", updateArgs);
  365. }
  366. return sql;
  367. }
  368. upsertQuery(tableName, insertValues, updateValues, where, model) {
  369. const targetTableAlias = this.quoteTable(`${tableName}_target`);
  370. const sourceTableAlias = this.quoteTable(`${tableName}_source`);
  371. const primaryKeysAttrs = [];
  372. const identityAttrs = [];
  373. const uniqueAttrs = [];
  374. const tableNameQuoted = this.quoteTable(tableName);
  375. let needIdentityInsertWrapper = false;
  376. for (const key in model.rawAttributes) {
  377. if (model.rawAttributes[key].primaryKey) {
  378. primaryKeysAttrs.push(model.rawAttributes[key].field || key);
  379. }
  380. if (model.rawAttributes[key].unique) {
  381. uniqueAttrs.push(model.rawAttributes[key].field || key);
  382. }
  383. if (model.rawAttributes[key].autoIncrement) {
  384. identityAttrs.push(model.rawAttributes[key].field || key);
  385. }
  386. }
  387. for (const index of model._indexes) {
  388. if (index.unique && index.fields) {
  389. for (const field of index.fields) {
  390. const fieldName = typeof field === "string" ? field : field.name || field.attribute;
  391. if (!uniqueAttrs.includes(fieldName) && model.rawAttributes[fieldName]) {
  392. uniqueAttrs.push(fieldName);
  393. }
  394. }
  395. }
  396. }
  397. const updateKeys = Object.keys(updateValues);
  398. const insertKeys = Object.keys(insertValues);
  399. const insertKeysQuoted = insertKeys.map((key) => this.quoteIdentifier(key)).join(", ");
  400. const insertValuesEscaped = insertKeys.map((key) => this.escape(insertValues[key])).join(", ");
  401. const sourceTableQuery = `VALUES(${insertValuesEscaped})`;
  402. let joinCondition;
  403. identityAttrs.forEach((key) => {
  404. if (insertValues[key] && insertValues[key] !== null) {
  405. needIdentityInsertWrapper = true;
  406. }
  407. });
  408. const clauses = where[Op.or].filter((clause) => {
  409. let valid = true;
  410. for (const key in clause) {
  411. if (typeof clause[key] === "undefined" || clause[key] == null) {
  412. valid = false;
  413. break;
  414. }
  415. }
  416. return valid;
  417. });
  418. const getJoinSnippet = (array) => {
  419. return array.map((key) => {
  420. key = this.quoteIdentifier(key);
  421. return `${targetTableAlias}.${key} = ${sourceTableAlias}.${key}`;
  422. });
  423. };
  424. if (clauses.length === 0) {
  425. throw new Error("Primary Key or Unique key should be passed to upsert query");
  426. } else {
  427. for (const key in clauses) {
  428. const keys = Object.keys(clauses[key]);
  429. if (primaryKeysAttrs.includes(keys[0])) {
  430. joinCondition = getJoinSnippet(primaryKeysAttrs).join(" AND ");
  431. break;
  432. }
  433. }
  434. if (!joinCondition) {
  435. joinCondition = getJoinSnippet(uniqueAttrs).join(" AND ");
  436. }
  437. }
  438. const filteredUpdateClauses = updateKeys.filter((key) => !identityAttrs.includes(key)).map((key) => {
  439. const value = this.escape(updateValues[key]);
  440. key = this.quoteIdentifier(key);
  441. return `${targetTableAlias}.${key} = ${value}`;
  442. });
  443. const updateSnippet = filteredUpdateClauses.length > 0 ? `WHEN MATCHED THEN UPDATE SET ${filteredUpdateClauses.join(", ")}` : "";
  444. const insertSnippet = `(${insertKeysQuoted}) VALUES(${insertValuesEscaped})`;
  445. let query = `MERGE INTO ${tableNameQuoted} WITH(HOLDLOCK) AS ${targetTableAlias} USING (${sourceTableQuery}) AS ${sourceTableAlias}(${insertKeysQuoted}) ON ${joinCondition}`;
  446. query += ` ${updateSnippet} WHEN NOT MATCHED THEN INSERT ${insertSnippet} OUTPUT $action, INSERTED.*;`;
  447. if (needIdentityInsertWrapper) {
  448. query = `SET IDENTITY_INSERT ${tableNameQuoted} ON; ${query} SET IDENTITY_INSERT ${tableNameQuoted} OFF;`;
  449. }
  450. return query;
  451. }
  452. truncateTableQuery(tableName) {
  453. return `TRUNCATE TABLE ${this.quoteTable(tableName)}`;
  454. }
  455. deleteQuery(tableName, where, options = {}, model) {
  456. const table = this.quoteTable(tableName);
  457. const whereClause = this.getWhereConditions(where, null, model, options);
  458. return Utils.joinSQLFragments([
  459. "DELETE",
  460. options.limit && `TOP(${this.escape(options.limit)})`,
  461. "FROM",
  462. table,
  463. whereClause && `WHERE ${whereClause}`,
  464. ";",
  465. "SELECT @@ROWCOUNT AS AFFECTEDROWS",
  466. ";"
  467. ]);
  468. }
  469. showIndexesQuery(tableName) {
  470. return `EXEC sys.sp_helpindex @objname = N'${this.quoteTable(tableName)}';`;
  471. }
  472. showConstraintsQuery(tableName) {
  473. return `EXEC sp_helpconstraint @objname = ${this.escape(this.quoteTable(tableName))};`;
  474. }
  475. removeIndexQuery(tableName, indexNameOrAttributes) {
  476. let indexName = indexNameOrAttributes;
  477. if (typeof indexName !== "string") {
  478. indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`);
  479. }
  480. return `DROP INDEX ${this.quoteIdentifiers(indexName)} ON ${this.quoteIdentifiers(tableName)}`;
  481. }
  482. attributeToSQL(attribute, options) {
  483. if (!_.isPlainObject(attribute)) {
  484. attribute = {
  485. type: attribute
  486. };
  487. }
  488. if (attribute.references) {
  489. if (attribute.Model && attribute.Model.tableName === attribute.references.model) {
  490. this.sequelize.log("MSSQL does not support self referencial constraints, we will remove it but we recommend restructuring your query");
  491. attribute.onDelete = "";
  492. attribute.onUpdate = "";
  493. }
  494. }
  495. let template;
  496. if (attribute.type instanceof DataTypes.ENUM) {
  497. if (attribute.type.values && !attribute.values)
  498. attribute.values = attribute.type.values;
  499. template = attribute.type.toSql();
  500. template += ` CHECK (${this.quoteIdentifier(attribute.field)} IN(${attribute.values.map((value) => {
  501. return this.escape(value);
  502. }).join(", ")}))`;
  503. return template;
  504. }
  505. template = attribute.type.toString();
  506. if (attribute.allowNull === false) {
  507. template += " NOT NULL";
  508. } else if (!attribute.primaryKey && !Utils.defaultValueSchemable(attribute.defaultValue)) {
  509. template += " NULL";
  510. }
  511. if (attribute.autoIncrement) {
  512. template += " IDENTITY(1,1)";
  513. }
  514. if (attribute.type !== "TEXT" && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) {
  515. template += ` DEFAULT ${this.escape(attribute.defaultValue)}`;
  516. }
  517. if (attribute.unique === true) {
  518. template += " UNIQUE";
  519. }
  520. if (attribute.primaryKey) {
  521. template += " PRIMARY KEY";
  522. }
  523. if ((!options || !options.withoutForeignKeyConstraints) && attribute.references) {
  524. template += ` REFERENCES ${this.quoteTable(attribute.references.model)}`;
  525. if (attribute.references.key) {
  526. template += ` (${this.quoteIdentifier(attribute.references.key)})`;
  527. } else {
  528. template += ` (${this.quoteIdentifier("id")})`;
  529. }
  530. if (attribute.onDelete) {
  531. template += ` ON DELETE ${attribute.onDelete.toUpperCase()}`;
  532. }
  533. if (attribute.onUpdate) {
  534. template += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`;
  535. }
  536. }
  537. if (attribute.comment && typeof attribute.comment === "string") {
  538. template += ` COMMENT ${attribute.comment}`;
  539. }
  540. return template;
  541. }
  542. attributesToSQL(attributes, options) {
  543. const result = {}, existingConstraints = [];
  544. let key, attribute;
  545. for (key in attributes) {
  546. attribute = attributes[key];
  547. if (attribute.references) {
  548. if (existingConstraints.includes(attribute.references.model.toString())) {
  549. attribute.onDelete = "";
  550. attribute.onUpdate = "";
  551. } else {
  552. existingConstraints.push(attribute.references.model.toString());
  553. attribute.onUpdate = "";
  554. }
  555. }
  556. if (key && !attribute.field)
  557. attribute.field = key;
  558. result[attribute.field || key] = this.attributeToSQL(attribute, options);
  559. }
  560. return result;
  561. }
  562. createTrigger() {
  563. throwMethodUndefined("createTrigger");
  564. }
  565. dropTrigger() {
  566. throwMethodUndefined("dropTrigger");
  567. }
  568. renameTrigger() {
  569. throwMethodUndefined("renameTrigger");
  570. }
  571. createFunction() {
  572. throwMethodUndefined("createFunction");
  573. }
  574. dropFunction() {
  575. throwMethodUndefined("dropFunction");
  576. }
  577. renameFunction() {
  578. throwMethodUndefined("renameFunction");
  579. }
  580. _getForeignKeysQueryPrefix(catalogName) {
  581. 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`;
  582. }
  583. getForeignKeysQuery(table, catalogName) {
  584. const tableName = table.tableName || table;
  585. let sql = `${this._getForeignKeysQueryPrefix(catalogName)} WHERE TB.NAME =${wrapSingleQuote(tableName)}`;
  586. if (table.schema) {
  587. sql += ` AND SCHEMA_NAME(TB.SCHEMA_ID) =${wrapSingleQuote(table.schema)}`;
  588. }
  589. return sql;
  590. }
  591. getForeignKeyQuery(table, attributeName) {
  592. const tableName = table.tableName || table;
  593. return Utils.joinSQLFragments([
  594. this._getForeignKeysQueryPrefix(),
  595. "WHERE",
  596. `TB.NAME =${wrapSingleQuote(tableName)}`,
  597. "AND",
  598. `COL.NAME =${wrapSingleQuote(attributeName)}`,
  599. table.schema && `AND SCHEMA_NAME(TB.SCHEMA_ID) =${wrapSingleQuote(table.schema)}`
  600. ]);
  601. }
  602. getPrimaryKeyConstraintQuery(table, attributeName) {
  603. const tableName = wrapSingleQuote(table.tableName || table);
  604. return Utils.joinSQLFragments([
  605. "SELECT K.TABLE_NAME AS tableName,",
  606. "K.COLUMN_NAME AS columnName,",
  607. "K.CONSTRAINT_NAME AS constraintName",
  608. "FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS C",
  609. "JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS K",
  610. "ON C.TABLE_NAME = K.TABLE_NAME",
  611. "AND C.CONSTRAINT_CATALOG = K.CONSTRAINT_CATALOG",
  612. "AND C.CONSTRAINT_SCHEMA = K.CONSTRAINT_SCHEMA",
  613. "AND C.CONSTRAINT_NAME = K.CONSTRAINT_NAME",
  614. "WHERE C.CONSTRAINT_TYPE = 'PRIMARY KEY'",
  615. `AND K.COLUMN_NAME = ${wrapSingleQuote(attributeName)}`,
  616. `AND K.TABLE_NAME = ${tableName}`,
  617. ";"
  618. ]);
  619. }
  620. dropForeignKeyQuery(tableName, foreignKey) {
  621. return Utils.joinSQLFragments([
  622. "ALTER TABLE",
  623. this.quoteTable(tableName),
  624. "DROP",
  625. this.quoteIdentifier(foreignKey)
  626. ]);
  627. }
  628. getDefaultConstraintQuery(tableName, attributeName) {
  629. const quotedTable = this.quoteTable(tableName);
  630. return Utils.joinSQLFragments([
  631. "SELECT name FROM sys.default_constraints",
  632. `WHERE PARENT_OBJECT_ID = OBJECT_ID('${quotedTable}', 'U')`,
  633. `AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('${attributeName}')`,
  634. `AND object_id = OBJECT_ID('${quotedTable}', 'U'))`,
  635. ";"
  636. ]);
  637. }
  638. dropConstraintQuery(tableName, constraintName) {
  639. return Utils.joinSQLFragments([
  640. "ALTER TABLE",
  641. this.quoteTable(tableName),
  642. "DROP CONSTRAINT",
  643. this.quoteIdentifier(constraintName),
  644. ";"
  645. ]);
  646. }
  647. setIsolationLevelQuery() {
  648. }
  649. generateTransactionId() {
  650. return randomBytes(10).toString("hex");
  651. }
  652. startTransactionQuery(transaction) {
  653. if (transaction.parent) {
  654. return `SAVE TRANSACTION ${this.quoteIdentifier(transaction.name)};`;
  655. }
  656. return "BEGIN TRANSACTION;";
  657. }
  658. commitTransactionQuery(transaction) {
  659. if (transaction.parent) {
  660. return;
  661. }
  662. return "COMMIT TRANSACTION;";
  663. }
  664. rollbackTransactionQuery(transaction) {
  665. if (transaction.parent) {
  666. return `ROLLBACK TRANSACTION ${this.quoteIdentifier(transaction.name)};`;
  667. }
  668. return "ROLLBACK TRANSACTION;";
  669. }
  670. selectFromTableFragment(options, model, attributes, tables, mainTableAs, where) {
  671. this._throwOnEmptyAttributes(attributes, { modelName: model && model.name, as: mainTableAs });
  672. const dbVersion = this.sequelize.options.databaseVersion;
  673. const isSQLServer2008 = semver.valid(dbVersion) && semver.lt(dbVersion, "11.0.0");
  674. if (isSQLServer2008 && options.offset) {
  675. const offset = options.offset || 0;
  676. const isSubQuery = options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation;
  677. let orders = { mainQueryOrder: [] };
  678. if (options.order) {
  679. orders = this.getQueryOrders(options, model, isSubQuery);
  680. }
  681. if (orders.mainQueryOrder.length === 0) {
  682. orders.mainQueryOrder.push(this.quoteIdentifier(model.primaryKeyField));
  683. }
  684. const tmpTable = mainTableAs || "OffsetTable";
  685. if (options.include) {
  686. const subQuery = options.subQuery === void 0 ? options.limit && options.hasMultiAssociation : options.subQuery;
  687. const mainTable = {
  688. name: mainTableAs,
  689. quotedName: null,
  690. as: null,
  691. model
  692. };
  693. const topLevelInfo = {
  694. names: mainTable,
  695. options,
  696. subQuery
  697. };
  698. let mainJoinQueries = [];
  699. for (const include of options.include) {
  700. if (include.separate) {
  701. continue;
  702. }
  703. const joinQueries = this.generateInclude(include, { externalAs: mainTableAs, internalAs: mainTableAs }, topLevelInfo);
  704. mainJoinQueries = mainJoinQueries.concat(joinQueries.mainQuery);
  705. }
  706. return Utils.joinSQLFragments([
  707. "SELECT TOP 100 PERCENT",
  708. attributes.join(", "),
  709. "FROM (",
  710. [
  711. "SELECT",
  712. options.limit && `TOP ${options.limit}`,
  713. "* FROM (",
  714. [
  715. "SELECT ROW_NUMBER() OVER (",
  716. [
  717. "ORDER BY",
  718. orders.mainQueryOrder.join(", ")
  719. ],
  720. `) as row_num, ${tmpTable}.* FROM (`,
  721. [
  722. "SELECT DISTINCT",
  723. `${tmpTable}.* FROM ${tables} AS ${tmpTable}`,
  724. mainJoinQueries,
  725. where && `WHERE ${where}`
  726. ],
  727. `) AS ${tmpTable}`
  728. ],
  729. `) AS ${tmpTable} WHERE row_num > ${offset}`
  730. ],
  731. `) AS ${tmpTable}`
  732. ]);
  733. }
  734. return Utils.joinSQLFragments([
  735. "SELECT TOP 100 PERCENT",
  736. attributes.join(", "),
  737. "FROM (",
  738. [
  739. "SELECT",
  740. options.limit && `TOP ${options.limit}`,
  741. "* FROM (",
  742. [
  743. "SELECT ROW_NUMBER() OVER (",
  744. [
  745. "ORDER BY",
  746. orders.mainQueryOrder.join(", ")
  747. ],
  748. `) as row_num, * FROM ${tables} AS ${tmpTable}`,
  749. where && `WHERE ${where}`
  750. ],
  751. `) AS ${tmpTable} WHERE row_num > ${offset}`
  752. ],
  753. `) AS ${tmpTable}`
  754. ]);
  755. }
  756. return Utils.joinSQLFragments([
  757. "SELECT",
  758. isSQLServer2008 && options.limit && `TOP ${options.limit}`,
  759. attributes.join(", "),
  760. `FROM ${tables}`,
  761. mainTableAs && `AS ${mainTableAs}`,
  762. options.tableHint && TableHints[options.tableHint] && `WITH (${TableHints[options.tableHint]})`
  763. ]);
  764. }
  765. addLimitAndOffset(options, model) {
  766. if (semver.valid(this.sequelize.options.databaseVersion) && semver.lt(this.sequelize.options.databaseVersion, "11.0.0")) {
  767. return "";
  768. }
  769. const offset = options.offset || 0;
  770. const isSubQuery = options.subQuery === void 0 ? options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation : options.subQuery;
  771. let fragment = "";
  772. let orders = {};
  773. if (options.order) {
  774. orders = this.getQueryOrders(options, model, isSubQuery);
  775. }
  776. if (options.limit || options.offset) {
  777. if (!options.order || options.order.length === 0 || options.include && orders.subQueryOrder.length === 0) {
  778. let primaryKey = model.primaryKeyField;
  779. const tablePkFragment = `${this.quoteTable(options.tableAs || model.name)}.${this.quoteIdentifier(primaryKey)}`;
  780. const aliasedAttribute = (options.attributes || []).find((attr) => Array.isArray(attr) && attr[1] && (attr[0] === primaryKey || attr[1] === primaryKey));
  781. if (aliasedAttribute) {
  782. const modelName = this.quoteIdentifier(options.tableAs || model.name);
  783. const alias = this._getAliasForField(modelName, aliasedAttribute[1], options);
  784. primaryKey = new Utils.Col(alias || aliasedAttribute[1]);
  785. }
  786. if (!options.order || !options.order.length) {
  787. fragment += ` ORDER BY ${tablePkFragment}`;
  788. } else {
  789. const orderFieldNames = (options.order || []).map((order) => {
  790. const value = Array.isArray(order) ? order[0] : order;
  791. if (value instanceof Utils.Col) {
  792. return value.col;
  793. }
  794. if (value instanceof Utils.Literal) {
  795. return value.val;
  796. }
  797. return value;
  798. });
  799. const primaryKeyFieldAlreadyPresent = orderFieldNames.some((fieldName) => fieldName === (primaryKey.col || primaryKey));
  800. if (!primaryKeyFieldAlreadyPresent) {
  801. fragment += options.order && !isSubQuery ? ", " : " ORDER BY ";
  802. fragment += tablePkFragment;
  803. }
  804. }
  805. }
  806. if (options.offset || options.limit) {
  807. fragment += ` OFFSET ${this.escape(offset)} ROWS`;
  808. }
  809. if (options.limit) {
  810. fragment += ` FETCH NEXT ${this.escape(options.limit)} ROWS ONLY`;
  811. }
  812. }
  813. return fragment;
  814. }
  815. booleanValue(value) {
  816. return value ? 1 : 0;
  817. }
  818. quoteIdentifier(identifier, force) {
  819. return `[${identifier.replace(/[[\]']+/g, "")}]`;
  820. }
  821. }
  822. function wrapSingleQuote(identifier) {
  823. return Utils.addTicks(Utils.removeTicks(identifier, "'"), "'");
  824. }
  825. module.exports = MSSQLQueryGenerator;
  826. //# sourceMappingURL=query-generator.js.map