123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- var Utils = require("../../utils")
- , DataTypes = require("../../data-types")
- , util = require("util")
- module.exports = (function() {
- var QueryGenerator = {
- dialect: 'mysql',
- addSchema: function(opts) {
- var tableName
- var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined)
- var schemaDelimiter = (!!opts && !!opts.options && !!opts.options.schemaDelimiter ? opts.options.schemaDelimiter : undefined)
- if (!!opts && !!opts.tableName) {
- tableName = opts.tableName
- }
- else if (typeof opts === "string") {
- tableName = opts
- }
- if (!schema || schema.toString().trim() === "") {
- return tableName
- }
- return this.quoteIdentifier(schema + (!schemaDelimiter ? '.' : schemaDelimiter) + tableName, false)
- },
- createSchema: function() {
- var query = "SHOW TABLES"
- return Utils._.template(query)({})
- },
- dropSchema: function(tableName, options) {
- return QueryGenerator.dropTableQuery(tableName, options)
- },
- showSchemasQuery: function() {
- return "SHOW TABLES"
- },
- createTableQuery: function(tableName, attributes, options) {
- options = Utils._.extend({
- engine: 'InnoDB',
- charset: null
- }, options || {})
- var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comment %> ENGINE=<%= engine %> <%= charset %> <%= collation %>"
- , primaryKeys = []
- , foreignKeys = {}
- , attrStr = []
- for (var attr in attributes) {
- if (attributes.hasOwnProperty(attr)) {
- var dataType = this.mysqlDataTypeMapping(tableName, attr, attributes[attr])
- if (Utils._.includes(dataType, 'PRIMARY KEY')) {
- primaryKeys.push(attr)
- dataType = dataType.replace(/PRIMARY KEY/, '');
- }
- if (Utils._.includes(dataType, 'REFERENCES')) {
- // MySQL doesn't support inline REFERENCES declarations: move to the end
- var m = dataType.match(/^(.+) (REFERENCES.*)$/)
- dataType = m[1];
- foreignKeys[attr] = m[2]
- }
- attrStr.push(this.quoteIdentifier(attr) + " " + dataType)
- }
- }
- var values = {
- table: this.quoteIdentifier(tableName),
- attributes: attrStr.join(", "),
- comment: options.comment && Utils._.isString(options.comment) ? " COMMENT " + this.escape(options.comment) : "",
- engine: options.engine,
- charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : ""),
- collation: (options.collate ? "COLLATE " + options.collate : "")
- }
- , pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk) }.bind(this)).join(", ")
- if (!!options.uniqueKeys) {
- Utils._.each(options.uniqueKeys, function(columns) {
- values.attributes += ", UNIQUE uniq_" + tableName + '_' + columns.fields.join('_') + " (" + columns.fields.join(', ') + ")"
- })
- }
- if (pkString.length > 0) {
- values.attributes += ", PRIMARY KEY (" + pkString + ")"
- }
- for (var fkey in foreignKeys) {
- if(foreignKeys.hasOwnProperty(fkey)) {
- values.attributes += ", FOREIGN KEY (" + this.quoteIdentifier(fkey) + ") " + foreignKeys[fkey]
- }
- }
- return Utils._.template(query)(values).trim() + ";"
- },
- dropTableQuery: function(tableName, options) {
- options = options || {}
- var query = "DROP TABLE IF EXISTS <%= table %>;"
- return Utils._.template(query)({
- table: this.quoteIdentifier(tableName)
- })
- },
- showTablesQuery: function() {
- return 'SHOW TABLES;'
- },
- uniqueConstraintMapping: {
- code: 'ER_DUP_ENTRY',
- map: function(str) {
- // we're manually remvoving uniq_ here for a future capability of defining column names explicitly
- var match = str.replace('uniq_', '').match(/Duplicate entry .* for key '(.*?)'$/)
- if (match === null || match.length < 2) {
- return false
- }
- return match[1].split('_')
- }
- },
- addColumnQuery: function(tableName, attributes) {
- var query = "ALTER TABLE `<%= tableName %>` ADD <%= attributes %>;"
- , attrString = []
- for (var attrName in attributes) {
- var definition = attributes[attrName]
- attrString.push(Utils._.template('`<%= attrName %>` <%= definition %>')({
- attrName: attrName,
- definition: this.mysqlDataTypeMapping(tableName, attrName, definition)
- }))
- }
- return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
- },
- removeColumnQuery: function(tableName, attributeName) {
- var query = "ALTER TABLE `<%= tableName %>` DROP `<%= attributeName %>`;"
- return Utils._.template(query)({ tableName: tableName, attributeName: attributeName })
- },
- changeColumnQuery: function(tableName, attributes) {
- var query = "ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;"
- var attrString = []
- for (var attrName in attributes) {
- var definition = attributes[attrName]
- attrString.push(Utils._.template('`<%= attrName %>` `<%= attrName %>` <%= definition %>')({
- attrName: attrName,
- definition: definition
- }))
- }
- return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
- },
- renameColumnQuery: function(tableName, attrBefore, attributes) {
- var query = "ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;"
- var attrString = []
- for (var attrName in attributes) {
- var definition = attributes[attrName]
- attrString.push(Utils._.template('`<%= before %>` `<%= after %>` <%= definition %>')({
- before: attrBefore,
- after: attrName,
- definition: definition
- }))
- }
- return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
- },
- bulkInsertQuery: function(tableName, attrValueHashes, options) {
- var query = "INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;"
- , tuples = []
- , allAttributes = []
- Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
- Utils._.forOwn(attrValueHash, function(value, key, hash) {
- if (allAttributes.indexOf(key) === -1) allAttributes.push(key)
- })
- })
- Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
- tuples.push("(" +
- allAttributes.map(function (key) {
- return this.escape(attrValueHash[key])
- }.bind(this)).join(",") +
- ")")
- }.bind(this))
- var replacements = {
- ignoreDuplicates: options && options.ignoreDuplicates ? ' IGNORE' : '',
- table: this.quoteIdentifier(tableName),
- attributes: allAttributes.map(function(attr){
- return this.quoteIdentifier(attr)
- }.bind(this)).join(","),
- tuples: tuples
- }
- return Utils._.template(query)(replacements)
- },
- deleteQuery: function(tableName, where, options) {
- options = options || {}
- var table = this.quoteIdentifier(tableName)
- if (options.truncate === true) {
- // Truncate does not allow LIMIT and WHERE
- return "TRUNCATE " + table
- }
- where = this.getWhereConditions(where)
- var limit = ""
- if(Utils._.isUndefined(options.limit)) {
- options.limit = 1;
- }
- if(!!options.limit) {
- limit = " LIMIT " + this.escape(options.limit)
- }
- return "DELETE FROM " + table + " WHERE " + where + limit
- },
- bulkDeleteQuery: function(tableName, where, options) {
- options = options || {}
- var table = this.quoteIdentifier(tableName)
- where = this.getWhereConditions(where)
- var query = "DELETE FROM " + table + " WHERE " + where
- return query
- },
- addIndexQuery: function(tableName, attributes, options) {
- var transformedAttributes = attributes.map(function(attribute) {
- if(typeof attribute === 'string') {
- return this.quoteIdentifier(attribute)
- } else {
- var result = ""
- if (!attribute.attribute) {
- throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute))
- }
- result += this.quoteIdentifier(attribute.attribute)
- if (attribute.length) {
- result += '(' + attribute.length + ')'
- }
- if (attribute.order) {
- result += ' ' + attribute.order
- }
- return result
- }
- }.bind(this))
- var onlyAttributeNames = attributes.map(function(attribute) {
- return (typeof attribute === 'string') ? attribute : attribute.attribute
- }.bind(this))
- options = Utils._.extend({
- indicesType: null,
- indexName: Utils._.underscored(tableName + '_' + onlyAttributeNames.join('_')),
- parser: null
- }, options || {})
- return Utils._.compact([
- "CREATE", options.indicesType, "INDEX", options.indexName,
- (options.indexType ? ('USING ' + options.indexType) : undefined),
- "ON", tableName, '(' + transformedAttributes.join(', ') + ')',
- (options.parser ? "WITH PARSER " + options.parser : undefined)
- ]).join(' ')
- },
- showIndexQuery: function(tableName, options) {
- var sql = "SHOW INDEX FROM `<%= tableName %>`<%= options %>"
- return Utils._.template(sql)({
- tableName: tableName,
- options: (options || {}).database ? ' FROM `' + options.database + '`' : ''
- })
- },
- removeIndexQuery: function(tableName, indexNameOrAttributes) {
- var sql = "DROP INDEX <%= indexName %> ON <%= tableName %>"
- , indexName = indexNameOrAttributes
- if (typeof indexName !== 'string') {
- indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_'))
- }
- return Utils._.template(sql)({ tableName: tableName, indexName: indexName })
- },
- attributesToSQL: function(attributes) {
- var result = {}
- for (var name in attributes) {
- var dataType = attributes[name]
- if (Utils.isHash(dataType)) {
- var template
- if (dataType.type.toString() === DataTypes.ENUM.toString()) {
- if (Array.isArray(dataType.values) && (dataType.values.length > 0)) {
- template = "ENUM(" + Utils._.map(dataType.values, function(value) {
- return this.escape(value)
- }.bind(this)).join(", ") + ")"
- } else {
- throw new Error('Values for ENUM haven\'t been defined.')
- }
- } else {
- template = dataType.type.toString();
- }
- if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) {
- template += " NOT NULL"
- }
- if (dataType.autoIncrement) {
- template += " auto_increment"
- }
- // Blobs/texts cannot have a defaultValue
- if (dataType.type !== "TEXT" && dataType.type._binary !== true && Utils.defaultValueSchemable(dataType.defaultValue)) {
- template += " DEFAULT " + this.escape(dataType.defaultValue)
- }
- if (dataType.unique === true) {
- template += " UNIQUE"
- }
- if (dataType.primaryKey) {
- template += " PRIMARY KEY"
- }
- if(dataType.references) {
- template += " REFERENCES " + this.quoteIdentifier(dataType.references)
- if(dataType.referencesKey) {
- template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")"
- } else {
- template += " (" + this.quoteIdentifier('id') + ")"
- }
- if(dataType.onDelete) {
- template += " ON DELETE " + dataType.onDelete.toUpperCase()
- }
- if(dataType.onUpdate) {
- template += " ON UPDATE " + dataType.onUpdate.toUpperCase()
- }
- }
- if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) {
- template += " COMMENT " + this.escape(dataType.comment)
- }
- result[name] = template
- } else {
- result[name] = dataType
- }
- }
- return result
- },
- findAutoIncrementField: function(factory) {
- var fields = []
- for (var name in factory.attributes) {
- if (factory.attributes.hasOwnProperty(name)) {
- var definition = factory.attributes[name]
- if (definition && (definition.indexOf('auto_increment') > -1)) {
- fields.push(name)
- }
- }
- }
- return fields
- },
- addLimitAndOffset: function(options, query){
- query = query || ""
- if (options.offset && !options.limit) {
- query += " LIMIT " + options.offset + ", " + 18440000000000000000;
- } else if (options.limit) {
- if (options.offset) {
- query += " LIMIT " + options.offset + ", " + options.limit
- } else {
- query += " LIMIT " + options.limit
- }
- }
- return query;
- },
- quoteIdentifier: function(identifier, force) {
- if (identifier === '*') return identifier
- return Utils.addTicks(identifier, "`")
- },
- quoteIdentifiers: function(identifiers, force) {
- return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
- },
- quoteTable: function(table) {
- return this.quoteIdentifier(table)
- },
- /**
- * Generates an SQL query that returns all foreign keys of a table.
- *
- * @param {String} tableName The name of the table.
- * @param {String} schemaName The name of the schema.
- * @return {String} The generated sql query.
- */
- getForeignKeysQuery: function(tableName, schemaName) {
- return "SELECT CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '" + tableName + "' AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='" + schemaName + "' AND REFERENCED_TABLE_NAME IS NOT NULL;"
- },
- /**
- * Generates an SQL query that removes a foreign key from a table.
- *
- * @param {String} tableName The name of the table.
- * @param {String} foreignKey The name of the foreign key constraint.
- * @return {String} The generated sql query.
- */
- dropForeignKeyQuery: function(tableName, foreignKey) {
- return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';'
- },
- mysqlDataTypeMapping: function(tableName, attr, dataType) {
- if (Utils._.includes(dataType, 'UUID')) {
- dataType = dataType.replace(/UUID/, 'CHAR(36) BINARY')
- }
- return dataType
- }
- }
- return Utils._.extend(Utils._.clone(require("../abstract/query-generator")), QueryGenerator)
- })()
|