query-generator.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. var Utils = require("../../utils")
  2. , DataTypes = require("../../data-types")
  3. , SqlString = require("../../sql-string")
  4. , Transaction = require("../../transaction")
  5. var MySqlQueryGenerator = Utils._.extend(
  6. Utils._.clone(require("../abstract/query-generator")),
  7. Utils._.clone(require("../mysql/query-generator"))
  8. )
  9. var hashToWhereConditions = MySqlQueryGenerator.hashToWhereConditions
  10. module.exports = (function() {
  11. var QueryGenerator = {
  12. options: {},
  13. dialect: 'sqlite',
  14. addSchema: function(opts) {
  15. var tableName = undefined
  16. var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined)
  17. var schemaDelimiter = (!!opts && !!opts.options && !!opts.options.schemaDelimiter ? opts.options.schemaDelimiter : undefined)
  18. if (!!opts && !!opts.tableName) {
  19. tableName = opts.tableName
  20. }
  21. else if (typeof opts === "string") {
  22. tableName = opts
  23. }
  24. if (!schema || schema.toString().trim() === "") {
  25. return tableName
  26. }
  27. return this.quoteIdentifier(schema + (!schemaDelimiter ? '.' : schemaDelimiter) + tableName)
  28. },
  29. createSchema: function() {
  30. var query = "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"
  31. return Utils._.template(query)({})
  32. },
  33. dropSchema: function(tableName, options) {
  34. return this.dropTableQuery(tableName, options)
  35. },
  36. showSchemasQuery: function() {
  37. return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"
  38. },
  39. createTableQuery: function(tableName, attributes, options) {
  40. options = options || {}
  41. var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)"
  42. , primaryKeys = []
  43. , needsMultiplePrimaryKeys = (Utils._.values(attributes).filter(function(definition) {
  44. return Utils._.includes(definition, 'PRIMARY KEY')
  45. }).length > 1)
  46. , attrStr = []
  47. , modifierLastIndex = -1
  48. for (var attr in attributes) {
  49. if (attributes.hasOwnProperty(attr)) {
  50. var dataType = attributes[attr]
  51. if (Utils._.includes(dataType, 'AUTOINCREMENT')) {
  52. dataType = dataType.replace(/BIGINT/, 'INTEGER')
  53. }
  54. // SQLite thinks that certain modifiers should come before the length declaration,
  55. // whereas other dialects want them after, see http://www.sqlite.org/lang_createtable.html.
  56. // Start by finding the index of the last of the modifiers
  57. ['UNSIGNED', 'BINARY', 'ZEROFILL'].forEach(function (modifier) {
  58. var tmpIndex = dataType.indexOf(modifier)
  59. if (tmpIndex > modifierLastIndex) {
  60. modifierLastIndex = tmpIndex + modifier.length
  61. }
  62. })
  63. if (modifierLastIndex) {
  64. // If a modifier was found, and a lenght declaration is given before the modifier, move the length
  65. var length = dataType.match(/\(\s*\d+(\s*,\s*\d)?\s*\)/)
  66. if (length && length.index < modifierLastIndex) {
  67. dataType = dataType.replace(length[0], '')
  68. // Since the legnth was placed before the modifier, removing the legnth has changed the index
  69. if (length.index < modifierLastIndex) {
  70. modifierLastIndex -= length[0].length
  71. }
  72. dataType = Utils._.insert(dataType, modifierLastIndex, length[0]).trim()
  73. }
  74. modifierLastIndex = -1
  75. }
  76. if (Utils._.includes(dataType, 'PRIMARY KEY') && needsMultiplePrimaryKeys) {
  77. primaryKeys.push(attr)
  78. attrStr.push(this.quoteIdentifier(attr) + " " + dataType.replace(/PRIMARY KEY/, 'NOT NULL'))
  79. } else {
  80. attrStr.push(this.quoteIdentifier(attr) + " " + dataType)
  81. }
  82. }
  83. }
  84. var values = {
  85. table: this.quoteIdentifier(tableName),
  86. attributes: attrStr.join(", "),
  87. charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "")
  88. }
  89. , pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk) }.bind(this)).join(", ")
  90. if (!!options.uniqueKeys) {
  91. Utils._.each(options.uniqueKeys, function(columns) {
  92. values.attributes += ", UNIQUE (" + columns.fields.join(', ') + ")"
  93. })
  94. }
  95. if (pkString.length > 0) {
  96. values.attributes += ", PRIMARY KEY (" + pkString + ")"
  97. }
  98. var sql = Utils._.template(query, values).trim() + ";"
  99. return this.replaceBooleanDefaults(sql)
  100. },
  101. booleanValue: function(value){
  102. return !!value ? 1 : 0;
  103. },
  104. dropTableQuery: function(tableName, options) {
  105. options = options || {}
  106. var query = "DROP TABLE IF EXISTS <%= table %>;"
  107. return Utils._.template(query)({
  108. table: this.quoteIdentifier(tableName)
  109. })
  110. },
  111. uniqueConstraintMapping: {
  112. code: 'SQLITE_CONSTRAINT',
  113. map: function(str) {
  114. var match = str.match(/columns (.*?) are/)
  115. if (match === null || match.length < 2) {
  116. return false
  117. }
  118. return match[1].split(', ')
  119. }
  120. },
  121. addLimitAndOffset: function(options, query){
  122. query = query || ""
  123. if (options.offset && !options.limit) {
  124. query += " LIMIT " + options.offset + ", " + 10000000000000;
  125. } else if (options.limit) {
  126. if (options.offset) {
  127. query += " LIMIT " + options.offset + ", " + options.limit
  128. } else {
  129. query += " LIMIT " + options.limit
  130. }
  131. }
  132. return query;
  133. },
  134. addColumnQuery: function() {
  135. var sql = MySqlQueryGenerator.addColumnQuery.apply(this, arguments)
  136. return this.replaceBooleanDefaults(sql)
  137. },
  138. showTablesQuery: function() {
  139. return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"
  140. },
  141. bulkInsertQuery: function(tableName, attrValueHashes, options) {
  142. var query = "INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;"
  143. , tuples = []
  144. , allAttributes = []
  145. Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
  146. Utils._.forOwn(attrValueHash, function(value, key, hash) {
  147. if (allAttributes.indexOf(key) === -1) allAttributes.push(key)
  148. })
  149. })
  150. Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
  151. tuples.push("(" +
  152. allAttributes.map(function (key) {
  153. return this.escape(attrValueHash[key])
  154. }.bind(this)).join(",") +
  155. ")")
  156. }.bind(this))
  157. var replacements = {
  158. ignoreDuplicates: options && options.ignoreDuplicates ? ' OR IGNORE' : '',
  159. table: this.quoteIdentifier(tableName),
  160. attributes: allAttributes.map(function(attr){
  161. return this.quoteIdentifier(attr)
  162. }.bind(this)).join(","),
  163. tuples: tuples
  164. }
  165. return Utils._.template(query)(replacements)
  166. },
  167. updateQuery: function(tableName, attrValueHash, where, options) {
  168. attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
  169. var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
  170. , values = []
  171. for (var key in attrValueHash) {
  172. var value = attrValueHash[key]
  173. values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
  174. }
  175. var replacements = {
  176. table: this.quoteIdentifier(tableName),
  177. values: values.join(","),
  178. where: this.getWhereConditions(where)
  179. }
  180. return Utils._.template(query)(replacements)
  181. },
  182. deleteQuery: function(tableName, where, options) {
  183. options = options || {}
  184. var query = "DELETE FROM <%= table %> WHERE <%= where %>"
  185. var replacements = {
  186. table: this.quoteIdentifier(tableName),
  187. where: this.getWhereConditions(where)
  188. }
  189. return Utils._.template(query)(replacements)
  190. },
  191. attributesToSQL: function(attributes) {
  192. var result = {}
  193. for (var name in attributes) {
  194. var dataType = attributes[name]
  195. if (Utils.isHash(dataType)) {
  196. var template = "<%= type %>"
  197. , replacements = { type: dataType.type }
  198. if (dataType.type.toString() === DataTypes.ENUM.toString()) {
  199. replacements.type = "TEXT"
  200. if (!(Array.isArray(dataType.values) && (dataType.values.length > 0))) {
  201. throw new Error('Values for ENUM haven\'t been defined.')
  202. }
  203. }
  204. if (dataType.hasOwnProperty('allowNull') && !dataType.allowNull && !dataType.primaryKey) {
  205. template += " NOT NULL"
  206. }
  207. if (Utils.defaultValueSchemable(dataType.defaultValue)) {
  208. // TODO thoroughly check that DataTypes.NOW will properly
  209. // get populated on all databases as DEFAULT value
  210. // i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP
  211. template += " DEFAULT <%= defaultValue %>"
  212. replacements.defaultValue = this.escape(dataType.defaultValue)
  213. }
  214. if (dataType.unique === true) {
  215. template += " UNIQUE"
  216. }
  217. if (dataType.primaryKey) {
  218. template += " PRIMARY KEY"
  219. if (dataType.autoIncrement) {
  220. template += ' AUTOINCREMENT'
  221. }
  222. }
  223. if(dataType.references) {
  224. template += " REFERENCES <%= referencesTable %> (<%= referencesKey %>)"
  225. replacements.referencesTable = this.quoteIdentifier(dataType.references)
  226. if(dataType.referencesKey) {
  227. replacements.referencesKey = this.quoteIdentifier(dataType.referencesKey)
  228. } else {
  229. replacements.referencesKey = this.quoteIdentifier('id')
  230. }
  231. if(dataType.onDelete) {
  232. template += " ON DELETE <%= onDeleteAction %>"
  233. replacements.onDeleteAction = dataType.onDelete.toUpperCase()
  234. }
  235. if(dataType.onUpdate) {
  236. template += " ON UPDATE <%= onUpdateAction %>"
  237. replacements.onUpdateAction = dataType.onUpdate.toUpperCase()
  238. }
  239. }
  240. result[name] = Utils._.template(template)(replacements)
  241. } else {
  242. result[name] = dataType
  243. }
  244. }
  245. return result
  246. },
  247. findAutoIncrementField: function(factory) {
  248. var fields = []
  249. for (var name in factory.attributes) {
  250. if (factory.attributes.hasOwnProperty(name)) {
  251. var definition = factory.attributes[name]
  252. if (definition && (definition.indexOf('INTEGER PRIMARY KEY AUTOINCREMENT') === 0)) {
  253. fields.push(name)
  254. }
  255. }
  256. }
  257. return fields
  258. },
  259. showIndexQuery: function(tableName) {
  260. var sql = "PRAGMA INDEX_LIST(<%= tableName %>)"
  261. return Utils._.template(sql, { tableName: tableName })
  262. },
  263. removeIndexQuery: function(tableName, indexNameOrAttributes) {
  264. var sql = "DROP INDEX IF EXISTS <%= indexName %>"
  265. , indexName = indexNameOrAttributes
  266. if (typeof indexName !== 'string') {
  267. indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_'))
  268. }
  269. return Utils._.template(sql, { tableName: tableName, indexName: indexName })
  270. },
  271. describeTableQuery: function(tableName, schema, schemaDelimiter) {
  272. var options = {}
  273. options.schema = schema || null
  274. options.schemaDelimiter = schemaDelimiter || null
  275. var sql = "PRAGMA TABLE_INFO(<%= tableName %>);"
  276. return Utils._.template(sql, { tableName: this.addSchema({tableName: tableName, options: options})})
  277. },
  278. removeColumnQuery: function(tableName, attributes) {
  279. attributes = this.attributesToSQL(attributes)
  280. var backupTableName = tableName + "_backup"
  281. var query = [
  282. this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'),
  283. "INSERT INTO <%= tableName %>_backup SELECT <%= attributeNames %> FROM <%= tableName %>;",
  284. "DROP TABLE <%= tableName %>;",
  285. this.createTableQuery(tableName, attributes),
  286. "INSERT INTO <%= tableName %> SELECT <%= attributeNames %> FROM <%= tableName %>_backup;",
  287. "DROP TABLE <%= tableName %>_backup;"
  288. ].join("")
  289. return Utils._.template(query, {
  290. tableName: tableName,
  291. attributeNames: Utils._.keys(attributes).join(', ')
  292. })
  293. },
  294. renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter, attributes) {
  295. attributes = this.attributesToSQL(attributes)
  296. var backupTableName = tableName + "_backup"
  297. var query = [
  298. this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'),
  299. "INSERT INTO <%= tableName %>_backup SELECT <%= attributeNamesImport %> FROM <%= tableName %>;",
  300. "DROP TABLE <%= tableName %>;",
  301. this.createTableQuery(tableName, attributes),
  302. "INSERT INTO <%= tableName %> SELECT <%= attributeNamesExport %> FROM <%= tableName %>_backup;",
  303. "DROP TABLE <%= tableName %>_backup;"
  304. ].join("")
  305. return Utils._.template(query, {
  306. tableName: tableName,
  307. attributeNamesImport: Utils._.keys(attributes).map(function(attr) {
  308. return (attrNameAfter === attr) ? attrNameBefore + ' AS ' + attr : attr
  309. }.bind(this)).join(', '),
  310. attributeNamesExport: Utils._.keys(attributes).map(function(attr) {
  311. return attr
  312. }.bind(this)).join(', ')
  313. })
  314. },
  315. startTransactionQuery: function(options) {
  316. return "BEGIN TRANSACTION;"
  317. },
  318. setAutocommitQuery: function(value) {
  319. return "-- SQLite does not support SET autocommit."
  320. },
  321. setIsolationLevelQuery: function(value) {
  322. switch (value) {
  323. case Transaction.ISOLATION_LEVELS.REPEATABLE_READ:
  324. return "-- SQLite is not able to choose the isolation level REPEATABLE READ."
  325. case Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED:
  326. return "PRAGMA read_uncommitted = ON;"
  327. case Transaction.ISOLATION_LEVELS.READ_COMMITTED:
  328. return "PRAGMA read_uncommitted = OFF;"
  329. case Transaction.ISOLATION_LEVELS.SERIALIZABLE:
  330. return "-- SQLite's default isolation level is SERIALIZABLE. Nothing to do."
  331. default:
  332. throw new Error('Unknown isolation level: ' + value)
  333. }
  334. },
  335. replaceBooleanDefaults: function(sql) {
  336. return sql.replace(/DEFAULT '?false'?/g, "DEFAULT 0").replace(/DEFAULT '?true'?/g, "DEFAULT 1")
  337. },
  338. quoteIdentifier: function(identifier, force) {
  339. if (identifier === '*') return identifier
  340. return Utils.addTicks(identifier, "`")
  341. },
  342. quoteIdentifiers: function(identifiers, force) {
  343. return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
  344. },
  345. quoteTable: function(table) {
  346. return this.quoteIdentifier(table)
  347. },
  348. /**
  349. * Generates an SQL query that returns all foreign keys of a table.
  350. *
  351. * @param {String} tableName The name of the table.
  352. * @param {String} schemaName The name of the schema.
  353. * @return {String} The generated sql query.
  354. */
  355. getForeignKeysQuery: function(tableName, schemaName) {
  356. var sql = "PRAGMA foreign_key_list(<%= tableName %>)"
  357. return Utils._.template(sql, { tableName: tableName })
  358. }
  359. }
  360. return Utils._.extend({}, MySqlQueryGenerator, QueryGenerator)
  361. })()