12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175 |
- var Utils = require("../../utils")
- , SqlString = require("../../sql-string")
- , daoFactory = require("../../dao-factory")
- module.exports = (function() {
- var QueryGenerator = {
- addSchema: function(opts) {
- throwMethodUndefined('addSchema')
- },
- /*
- Returns a query for creating a table.
- Parameters:
- - tableName: Name of the new table.
- - attributes: An object with containing attribute-attributeType-pairs.
- Attributes should have the format:
- {attributeName: type, attr2: type2}
- --> e.g. {title: 'VARCHAR(255)'}
- - options: An object with options.
- Defaults: { engine: 'InnoDB', charset: null }
- */
- createTableQuery: function(tableName, attributes, options) {
- throwMethodUndefined('createTableQuery')
- },
- /*
- Returns a query for dropping a table.
- */
- dropTableQuery: function(tableName, options) {
- throwMethodUndefined('dropTableQuery')
- },
- /*
- Returns a rename table query.
- Parameters:
- - originalTableName: Name of the table before execution.
- - futureTableName: Name of the table after execution.
- */
- renameTableQuery: function(before, after) {
- var query = "ALTER TABLE <%= before %> RENAME TO <%= after %>;"
- return Utils._.template(query)({
- before: this.quoteTable(before),
- after: this.quoteTable(after)
- })
- },
- /*
- Returns a query, which gets all available table names in the database.
- */
- showTablesQuery: function() {
- throwMethodUndefined('showTablesQuery')
- },
- /*
- Returns a query, which adds an attribute to an existing table.
- Parameters:
- - tableName: Name of the existing table.
- - attributes: A hash with attribute-attributeOptions-pairs.
- - key: attributeName
- - value: A hash with attribute specific options:
- - type: DataType
- - defaultValue: A String with the default value
- - allowNull: Boolean
- */
- addColumnQuery: function(tableName, attributes) {
- throwMethodUndefined('addColumnQuery')
- },
- /*
- Returns a query, which removes an attribute from an existing table.
- Parameters:
- - tableName: Name of the existing table
- - attributeName: Name of the obsolete attribute.
- */
- removeColumnQuery: function(tableName, attributeName) {
- throwMethodUndefined('removeColumnQuery')
- },
- /*
- Returns a query, which modifies an existing attribute from a table.
- Parameters:
- - tableName: Name of the existing table.
- - attributes: A hash with attribute-attributeOptions-pairs.
- - key: attributeName
- - value: A hash with attribute specific options:
- - type: DataType
- - defaultValue: A String with the default value
- - allowNull: Boolean
- */
- changeColumnQuery: function(tableName, attributes) {
- throwMethodUndefined('changeColumnQuery')
- },
- /*
- Returns a query, which renames an existing attribute.
- Parameters:
- - tableName: Name of an existing table.
- - attrNameBefore: The name of the attribute, which shall be renamed.
- - attrNameAfter: The name of the attribute, after renaming.
- */
- renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter) {
- throwMethodUndefined('renameColumnQuery')
- },
- /*
- Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.
- */
- insertQuery: function(table, valueHash, modelAttributes) {
- var query
- , valueQuery = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>)"
- , emptyQuery = "INSERT INTO <%= table %>"
- , fields = []
- , values = []
- , key
- , value
- if (this._dialect.supports['DEFAULT VALUES']) {
- emptyQuery += " DEFAULT VALUES"
- } else if (this._dialect.supports['VALUES ()']) {
- emptyQuery += " VALUES ()"
- }
- if (this._dialect.supports['RETURNING']) {
- valueQuery += " RETURNING *"
- emptyQuery += " RETURNING *"
- }
- valueHash = Utils.removeNullValuesFromHash(valueHash, this.options.omitNull)
- for (key in valueHash) {
- if (valueHash.hasOwnProperty(key)) {
- value = valueHash[key]
- fields.push(this.quoteIdentifier(key))
- // SERIALS' can't be NULL in postgresql, use DEFAULT where supported
- if (modelAttributes && modelAttributes[key] && modelAttributes[key].autoIncrement === true && !value) {
- if (this._dialect.supports['DEFAULT']) {
- values.push('DEFAULT')
- } else {
- values.push(this.escape(null))
- }
- } else {
- values.push(this.escape(value, (modelAttributes && modelAttributes[key]) || undefined))
- }
- }
- }
- var replacements = {
- table: this.quoteTable(table),
- attributes: fields.join(","),
- values: values.join(",")
- }
- query = (replacements.attributes.length ? valueQuery : emptyQuery) + ";"
- return Utils._.template(query)(replacements)
- },
- /*
- Returns an insert into command for multiple values.
- Parameters: table name + list of hashes of attribute-value-pairs.
- */
- bulkInsertQuery: function(tableName, attrValueHashes) {
- throwMethodUndefined('bulkInsertQuery')
- },
- /*
- Returns an update query.
- Parameters:
- - tableName -> Name of the table
- - values -> A hash with attribute-value-pairs
- - where -> A hash with conditions (e.g. {name: 'foo'})
- OR an ID as integer
- OR a string with conditions (e.g. 'name="foo"').
- If you use a string, you have to escape it on your own.
- */
- updateQuery: function(tableName, attrValueHash, where, options, attributes) {
- options = options || {}
- attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
- var query
- , values = []
- query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
- if (this._dialect.supports['RETURNING'] && (options.returning || options.returning === undefined)) {
- query += " RETURNING *"
- }
- for (var key in attrValueHash) {
- var value = attrValueHash[key]
- values.push(this.quoteIdentifier(key) + "=" + this.escape(value, (!!attributes && !!attributes[key] ? attributes[key] : undefined)))
- }
- var replacements = {
- table: this.quoteTable(tableName),
- values: values.join(","),
- where: this.getWhereConditions(where)
- }
- return Utils._.template(query)(replacements)
- },
- /*
- Returns a deletion query.
- Parameters:
- - tableName -> Name of the table
- - where -> A hash with conditions (e.g. {name: 'foo'})
- OR an ID as integer
- OR a string with conditions (e.g. 'name="foo"').
- If you use a string, you have to escape it on your own.
- Options:
- - limit -> Maximaum count of lines to delete
- - truncate -> boolean - whether to use an 'optimized' mechanism (i.e. TRUNCATE) if available,
- note that this should not be the default behaviour because TRUNCATE does not
- always play nicely (e.g. InnoDB tables with FK constraints)
- (@see http://dev.mysql.com/doc/refman/5.6/en/truncate-table.html).
- Note that truncate must ignore limit and where
- */
- deleteQuery: function(tableName, where, options) {
- throwMethodUndefined('deleteQuery')
- },
- /*
- Returns a bulk deletion query.
- Parameters:
- - tableName -> Name of the table
- - where -> A hash with conditions (e.g. {name: 'foo'})
- OR an ID as integer
- OR a string with conditions (e.g. 'name="foo"').
- If you use a string, you have to escape it on your own.
- */
- bulkDeleteQuery: function(tableName, where, options) {
- throwMethodUndefined('bulkDeleteQuery')
- },
- /*
- Returns an update query.
- Parameters:
- - tableName -> Name of the table
- - values -> A hash with attribute-value-pairs
- - where -> A hash with conditions (e.g. {name: 'foo'})
- OR an ID as integer
- OR a string with conditions (e.g. 'name="foo"').
- If you use a string, you have to escape it on your own.
- */
- incrementQuery: function(tableName, attrValueHash, where, options) {
- attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
- var query
- , values = []
- query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
- if (this._dialect.supports['RETURNING']) {
- query += " RETURNING *"
- }
- for (var key in attrValueHash) {
- var value = attrValueHash[key]
- values.push(this.quoteIdentifier(key) + "=" + this.quoteIdentifier(key) + " + " + this.escape(value))
- }
- options = options || {}
- for (var key in options) {
- var value = options[key];
- values.push(this.quoteIdentifier(key) + "=" + this.escape(value))
- }
- var replacements = {
- table: this.quoteIdentifiers(tableName),
- values: values.join(","),
- where: this.getWhereConditions(where)
- }
- return Utils._.template(query)(replacements)
- },
- /*
- Returns an add index query.
- Parameters:
- - tableName -> Name of an existing table.
- - attributes:
- An array of attributes as string or as hash.
- If the attribute is a hash, it must have the following content:
- - attribute: The name of the attribute/column
- - length: An integer. Optional
- - order: 'ASC' or 'DESC'. Optional
- - options:
- - indicesType: UNIQUE|FULLTEXT|SPATIAL
- - indexName: The name of the index. Default is <tableName>_<attrName1>_<attrName2>
- - parser
- */
- addIndexQuery: function(tableName, attributes, options) {
- throwMethodUndefined('addIndexQuery')
- },
- /*
- Returns an show index query.
- Parameters:
- - tableName: Name of an existing table.
- - options:
- - database: Name of the database.
- */
- showIndexQuery: function(tableName, options) {
- throwMethodUndefined('showIndexQuery')
- },
- /*
- Returns a remove index query.
- Parameters:
- - tableName: Name of an existing table.
- - indexNameOrAttributes: The name of the index as string or an array of attribute names.
- */
- removeIndexQuery: function(tableName, indexNameOrAttributes) {
- throwMethodUndefined('removeIndexQuery')
- },
- /*
- This method transforms an array of attribute hashes into equivalent
- sql attribute definition.
- */
- attributesToSQL: function(attributes) {
- throwMethodUndefined('attributesToSQL')
- },
- /*
- Returns all auto increment fields of a factory.
- */
- findAutoIncrementField: function(factory) {
- throwMethodUndefined('findAutoIncrementField')
- },
- /*
- Quote an object based on its type. This is a more general version of quoteIdentifiers
- Strings: should proxy to quoteIdentifiers
- Arrays:
- * Expects array in the form: [<model> (optional), <model> (optional),... String, String (optional)]
- Each <model> can be a daoFactory or an object {model: DaoFactory, as: String}, matching include
- * Zero or more models can be included in the array and are used to trace a path through the tree of
- included nested associations. This produces the correct table name for the ORDER BY/GROUP BY SQL
- and quotes it.
- * If a single string is appended to end of array, it is quoted.
- If two strings appended, the 1st string is quoted, the 2nd string unquoted.
- Objects:
- * If raw is set, that value should be returned verbatim, without quoting
- * If fn is set, the string should start with the value of fn, starting paren, followed by
- the values of cols (which is assumed to be an array), quoted and joined with ', ',
- unless they are themselves objects
- * If direction is set, should be prepended
- Currently this function is only used for ordering / grouping columns and Sequelize.col(), but it could
- potentially also be used for other places where we want to be able to call SQL functions (e.g. as default values)
- */
- quote: function(obj, parent, force) {
- if (Utils._.isString(obj)) {
- return this.quoteIdentifiers(obj, force)
- } else if (Array.isArray(obj)) {
- // loop through array, adding table names of models to quoted
- // (checking associations to see if names should be singularised or not)
- var tableNames = []
- , parentAssociation
- , len = obj.length
- for (var i = 0; i < len - 1; i++) {
- var item = obj[i]
- if (Utils._.isString(item) || item instanceof Utils.fn || item instanceof Utils.col || item instanceof Utils.literal || item instanceof Utils.cast || 'raw' in item) {
- break
- }
- var model, as
- if (item instanceof daoFactory) {
- model = item
- } else {
- model = item.model
- as = item.as
- }
- // check if model provided is through table
- var association
- if (!as && parentAssociation && parentAssociation.through === model) {
- association = {as: Utils.singularize(model.tableName, model.options.language)}
- } else {
- // find applicable association for linking parent to this model
- association = parent.getAssociation(model, as)
- }
- if (association) {
- tableNames[i] = association.as
- parent = model
- parentAssociation = association
- } else {
- tableNames[i] = model.tableName
- throw new Error('\'' + tableNames.join('.') + '\' in order / group clause is not valid association')
- }
- }
- // add 1st string as quoted, 2nd as unquoted raw
- var sql = (i > 0 ? this.quoteIdentifier(tableNames.join('.')) + '.' : '') + this.quote(obj[i], parent, force)
- if (i < len - 1) {
- sql += ' ' + obj[i + 1]
- }
- return sql
- } else if (obj instanceof Utils.fn || obj instanceof Utils.col || obj instanceof Utils.literal || obj instanceof Utils.cast) {
- return obj.toString(this)
- } else if (Utils._.isObject(obj) && 'raw' in obj) {
- return obj.raw
- } else {
- throw new Error('Unknown structure passed to order / group: ' + JSON.stringify(obj))
- }
- },
- /*
- Create a trigger
- */
- createTrigger: function(tableName, triggerName, timingType, fireOnArray, functionName, functionParams,
- optionsArray) {
- throwMethodUndefined('createTrigger')
- },
- /*
- Drop a trigger
- */
- dropTrigger: function(tableName, triggerName) {
- throwMethodUndefined('dropTrigger')
- },
- /*
- Rename a trigger
- */
- renameTrigger: function(tableName, oldTriggerName, newTriggerName) {
- throwMethodUndefined('renameTrigger')
- },
- /*
- Create a function
- */
- createFunction: function(functionName, params, returnType, language, body, options) {
- throwMethodUndefined('createFunction')
- },
- /*
- Drop a function
- */
- dropFunction: function(functionName, params) {
- throwMethodUndefined('dropFunction')
- },
- /*
- Rename a function
- */
- renameFunction: function(oldFunctionName, params, newFunctionName) {
- throwMethodUndefined('renameFunction')
- },
- /*
- Escape an identifier (e.g. a table or attribute name)
- */
- quoteIdentifier: function(identifier, force) {
- throwMethodUndefined('quoteIdentifier')
- },
- /*
- Split an identifier into .-separated tokens and quote each part
- */
- quoteIdentifiers: function(identifiers, force) {
- throwMethodUndefined('quoteIdentifiers')
- },
- /*
- Escape a value (e.g. a string, number or date)
- */
- escape: function(value, field) {
- if (value instanceof Utils.fn || value instanceof Utils.col || value instanceof Utils.literal || value instanceof Utils.cast) {
- return value.toString(this)
- } else {
- return SqlString.escape(value, false, null, this.dialect, field)
- }
- },
- /**
- * 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) {
- throwMethodUndefined('getForeignKeysQuery')
- },
- /**
- * 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) {
- throwMethodUndefined('dropForeignKeyQuery')
- },
- /*
- Returns a query for selecting elements in the table <tableName>.
- Options:
- - attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: *
- - where -> A hash with conditions (e.g. {name: 'foo'})
- OR an ID as integer
- OR a string with conditions (e.g. 'name="foo"').
- If you use a string, you have to escape it on your own.
- - order -> e.g. 'id DESC'
- - group
- - limit -> The maximum count you want to get.
- - offset -> An offset value to start from. Only useable with limit!
- */
- selectQuery: function(tableName, options, factory) {
- // Enter and change at your own peril -- Mick Hansen
- options = options || {}
- var table = null
- , self = this
- , query
- , limit = options.limit
- , mainQueryItems = []
- , mainAttributes = options.attributes
- , mainJoinQueries = []
- // We'll use a subquery if we have hasMany associations and a limit and a filtered/required association
- , subQuery = limit && (options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation)
- , subQueryItems = []
- , subQueryAttributes = null
- , subJoinQueries = []
- // Escape table
- options.table = table = !Array.isArray(tableName) ? this.quoteIdentifiers(tableName) : tableName.map(function(t) {
- return this.quoteIdentifiers(t)
- }.bind(this)).join(", ")
- if (subQuery && mainAttributes) {
- if (factory.hasPrimaryKeys) {
- factory.primaryKeyAttributes.forEach(function(keyAtt){
- if(mainAttributes.indexOf(keyAtt) == -1){
- mainAttributes.push(keyAtt)
- }
- })
- } else {
- mainAttributes.push("id")
- }
- }
- // Escape attributes
- mainAttributes = mainAttributes && mainAttributes.map(function(attr){
- var addTable = true
- if (attr instanceof Utils.literal) {
- return attr.toString(this)
- }
- if (attr instanceof Utils.fn || attr instanceof Utils.col) {
- return attr.toString(self)
- }
- if(Array.isArray(attr) && attr.length == 2) {
- if (attr[0] instanceof Utils.fn || attr[0] instanceof Utils.col) {
- attr[0] = attr[0].toString(self)
- addTable = false
- }
- attr = [attr[0], this.quoteIdentifier(attr[1])].join(' as ')
- } else {
- attr = attr.indexOf(Utils.TICK_CHAR) < 0 && attr.indexOf('"') < 0 ? this.quoteIdentifiers(attr) : attr
- }
- if (options.include && attr.indexOf('.') === -1 && addTable) {
- attr = this.quoteIdentifier(options.table) + '.' + attr
- }
- return attr
- }.bind(this))
- // If no attributes specified, use *
- mainAttributes = mainAttributes || (options.include ? [options.table+'.*'] : ['*'])
- // If subquery, we ad the mainAttributes to the subQuery and set the mainAttributes to select * from subquery
- if (subQuery) {
- // We need primary keys
- subQueryAttributes = mainAttributes
- mainAttributes = [options.table+'.*']
- }
- if (options.include) {
- var generateJoinQueries = function(include, parentTable) {
- var table = include.daoFactory.tableName
- , as = include.as
- , joinQueryItem = ""
- , joinQueries = {
- mainQuery: [],
- subQuery: []
- }
- , attributes
- , association = include.association
- , through = include.through
- , joinType = include.required ? ' INNER JOIN ' : ' LEFT OUTER JOIN '
- , includeWhere = {}
- , whereOptions = Utils._.clone(options)
- whereOptions.keysEscaped = true
- if (tableName !== parentTable) {
- as = parentTable+'.'+include.as
- }
- // includeIgnoreAttributes is used by aggregate functions
- if (options.includeIgnoreAttributes !== false) {
- attributes = include.attributes.map(function(attr) {
- return self.quoteIdentifier(as) + "." + self.quoteIdentifier(attr) + " AS " + self.quoteIdentifier(as + "." + attr)
- })
- if (include.subQuery && subQuery) {
- subQueryAttributes = subQueryAttributes.concat(attributes)
- } else {
- mainAttributes = mainAttributes.concat(attributes)
- }
- }
- if (through) {
- var throughTable = through.daoFactory.tableName
- , throughAs = as + "." + through.as
- , throughAttributes = through.attributes.map(function(attr) {
- return self.quoteIdentifier(throughAs) + "." + self.quoteIdentifier(attr) + " AS " + self.quoteIdentifier(throughAs + "." + attr)
- })
- , primaryKeysSource = Object.keys(association.source.primaryKeys)
- , tableSource = parentTable
- , identSource = association.identifier
- , attrSource = ((!association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0])
- , where
- , primaryKeysTarget = Object.keys(association.target.primaryKeys)
- , tableTarget = as
- , identTarget = association.foreignIdentifier
- , attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0])
- , sourceJoinOn
- , targetJoinOn
- , targetWhere
- if (options.includeIgnoreAttributes !== false) {
- // Through includes are always hasMany, so we need to add the attributes to the mainAttributes no matter what (Real join will never be executed in subquery)
- mainAttributes = mainAttributes.concat(throughAttributes)
- }
- // Filter statement for left side of through
- // Used by both join and subquery where
- sourceJoinOn = self.quoteIdentifier(tableSource) + "." + self.quoteIdentifier(attrSource) + " = "
- sourceJoinOn += self.quoteIdentifier(throughAs) + "." + self.quoteIdentifier(identSource)
- // Filter statement for right side of through
- // Used by both join and subquery where
- targetJoinOn = self.quoteIdentifier(tableTarget) + "." + self.quoteIdentifier(attrTarget) + " = "
- targetJoinOn += self.quoteIdentifier(throughAs) + "." + self.quoteIdentifier(identTarget)
- // Generate join SQL for left side of through
- joinQueryItem += joinType + self.quoteIdentifier(throughTable) + " AS " + self.quoteIdentifier(throughAs) + " ON "
- joinQueryItem += sourceJoinOn
- // Generate join SQL for right side of through
- joinQueryItem += joinType + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON "
- joinQueryItem += targetJoinOn
- if (include.where) {
- targetWhere = self.getWhereConditions(include.where, self.sequelize.literal(self.quoteIdentifier(as)), include.daoFactory, whereOptions)
- joinQueryItem += " AND "+ targetWhere
- if (subQuery) {
- if (!options.where) options.where = {}
- // Creating the as-is where for the subQuery, checks that the required association exists
- var _where = "(";
- _where += "SELECT "+self.quoteIdentifier(identSource)+" FROM " + self.quoteIdentifier(throughTable) + " AS " + self.quoteIdentifier(throughAs);
- _where += joinType + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON "+targetJoinOn;
- _where += " WHERE " + sourceJoinOn + " AND " + targetWhere + " LIMIT 1"
- _where += ")";
- _where += " IS NOT NULL"
- options.where["__"+throughAs] = self.sequelize.asIs(_where)
- }
- }
- } else {
- var primaryKeysLeft = ((association.associationType === 'BelongsTo') ? Object.keys(association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys))
- , tableLeft = ((association.associationType === 'BelongsTo') ? as : parentTable)
- , attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0])
- , tableRight = ((association.associationType === 'BelongsTo') ? parentTable : as)
- , attrRight = association.identifier
- , where
- // Filter statement
- // Used by both join and subquery where
- if (subQuery && !include.subQuery && include.parent.subQuery) {
- where = self.quoteIdentifier(tableLeft + "." + attrLeft) + " = "
- } else {
- where = self.quoteIdentifier(tableLeft) + "." + self.quoteIdentifier(attrLeft) + " = "
- }
- where += self.quoteIdentifier(tableRight) + "." + self.quoteIdentifier(attrRight)
- // Generate join SQL
- joinQueryItem += joinType + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON "
- joinQueryItem += where
- if (include.where) {
- joinQueryItem += " AND "+self.getWhereConditions(include.where, self.sequelize.literal(self.quoteIdentifier(as)), include.daoFactory, whereOptions)
- // If its a multi association we need to add a where query to the main where (executed in the subquery)
- if (subQuery && association.isMultiAssociation) {
- if (!options.where) options.where = {}
- // Creating the as-is where for the subQuery, checks that the required association exists
- options.where["__"+as] = self.sequelize.asIs("(SELECT "+self.quoteIdentifier(attrRight)+" FROM " + self.quoteIdentifier(tableRight) + " WHERE " + where + " LIMIT 1) IS NOT NULL")
- }
- }
- }
- if (include.subQuery && subQuery) {
- joinQueries.subQuery.push(joinQueryItem);
- } else {
- joinQueries.mainQuery.push(joinQueryItem);
- }
- if (include.include) {
- include.include.forEach(function(childInclude) {
- if (childInclude._pseudo) return
- var childJoinQueries = generateJoinQueries(childInclude, as)
- if (childInclude.subQuery && subQuery) {
- joinQueries.subQuery = joinQueries.subQuery.concat(childJoinQueries.subQuery)
- } else {
- joinQueries.mainQuery = joinQueries.mainQuery.concat(childJoinQueries.mainQuery)
- }
- }.bind(this))
- }
- return joinQueries
- }
- // Loop through includes and generate subqueries
- options.include.forEach(function(include) {
- var joinQueries = generateJoinQueries(include, tableName)
- subJoinQueries = subJoinQueries.concat(joinQueries.subQuery)
- mainJoinQueries = mainJoinQueries.concat(joinQueries.mainQuery)
- }.bind(this))
- }
- // If using subQuery select defined subQuery attributes and join subJoinQueries
- if (subQuery) {
- subQueryItems.push("SELECT " + subQueryAttributes.join(', ') + " FROM " + options.table)
- subQueryItems.push(subJoinQueries.join(''))
- // Else do it the reguar way
- } else {
- mainQueryItems.push("SELECT " + mainAttributes.join(', ') + " FROM " + options.table)
- mainQueryItems.push(mainJoinQueries.join(''))
- }
- // Add WHERE to sub or main query
- if (options.hasOwnProperty('where')) {
- options.where = this.getWhereConditions(options.where, tableName, factory, options)
- if (subQuery) {
- subQueryItems.push(" WHERE " + options.where)
- } else {
- mainQueryItems.push(" WHERE " + options.where)
- }
- }
- // Add GROUP BY to sub or main query
- if (options.group) {
- options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t, factory) }.bind(this)).join(', ') : options.group
- if (subQuery) {
- subQueryItems.push(" GROUP BY " + options.group)
- } else {
- mainQueryItems.push(" GROUP BY " + options.group)
- }
- }
-
- // Add HAVING to sub or main query
- if (options.hasOwnProperty('having')) {
- options.having = this.getWhereConditions(options.having, tableName, factory, options, false)
- if (subQuery) {
- subQueryItems.push(" HAVING " + options.having)
- } else {
- mainQueryItems.push(" HAVING " + options.having)
- }
- }
- // Add ORDER to sub or main query
- if (options.order) {
- var mainQueryOrder = [];
- var subQueryOrder = [];
- if (Array.isArray(options.order)) {
- options.order.forEach(function (t) {
- if (subQuery && !(t[0] instanceof daoFactory) && !(t[0].model instanceof daoFactory)) {
- subQueryOrder.push(this.quote(t, factory))
- }
- mainQueryOrder.push(this.quote(t, factory))
- }.bind(this))
- } else {
- mainQueryOrder.push(options.order)
- }
-
- if (mainQueryOrder.length) {
- mainQueryItems.push(" ORDER BY " + mainQueryOrder.join(', '))
- }
- if (subQueryOrder.length) {
- subQueryItems.push(" ORDER BY " + subQueryOrder.join(', '))
- }
- }
- var limitOrder = this.addLimitAndOffset(options, query)
- // Add LIMIT, OFFSET to sub or main query
- if (limitOrder) {
- if (subQuery) {
- subQueryItems.push(limitOrder)
- } else {
- mainQueryItems.push(limitOrder)
- }
- }
- // If using subQuery, select attributes from wrapped subQuery and join out join tables
- if (subQuery) {
- query = "SELECT " + mainAttributes.join(', ') + " FROM ("
- query += subQueryItems.join('')
- query += ") AS "+options.table
- query += mainJoinQueries.join('')
- query += mainQueryItems.join('')
- } else {
- query = mainQueryItems.join('')
- }
- query += ";";
- return query
- },
- /**
- * Returns a query that starts a transaction.
- *
- * @param {Boolean} value A boolean that states whether autocommit shall be done or not.
- * @return {String} The generated sql query.
- */
- setAutocommitQuery: function(value) {
- return "SET autocommit = " + (!!value ? 1 : 0) + ";"
- },
- setIsolationLevelQuery: function(value) {
- return "SET SESSION TRANSACTION ISOLATION LEVEL " + value + ";"
- },
- /**
- * Returns a query that starts a transaction.
- *
- * @param {Object} options An object with options.
- * @return {String} The generated sql query.
- */
- startTransactionQuery: function(options) {
- return "START TRANSACTION;"
- },
- /**
- * Returns a query that commits a transaction.
- *
- * @param {Object} options An object with options.
- * @return {String} The generated sql query.
- */
- commitTransactionQuery: function(options) {
- return "COMMIT;"
- },
- /**
- * Returns a query that rollbacks a transaction.
- *
- * @param {Object} options An object with options.
- * @return {String} The generated sql query.
- */
- rollbackTransactionQuery: function(options) {
- return "ROLLBACK;"
- },
- addLimitAndOffset: function(options, query) {
- query = query || ""
- if (options.offset && !options.limit) {
- query += " LIMIT " + options.offset + ", " + 10000000000000;
- } else if (options.limit) {
- if (options.offset) {
- query += " LIMIT " + options.offset + ", " + options.limit
- } else {
- query += " LIMIT " + options.limit
- }
- }
- return query;
- },
- /*
- Takes something and transforms it into values of a where condition.
- */
- getWhereConditions: function(smth, tableName, factory, options, prepend) {
- var result = null
- , where = {}
- , self = this
- if (typeof prepend === 'undefined') {
- prepend = true
- }
- if ((smth instanceof Utils.and) || (smth instanceof Utils.or)) {
- var connector = (smth instanceof Utils.and) ? ' AND ' : ' OR '
- result = smth.args.map(function(arg) {
- return self.getWhereConditions(arg, tableName, factory, options, prepend)
- }).join(connector)
- result = "(" + result + ")"
- } else if (Utils.isHash(smth)) {
- if (prepend) {
- smth = Utils.prependTableNameToHash(tableName, smth, self.quoteIdentifier.bind(self))
- }
- result = this.hashToWhereConditions(smth, factory, options)
- } else if (typeof smth === 'number') {
- var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : []
- if (primaryKeys.length > 0) {
- // Since we're just a number, assume only the first key
- primaryKeys = primaryKeys[0]
- } else {
- primaryKeys = 'id'
- }
- where[primaryKeys] = smth
- smth = Utils.prependTableNameToHash(tableName, where)
- result = this.hashToWhereConditions(smth)
- } else if (typeof smth === "string") {
- result = smth
- } else if (Buffer.isBuffer(smth)) {
- result = this.escape(smth)
- } else if (Array.isArray(smth)) {
- var treatAsAnd = smth.reduce(function(treatAsAnd, arg) {
- if (treatAsAnd) {
- return treatAsAnd
- } else {
- return !(arg instanceof Date) && ((arg instanceof Utils.and) || (arg instanceof Utils.or) || Utils.isHash(arg))
- }
- }, false)
- if (treatAsAnd) {
- var _smth = self.sequelize.and.apply(null, smth)
- result = self.getWhereConditions(_smth, tableName, factory, options, prepend)
- } else {
- result = Utils.format(smth, this.dialect)
- }
- }
- return result ? result : '1=1'
- },
- findAssociation: function(attribute, dao){
- var associationToReturn;
- Object.keys(dao.associations).forEach(function(key){
- if(!dao.associations[key]) return;
- var association = dao.associations[key]
- , associationName
- if (association.associationType === 'BelongsTo') {
- associationName = Utils.singularize(association.associationAccessor[0].toLowerCase() + association.associationAccessor.slice(1));
- } else {
- associationName = association.accessors.get.replace('get', '')
- associationName = associationName[0].toLowerCase() + associationName.slice(1);
- }
- if(associationName === attribute){
- associationToReturn = association;
- }
- });
- return associationToReturn;
- },
- getAssociationFilterDAO: function(filterStr, dao){
- var associationParts = filterStr.split('.')
- , self = this
- associationParts.pop()
- associationParts.forEach(function (attribute) {
- dao = self.findAssociation(attribute, dao).target;
- });
- return dao;
- },
- isAssociationFilter: function(filterStr, dao, options){
- if(!dao){
- return false;
- }
- var pattern = /^[a-z][a-zA-Z0-9]+(\.[a-z][a-zA-Z0-9]+)+$/;
- if (!pattern.test(filterStr)) return false;
- var associationParts = filterStr.split('.')
- , attributePart = associationParts.pop()
- , self = this
- return associationParts.every(function (attribute) {
- var association = self.findAssociation(attribute, dao);
- if (!association) return false;
- dao = association.target;
- return !!dao;
- }) && dao.rawAttributes.hasOwnProperty(attributePart);
- },
- getAssociationFilterColumn: function(filterStr, dao, options){
- var associationParts = filterStr.split('.')
- , attributePart = associationParts.pop()
- , self = this
- , association
- , keyParts = []
- associationParts.forEach(function (attribute) {
- association = self.findAssociation(attribute, dao)
- dao = association.target;
- if (options.include) {
- keyParts.push(association.as || association.options.as || dao.tableName)
- }
- })
- if (options.include) {
- return this.quoteIdentifier(keyParts.join('.')) + '.' + this.quoteIdentifiers(attributePart)
- }
- return this.quoteIdentifiers(dao.tableName + '.' + attributePart)
- },
- getConditionalJoins: function(options, originalDao){
- var joins = ''
- , self = this
- , joinedTables = {}
- if (Utils.isHash(options.where)) {
- Object.keys(options.where).forEach(function(filterStr){
- var associationParts = filterStr.split('.')
- , attributePart = associationParts.pop()
- , dao = originalDao
- if (self.isAssociationFilter(filterStr, dao, options)) {
- associationParts.forEach(function (attribute) {
- var association = self.findAssociation(attribute, dao);
- if(!joinedTables[association.target.tableName]){
- joinedTables[association.target.tableName] = true;
- if(association.associationType === 'BelongsTo'){
- joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName)
- joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.identifier)
- joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField)
- } else if (Object(association.through) === association.through) {
- joinedTables[association.through.tableName] = true;
- joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.through.tableName)
- joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField)
- joins += ' = ' + self.quoteIdentifiers(association.through.tableName + '.' + association.identifier)
- joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName)
- joins += ' ON ' + self.quoteIdentifiers(association.through.tableName + '.' + association.foreignIdentifier)
- joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.target.autoIncrementField)
- } else {
- joins += ' LEFT JOIN ' + self.quoteIdentifiers(association.target.tableName)
- joins += ' ON ' + self.quoteIdentifiers(association.source.tableName + '.' + association.source.autoIncrementField)
- joins += ' = ' + self.quoteIdentifiers(association.target.tableName + '.' + association.identifier)
- }
- }
- dao = association.target;
- });
- }
- });
- }
- return joins;
- },
- arrayValue: function(value, key, _key, factory, logicResult){
- var _value = null;
- if (value.length === 0) { value = [null] }
- _value = "(" + value.map(function(v) { return this.escape(v) }.bind(this)).join(',') + ")"
- return [_key, _value].join(" " + logicResult + " ")
- },
- /*
- Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2
- The values are transformed by the relevant datatype.
- */
- hashToWhereConditions: function(hash, dao, options) {
- var result = []
- options = options || {}
- // Closures are nice
- Utils._.each(hash, function (value, key) {
- var _key
- , _value = null
- if (value instanceof Utils.asIs) {
- result.push(value.toString(this))
- return
- }
- if (options.keysEscaped) {
- _key = key
- } else {
- if(this.isAssociationFilter(key, dao, options)){
- _key = key = this.getAssociationFilterColumn(key, dao, options);
- } else {
- _key = this.quoteIdentifiers(key)
- }
- }
- if (Array.isArray(value)) {
- result.push(this.arrayValue(value, key, _key, dao, "IN"))
- } else if ((value) && (typeof value == 'object') && !(value instanceof Date) && !Buffer.isBuffer(value)) {
- if (!!value.join) {
- //using as sentinel for join column => value
- _value = this.quoteIdentifiers(value.join)
- result.push([_key, _value].join("="))
- } else {
- for (var logic in value) {
- var logicResult = Utils.getWhereLogic(logic, hash[key][logic]);
- if (logicResult === "IN" || logicResult === "NOT IN") {
- var values = Array.isArray(value[logic]) ? value[logic] : [value[logic]]
- result.push(this.arrayValue(values, key, _key, dao, logicResult))
- }
- else if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") {
- _value = this.escape(value[logic][0])
- var _value2 = this.escape(value[logic][1])
- result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ')
- } else {
- _value = this.escape(value[logic])
- result.push([_key, _value].join(' ' + logicResult + ' '))
- }
- }
- }
- } else {
- if (typeof value === 'boolean') {
- _value = this.booleanValue(value);
- } else {
- _value = this.escape(value)
- }
- result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
- }
- }.bind(this))
- return result.join(" AND ")
- },
- booleanValue: function(value){
- return value;
- }
- }
- var throwMethodUndefined = function(methodName) {
- throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.')
- }
- return QueryGenerator
- })()
|