API Reference Source

lib/dialects/snowflake/query-generator.js

  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __defProps = Object.defineProperties;
  4. var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
  5. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  6. var __hasOwnProp = Object.prototype.hasOwnProperty;
  7. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  8. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  9. var __spreadValues = (a, b) => {
  10. for (var prop in b || (b = {}))
  11. if (__hasOwnProp.call(b, prop))
  12. __defNormalProp(a, prop, b[prop]);
  13. if (__getOwnPropSymbols)
  14. for (var prop of __getOwnPropSymbols(b)) {
  15. if (__propIsEnum.call(b, prop))
  16. __defNormalProp(a, prop, b[prop]);
  17. }
  18. return a;
  19. };
  20. var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
  21. const _ = require("lodash");
  22. const Utils = require("../../utils");
  23. const AbstractQueryGenerator = require("../abstract/query-generator");
  24. const util = require("util");
  25. const Op = require("../../operators");
  26. const JSON_FUNCTION_REGEX = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i;
  27. const JSON_OPERATOR_REGEX = /^\s*(->>?|@>|<@|\?[|&]?|\|{2}|#-)/i;
  28. const TOKEN_CAPTURE_REGEX = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i;
  29. const FOREIGN_KEY_FIELDS = [
  30. "CONSTRAINT_NAME as constraint_name",
  31. "CONSTRAINT_NAME as constraintName",
  32. "CONSTRAINT_SCHEMA as constraintSchema",
  33. "CONSTRAINT_SCHEMA as constraintCatalog",
  34. "TABLE_NAME as tableName",
  35. "TABLE_SCHEMA as tableSchema",
  36. "TABLE_SCHEMA as tableCatalog",
  37. "COLUMN_NAME as columnName",
  38. "REFERENCED_TABLE_SCHEMA as referencedTableSchema",
  39. "REFERENCED_TABLE_SCHEMA as referencedTableCatalog",
  40. "REFERENCED_TABLE_NAME as referencedTableName",
  41. "REFERENCED_COLUMN_NAME as referencedColumnName"
  42. ].join(",");
  43. 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(",");
  44. const typeWithoutDefault = /* @__PURE__ */ new Set(["BLOB", "TEXT", "GEOMETRY", "JSON"]);
  45. class SnowflakeQueryGenerator extends AbstractQueryGenerator {
  46. constructor(options) {
  47. super(options);
  48. this.OperatorMap = __spreadProps(__spreadValues({}, this.OperatorMap), {
  49. [Op.regexp]: "REGEXP",
  50. [Op.notRegexp]: "NOT REGEXP"
  51. });
  52. }
  53. createDatabaseQuery(databaseName, options) {
  54. options = __spreadValues({
  55. charset: null,
  56. collate: null
  57. }, options);
  58. return Utils.joinSQLFragments([
  59. "CREATE DATABASE IF NOT EXISTS",
  60. this.quoteIdentifier(databaseName),
  61. options.charset && `DEFAULT CHARACTER SET ${this.escape(options.charset)}`,
  62. options.collate && `DEFAULT COLLATE ${this.escape(options.collate)}`,
  63. ";"
  64. ]);
  65. }
  66. dropDatabaseQuery(databaseName) {
  67. return `DROP DATABASE IF EXISTS ${this.quoteIdentifier(databaseName)};`;
  68. }
  69. createSchema() {
  70. return "SHOW TABLES";
  71. }
  72. showSchemasQuery() {
  73. return "SHOW TABLES";
  74. }
  75. versionQuery() {
  76. return "SELECT CURRENT_VERSION()";
  77. }
  78. createTableQuery(tableName, attributes, options) {
  79. options = __spreadValues({
  80. charset: null,
  81. rowFormat: null
  82. }, options);
  83. const primaryKeys = [];
  84. const foreignKeys = {};
  85. const attrStr = [];
  86. for (const attr in attributes) {
  87. if (!Object.prototype.hasOwnProperty.call(attributes, attr))
  88. continue;
  89. const dataType = attributes[attr];
  90. let match;
  91. if (dataType.includes("PRIMARY KEY")) {
  92. primaryKeys.push(attr);
  93. if (dataType.includes("REFERENCES")) {
  94. match = dataType.match(/^(.+) (REFERENCES.*)$/);
  95. attrStr.push(`${this.quoteIdentifier(attr)} ${match[1].replace("PRIMARY KEY", "")}`);
  96. foreignKeys[attr] = match[2];
  97. } else {
  98. attrStr.push(`${this.quoteIdentifier(attr)} ${dataType.replace("PRIMARY KEY", "")}`);
  99. }
  100. } else if (dataType.includes("REFERENCES")) {
  101. match = dataType.match(/^(.+) (REFERENCES.*)$/);
  102. attrStr.push(`${this.quoteIdentifier(attr)} ${match[1]}`);
  103. foreignKeys[attr] = match[2];
  104. } else {
  105. attrStr.push(`${this.quoteIdentifier(attr)} ${dataType}`);
  106. }
  107. }
  108. const table = this.quoteTable(tableName);
  109. let attributesClause = attrStr.join(", ");
  110. const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", ");
  111. if (options.uniqueKeys) {
  112. _.each(options.uniqueKeys, (columns, indexName) => {
  113. if (columns.customIndex) {
  114. if (typeof indexName !== "string") {
  115. indexName = `uniq_${tableName}_${columns.fields.join("_")}`;
  116. }
  117. attributesClause += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`;
  118. }
  119. });
  120. }
  121. if (pkString.length > 0) {
  122. attributesClause += `, PRIMARY KEY (${pkString})`;
  123. }
  124. for (const fkey in foreignKeys) {
  125. if (Object.prototype.hasOwnProperty.call(foreignKeys, fkey)) {
  126. attributesClause += `, FOREIGN KEY (${this.quoteIdentifier(fkey)}) ${foreignKeys[fkey]}`;
  127. }
  128. }
  129. return Utils.joinSQLFragments([
  130. "CREATE TABLE IF NOT EXISTS",
  131. table,
  132. `(${attributesClause})`,
  133. options.comment && typeof options.comment === "string" && `COMMENT ${this.escape(options.comment)}`,
  134. options.charset && `DEFAULT CHARSET=${options.charset}`,
  135. options.collate && `COLLATE ${options.collate}`,
  136. options.rowFormat && `ROW_FORMAT=${options.rowFormat}`,
  137. ";"
  138. ]);
  139. }
  140. describeTableQuery(tableName, schema, schemaDelimiter) {
  141. const table = this.quoteTable(this.addSchema({
  142. tableName,
  143. _schema: schema,
  144. _schemaDelimiter: schemaDelimiter
  145. }));
  146. return `SHOW FULL COLUMNS FROM ${table};`;
  147. }
  148. showTablesQuery(database) {
  149. return Utils.joinSQLFragments([
  150. "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'",
  151. database ? `AND TABLE_SCHEMA = ${this.escape(database)}` : "AND TABLE_SCHEMA NOT IN ( 'INFORMATION_SCHEMA', 'PERFORMANCE_SCHEMA', 'SYS')",
  152. ";"
  153. ]);
  154. }
  155. addColumnQuery(table, key, dataType) {
  156. return Utils.joinSQLFragments([
  157. "ALTER TABLE",
  158. this.quoteTable(table),
  159. "ADD",
  160. this.quoteIdentifier(key),
  161. this.attributeToSQL(dataType, {
  162. context: "addColumn",
  163. tableName: table,
  164. foreignKey: key
  165. }),
  166. ";"
  167. ]);
  168. }
  169. removeColumnQuery(tableName, attributeName) {
  170. return Utils.joinSQLFragments([
  171. "ALTER TABLE",
  172. this.quoteTable(tableName),
  173. "DROP",
  174. this.quoteIdentifier(attributeName),
  175. ";"
  176. ]);
  177. }
  178. changeColumnQuery(tableName, attributes) {
  179. const query = (...subQuerys) => Utils.joinSQLFragments([
  180. "ALTER TABLE",
  181. this.quoteTable(tableName),
  182. "ALTER COLUMN",
  183. ...subQuerys,
  184. ";"
  185. ]);
  186. const sql = [];
  187. for (const attributeName in attributes) {
  188. let definition = this.dataTypeMapping(tableName, attributeName, attributes[attributeName]);
  189. const attrSql = [];
  190. if (definition.includes("NOT NULL")) {
  191. attrSql.push(query(this.quoteIdentifier(attributeName), "SET NOT NULL"));
  192. definition = definition.replace("NOT NULL", "").trim();
  193. } else if (!definition.includes("REFERENCES")) {
  194. attrSql.push(query(this.quoteIdentifier(attributeName), "DROP NOT NULL"));
  195. }
  196. if (definition.includes("DEFAULT")) {
  197. attrSql.push(query(this.quoteIdentifier(attributeName), "SET DEFAULT", definition.match(/DEFAULT ([^;]+)/)[1]));
  198. definition = definition.replace(/(DEFAULT[^;]+)/, "").trim();
  199. } else if (!definition.includes("REFERENCES")) {
  200. attrSql.push(query(this.quoteIdentifier(attributeName), "DROP DEFAULT"));
  201. }
  202. if (definition.match(/UNIQUE;*$/)) {
  203. definition = definition.replace(/UNIQUE;*$/, "");
  204. attrSql.push(query("ADD UNIQUE (", this.quoteIdentifier(attributeName), ")").replace("ALTER COLUMN", ""));
  205. }
  206. if (definition.includes("REFERENCES")) {
  207. definition = definition.replace(/.+?(?=REFERENCES)/, "");
  208. attrSql.push(query("ADD FOREIGN KEY (", this.quoteIdentifier(attributeName), ")", definition).replace("ALTER COLUMN", ""));
  209. } else {
  210. attrSql.push(query(this.quoteIdentifier(attributeName), "TYPE", definition));
  211. }
  212. sql.push(attrSql.join(""));
  213. }
  214. return sql.join("");
  215. }
  216. renameColumnQuery(tableName, attrBefore, attributes) {
  217. const attrString = [];
  218. for (const attrName in attributes) {
  219. const definition = attributes[attrName];
  220. attrString.push(`'${attrBefore}' '${attrName}' ${definition}`);
  221. }
  222. return Utils.joinSQLFragments([
  223. "ALTER TABLE",
  224. this.quoteTable(tableName),
  225. "RENAME COLUMN",
  226. attrString.join(" to "),
  227. ";"
  228. ]);
  229. }
  230. handleSequelizeMethod(attr, tableName, factory, options, prepend) {
  231. if (attr instanceof Utils.Json) {
  232. if (attr.conditions) {
  233. const conditions = this.parseConditionObject(attr.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`);
  234. return conditions.join(" AND ");
  235. }
  236. if (attr.path) {
  237. let str;
  238. if (this._checkValidJsonStatement(attr.path)) {
  239. str = attr.path;
  240. } else {
  241. const paths = _.toPath(attr.path);
  242. const column = paths.shift();
  243. str = this.jsonPathExtractionQuery(column, paths);
  244. }
  245. if (attr.value) {
  246. str += util.format(" = %s", this.escape(attr.value));
  247. }
  248. return str;
  249. }
  250. } else if (attr instanceof Utils.Cast) {
  251. if (/timestamp/i.test(attr.type)) {
  252. attr.type = "datetime";
  253. } else if (attr.json && /boolean/i.test(attr.type)) {
  254. attr.type = "char";
  255. } else if (/double precision/i.test(attr.type) || /boolean/i.test(attr.type) || /integer/i.test(attr.type)) {
  256. attr.type = "decimal";
  257. } else if (/text/i.test(attr.type)) {
  258. attr.type = "char";
  259. }
  260. }
  261. return super.handleSequelizeMethod(attr, tableName, factory, options, prepend);
  262. }
  263. truncateTableQuery(tableName) {
  264. return Utils.joinSQLFragments([
  265. "TRUNCATE",
  266. this.quoteTable(tableName)
  267. ]);
  268. }
  269. deleteQuery(tableName, where, options = {}, model) {
  270. const table = this.quoteTable(tableName);
  271. let whereClause = this.getWhereConditions(where, null, model, options);
  272. const limit = options.limit && ` LIMIT ${this.escape(options.limit)}`;
  273. let primaryKeys = "";
  274. let primaryKeysSelection = "";
  275. if (whereClause) {
  276. whereClause = `WHERE ${whereClause}`;
  277. }
  278. if (limit) {
  279. if (!model) {
  280. throw new Error("Cannot LIMIT delete without a model.");
  281. }
  282. const pks = Object.values(model.primaryKeys).map((pk) => this.quoteIdentifier(pk.field)).join(",");
  283. primaryKeys = model.primaryKeyAttributes.length > 1 ? `(${pks})` : pks;
  284. primaryKeysSelection = pks;
  285. return Utils.joinSQLFragments([
  286. "DELETE FROM",
  287. table,
  288. "WHERE",
  289. primaryKeys,
  290. "IN (SELECT",
  291. primaryKeysSelection,
  292. "FROM",
  293. table,
  294. whereClause,
  295. limit,
  296. ")",
  297. ";"
  298. ]);
  299. }
  300. return Utils.joinSQLFragments([
  301. "DELETE FROM",
  302. table,
  303. whereClause,
  304. ";"
  305. ]);
  306. }
  307. showIndexesQuery() {
  308. return "SELECT '' FROM DUAL";
  309. }
  310. showConstraintsQuery(table, constraintName) {
  311. const tableName = table.tableName || table;
  312. const schemaName = table.schema;
  313. return Utils.joinSQLFragments([
  314. "SELECT CONSTRAINT_CATALOG AS constraintCatalog,",
  315. "CONSTRAINT_NAME AS constraintName,",
  316. "CONSTRAINT_SCHEMA AS constraintSchema,",
  317. "CONSTRAINT_TYPE AS constraintType,",
  318. "TABLE_NAME AS tableName,",
  319. "TABLE_SCHEMA AS tableSchema",
  320. "from INFORMATION_SCHEMA.TABLE_CONSTRAINTS",
  321. `WHERE table_name='${tableName}'`,
  322. constraintName && `AND constraint_name = '${constraintName}'`,
  323. schemaName && `AND TABLE_SCHEMA = '${schemaName}'`,
  324. ";"
  325. ]);
  326. }
  327. removeIndexQuery(tableName, indexNameOrAttributes) {
  328. let indexName = indexNameOrAttributes;
  329. if (typeof indexName !== "string") {
  330. indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`);
  331. }
  332. return Utils.joinSQLFragments([
  333. "DROP INDEX",
  334. this.quoteIdentifier(indexName),
  335. "ON",
  336. this.quoteTable(tableName),
  337. ";"
  338. ]);
  339. }
  340. attributeToSQL(attribute, options) {
  341. if (!_.isPlainObject(attribute)) {
  342. attribute = {
  343. type: attribute
  344. };
  345. }
  346. const attributeString = attribute.type.toString({ escape: this.escape.bind(this) });
  347. let template = attributeString;
  348. if (attribute.allowNull === false) {
  349. template += " NOT NULL";
  350. }
  351. if (attribute.autoIncrement) {
  352. template += " AUTOINCREMENT";
  353. }
  354. if (!typeWithoutDefault.has(attributeString) && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) {
  355. template += ` DEFAULT ${this.escape(attribute.defaultValue)}`;
  356. }
  357. if (attribute.unique === true) {
  358. template += " UNIQUE";
  359. }
  360. if (attribute.primaryKey) {
  361. template += " PRIMARY KEY";
  362. }
  363. if (attribute.comment) {
  364. template += ` COMMENT ${this.escape(attribute.comment)}`;
  365. }
  366. if (attribute.first) {
  367. template += " FIRST";
  368. }
  369. if (attribute.after) {
  370. template += ` AFTER ${this.quoteIdentifier(attribute.after)}`;
  371. }
  372. if (attribute.references) {
  373. if (options && options.context === "addColumn" && options.foreignKey) {
  374. const attrName = this.quoteIdentifier(options.foreignKey);
  375. const fkName = this.quoteIdentifier(`${options.tableName}_${attrName}_foreign_idx`);
  376. template += `, ADD CONSTRAINT ${fkName} FOREIGN KEY (${attrName})`;
  377. }
  378. template += ` REFERENCES ${this.quoteTable(attribute.references.model)}`;
  379. if (attribute.references.key) {
  380. template += ` (${this.quoteIdentifier(attribute.references.key)})`;
  381. } else {
  382. template += ` (${this.quoteIdentifier("id")})`;
  383. }
  384. if (attribute.onDelete) {
  385. template += ` ON DELETE ${attribute.onDelete.toUpperCase()}`;
  386. }
  387. if (attribute.onUpdate) {
  388. template += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`;
  389. }
  390. }
  391. return template;
  392. }
  393. attributesToSQL(attributes, options) {
  394. const result = {};
  395. for (const key in attributes) {
  396. const attribute = attributes[key];
  397. result[attribute.field || key] = this.attributeToSQL(attribute, options);
  398. }
  399. return result;
  400. }
  401. _checkValidJsonStatement(stmt) {
  402. if (typeof stmt !== "string") {
  403. return false;
  404. }
  405. let currentIndex = 0;
  406. let openingBrackets = 0;
  407. let closingBrackets = 0;
  408. let hasJsonFunction = false;
  409. let hasInvalidToken = false;
  410. while (currentIndex < stmt.length) {
  411. const string = stmt.substr(currentIndex);
  412. const functionMatches = JSON_FUNCTION_REGEX.exec(string);
  413. if (functionMatches) {
  414. currentIndex += functionMatches[0].indexOf("(");
  415. hasJsonFunction = true;
  416. continue;
  417. }
  418. const operatorMatches = JSON_OPERATOR_REGEX.exec(string);
  419. if (operatorMatches) {
  420. currentIndex += operatorMatches[0].length;
  421. hasJsonFunction = true;
  422. continue;
  423. }
  424. const tokenMatches = TOKEN_CAPTURE_REGEX.exec(string);
  425. if (tokenMatches) {
  426. const capturedToken = tokenMatches[1];
  427. if (capturedToken === "(") {
  428. openingBrackets++;
  429. } else if (capturedToken === ")") {
  430. closingBrackets++;
  431. } else if (capturedToken === ";") {
  432. hasInvalidToken = true;
  433. break;
  434. }
  435. currentIndex += tokenMatches[0].length;
  436. continue;
  437. }
  438. break;
  439. }
  440. if (hasJsonFunction && (hasInvalidToken || openingBrackets !== closingBrackets)) {
  441. throw new Error(`Invalid json statement: ${stmt}`);
  442. }
  443. return hasJsonFunction;
  444. }
  445. dataTypeMapping(tableName, attr, dataType) {
  446. if (dataType.includes("PRIMARY KEY")) {
  447. dataType = dataType.replace("PRIMARY KEY", "");
  448. }
  449. if (dataType.includes("SERIAL")) {
  450. if (dataType.includes("BIGINT")) {
  451. dataType = dataType.replace("SERIAL", "BIGSERIAL");
  452. dataType = dataType.replace("BIGINT", "");
  453. } else if (dataType.includes("SMALLINT")) {
  454. dataType = dataType.replace("SERIAL", "SMALLSERIAL");
  455. dataType = dataType.replace("SMALLINT", "");
  456. } else {
  457. dataType = dataType.replace("INTEGER", "");
  458. }
  459. dataType = dataType.replace("NOT NULL", "");
  460. }
  461. return dataType;
  462. }
  463. getForeignKeysQuery(table, schemaName) {
  464. const tableName = table.tableName || table;
  465. return Utils.joinSQLFragments([
  466. "SELECT",
  467. FOREIGN_KEY_FIELDS,
  468. `FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '${tableName}'`,
  469. `AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='${schemaName}'`,
  470. "AND REFERENCED_TABLE_NAME IS NOT NULL",
  471. ";"
  472. ]);
  473. }
  474. getForeignKeyQuery(table, columnName) {
  475. const quotedSchemaName = table.schema ? wrapSingleQuote(table.schema) : "";
  476. const quotedTableName = wrapSingleQuote(table.tableName || table);
  477. const quotedColumnName = wrapSingleQuote(columnName);
  478. return Utils.joinSQLFragments([
  479. "SELECT",
  480. FOREIGN_KEY_FIELDS,
  481. "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE",
  482. "WHERE (",
  483. [
  484. `REFERENCED_TABLE_NAME = ${quotedTableName}`,
  485. table.schema && `AND REFERENCED_TABLE_SCHEMA = ${quotedSchemaName}`,
  486. `AND REFERENCED_COLUMN_NAME = ${quotedColumnName}`
  487. ],
  488. ") OR (",
  489. [
  490. `TABLE_NAME = ${quotedTableName}`,
  491. table.schema && `AND TABLE_SCHEMA = ${quotedSchemaName}`,
  492. `AND COLUMN_NAME = ${quotedColumnName}`,
  493. "AND REFERENCED_TABLE_NAME IS NOT NULL"
  494. ],
  495. ")"
  496. ]);
  497. }
  498. dropForeignKeyQuery(tableName, foreignKey) {
  499. return Utils.joinSQLFragments([
  500. "ALTER TABLE",
  501. this.quoteTable(tableName),
  502. "DROP FOREIGN KEY",
  503. this.quoteIdentifier(foreignKey),
  504. ";"
  505. ]);
  506. }
  507. addLimitAndOffset(options) {
  508. let fragment = [];
  509. if (options.offset !== null && options.offset !== void 0 && options.offset !== 0) {
  510. fragment = fragment.concat([" LIMIT ", this.escape(options.limit), " OFFSET ", this.escape(options.offset)]);
  511. } else if (options.limit !== null && options.limit !== void 0) {
  512. fragment = [" LIMIT ", this.escape(options.limit)];
  513. }
  514. return fragment.join("");
  515. }
  516. quoteIdentifier(identifier, force) {
  517. const optForceQuote = force || false;
  518. const optQuoteIdentifiers = this.options.quoteIdentifiers !== false;
  519. const rawIdentifier = Utils.removeTicks(identifier, '"');
  520. if (optForceQuote === true || optQuoteIdentifiers !== false || identifier.includes(".") || identifier.includes("->") || SNOWFLAKE_RESERVED_WORDS.includes(rawIdentifier.toLowerCase())) {
  521. return Utils.addTicks(rawIdentifier, '"');
  522. }
  523. return rawIdentifier;
  524. }
  525. }
  526. function wrapSingleQuote(identifier) {
  527. return Utils.addTicks(identifier, "'");
  528. }
  529. module.exports = SnowflakeQueryGenerator;
  530. //# sourceMappingURL=query-generator.js.map